@standmit Да, будет. Причём то, в какую сторону она будет, зависит от решаемой задачи.
В среднем по больнице будут рассказывать, что lock-free наше всё, и мьютексы не нужны, но когда придёшь в какой-нибудь гугл, то там скажут «Ты наркоман, что ли, весь процессор тормозить, чтобы циферку заменить?! Ну-ка перепиши по-нормальному»
Все тонкости здесь прячутся от архитектурной реализации CAS-инструкций под конкретный процессор, и поддержке мьютексов под конкретную платформу, там тоже тысяча реализаций на все случаи жизни.
Правильный ответ: сделать бенчмарк, похожий на хотспот своей задачи, и отпрофилировать его на целевой платформе
@rayslava А можно подробнее про торможение процессора? Из того, что я читал, я сделал вывод, что торможение будет в любом случае, просто мьютекс тормозит все ядра сразу, а барьеры памяти позволяют правильным образом разнести торможение ядер во времени, давая больше свободы для оптимизации компилятором. Это не так?
@standmit Ну мьютекс вовсе необязательно тормозит хоть что-нибудь, это зависит от имплементации и кода (например, вот обзор бустового мьютекса есть, он тут вообще не лочится: https://www.appsloveworld.com/cplus/100/33/why-is-stdmutex-faster-than-stdatomic ), а про барьеры — на восстановления когерентности кэшей между ядрами всё равно будет уходить время, причём тут зависит именно от процессора и от реализации этого самого протокола когерентности, а не от того, как расставлены барьеры в коде. Тут скорость зависит не только от кода и компилятора, но и от того, как код разлёгся по ядрам, что попало в кэши, и как оно себя ведёт. При аккуратно расставленных барьерах торможение будет, конечно, меньше, чем если везде лупить sequential consistent требование, но какое-то всё равно будет, и это стоит учитывать
@standmit ну и само собой, всё это относится скорее к очень суровому хайлоаду, когда мы начинаем экономить такты, и когда экономия сотни микросекунд, умноженная на миллиарды серверов очень конкретной аппаратной конфигурации, даёт миллионы долларов экономии.
В бытовом применении вряд ли углубление в такие детали имеет смысл. Скажем, сейчас почти любой бинарный дистрибутив линукса для x86_64 собирается под nehalem пятнадцатилетней давности, и всем норм. Какие уж тут войны за время синхронизации ядер :)
@rayslava Спасибо за развёрнутый ответ! Тема, действительно, сильно сложнее, чем я представлял. Если вернуться к конкретному примеру, который я описал. Один поток только записывает эту небольшую структуру, и некое неопределённое число потоков нерегулярно только читают эту структуру. Всё это в динамической библиотеке, которая точно будет собираться как под Linux, так и под Windows и будет использоваться на архитектурах amd64 и различных arm*. Как при такой степени неопределённости сделать выбор в пользу того или другого варианта?
@standmit ну при таком разбросе вариантов я бы во главу угла поставил поддерживаемость.
Если есть универсальная переносимая библиотека в зависимостях, которая предоставляет подходящие примитивы (скажем, если зависимость от какого-нибудь qt), я бы использовал что-то оттуда, чтобы самому всё это не писать.
А если писать самому всю эту синхронизацию, то я бы сделал на атомиках, потому что они меньше зависят от платформенной реализации и больше от аппаратной, в кроссплатформенной сборке под linux и windows можно наступить на какие-нибудь совершенно неочевидные грабли в разнице работы futex и WaitOnAddress (или кто там в windows-мире сейчас за него), а потом отлаживаться до скончания веков.