чтения не будут отставать от реальных значений. мол, для 100% уверенности не только в разделении доступа, но и консистентности, нужно использовать мьютексы. однако я не могу нигде найти разъяснений, почему так. кеш процов может невовремя обновиться?
это странное мнение
Наверное, потому что это так: чтение памяти через атомик не даёт гарантии актуальности значения :)
https://groups.google.com/g/golang-nuts/c/0uPDCRiBWqc/m/JISqbkl7288J
то есть записать в одну процессорную инструкцию мы можем атомарно, а для чтения придется подождать загрузки в кеш? но ведь мы туда уже положили значение при записи, почему нет гарантий не понимаю 🤷♂️ что значение может быть вытеснено из кеша и придется все-таки идти в память, а там stale значение?
точно одна процессорная инструкция?
суть атомиков же
Количество инструкций зависит от архитектуры.
https://github.com/golang/go/blob/master/src/runtime/internal/atomic/asm_amd64.s#L84
атомик дает всего две гарантии 1. значение обновится целиком 2. компилятор не переставит инструкции так, чтобы те чтения, что в тексте программы после записи, оказались перед на amd64 мы еще знаем, что atomic приведет к синхронизации кешей процессоров, и это будет тормозить больше ничего атомик нам не дает.
А где гарантии, что сразу после записи кто-то не записал туда же с другого cpu и вы прочитаете совершенно другое значение? :)
и всё это только в рамках 1 ядра.
но ведь общий кеш гарантирует, что все ядра прочитают одно и то же, актуальное для всех, значение. иначе бы это не работало: https://play.golang.org/p/cOTzCb1rroy
то есть есть архитектуры, где это https://play.golang.org/p/cOTzCb1rroy даст некорректный результат, потому что разные ядра будут видеть разное значение в кеше?
какого такого 1 ядра. для 1 ядра и атомики ненужны, всё и так почти хорошо было до появления HT и двухядерников
Добавьте какую-то логику, завязанную на значение счётчика и сразу поймёте в чём проблема. Например, при ops == 50 вы делаете декремент до 0, потом снова инкремент до верхнего порога.
общего кэша нет для NUMA серверов (н.р. на 2x Xeon-ах). В этом случае синхронизация кэша с атомиками будет через межпроцессорную QPI шину. А также нет когерентности кешей L1,L2,L3 между ядрами на одном процессоре.
двухпроцессорные конфигурации были уже в 90х.
не ну так то да, как и атомарные операции для них ) просто более массово это пошло с появлениям ht, потом двух и x-ядерников. И соответсвенно вся тема с lockfree алгоритмами
Кстати, для 1 ядра тоже важны, правда не в прикладном коде
По идее, ht тут не должен быть при делах, кеши общие же
абсолютно предсказуемые результаты: https://play.golang.org/p/gv3YP7ufQFA в чем загвоздка? что должно пойти не так?
да кэши то общие. Но "массовый софт не ожидал" что будет реально исполнятся параллельно, поэтому было время глобального багофикса гонок в эру первых HT и осознавания параллельности
Вы сделали не то. Я говорил о том, чтобы значение ops "гуляло" в интервале от 0 до 50, а вся эта толпа горутин работали как единый механизм, продвигающий значение ops от 0 до 50 и обратно.
сложность реализации чего-либо, н.р. счетчики, простые флаги, атомарные значения сами по себе - более менее просты, а вот мапу или дерево на атомиках - на это уже есть и научные работы (с обоснованием отсутсвия гонок). Само представление о сложности таких алгоритмов можете посмотреть в статьях по lockfree. На том же хабре есть например.
Обсуждают сегодня