Каковы преимущества использования специально разработанного спинлока (например, http://anki3d.org/spinlock) против кода, как это:
std::mutex m;
while (!m.try_lock()) {}
# do work
m.unlock();
На типичном оборудовании есть огромные преимущества:
Ваш наивный «фальшивый спин-блокировка» может насыщать внутренние шины ЦП, в то время как ЦП вращается, что приводит к истощению других физических ядер, включая физическое ядро, которое удерживает блокировку.
Если процессор поддерживает гиперпоточность или что-то подобное, ваш наивный «фальшивый спин-блокировка» может потреблять чрезмерные ресурсы выполнения на физическом ядре, из-за чего у другого потока не будет общего физического ядра.
Ваш наивный «фальшивый спинлок», вероятно, выполняет посторонние операции записи, которые приводят к плохому поведению в кэше. Когда вы выполняете операцию чтения-изменения-записи на процессоре x86 / x86_64 (как, вероятно, выполняет сравнение / обмен, что делает try_lock), он всегда записывает, даже если значение не изменилось. Эта запись приводит к тому, что строка кэша становится недействительной на других ядрах, что требует их повторного использования, когда другое ядро обращается к этой строке. Это ужасно, если потоки на других ядрах одновременно борются за одну и ту же блокировку.
Ваш наивный «фальшивый спинлок» плохо взаимодействует с предсказанием ветвлений. Когда вы, наконец, получаете блокировку, вы берете мать всех непредсказуемых ветвей прямо в тот момент, когда вы блокируете другие потоки и должны выполнять как можно быстрее. Это похоже на то, что бегун накачан и готов бежать на стартовой линии, но затем, когда он слышит стартовый пистолет, он останавливается, чтобы отдышаться.
По сути, этот код делает все неправильно, так что возможна неправильная спин-блокировка. Абсолютно ничего не сделано эффективно. Написание хороших примитивов синхронизации требует глубоких аппаратных знаний.
Основное преимущество использования спин-блокировки заключается в том, что его очень дешево приобрести и выпустить, если все важные условия выполняются: На замке мало или нет заторов.
Если вы с достаточной уверенностью знаете, что споров не будет, спин-блокировка значительно превзойдет наивную реализацию мьютекса, которая будет проходить через код библиотеки, выполняя проверки, которые вам не обязательно нужны, и выполнять системный вызов. Это означает, что нужно сделать переключение контекста (занимая несколько сотен циклов) и отказаться от временного интервала потока и вызвать перепланирование потока. Это может занять неопределенное время — даже если блокировка будет доступна почти сразу после этого, вам все равно придется подождать несколько десятков миллисекунд, прежде чем ваш поток снова начнет работать в неблагоприятных условиях.
Однако, если предварительное условие отсутствия конкуренции не выполняется, спин-блокировка, как правило, будет значительно уступать, поскольку она не прогрессирует, но все равно потребляет ресурсы ЦП, как если бы она выполняла работу. При блокировке мьютекса ваш поток не потребляет ресурсы ЦП, поэтому их можно использовать для работы другого потока, иначе ЦП может замедлиться, что экономит электроэнергию. Это невозможно для спин-блокировки, которая выполняет «активную работу», пока не преуспеет (или не выйдет из строя).
В худшем случае, если число официантов больше, чем количество ядер ЦП, могут возникнуть спин-блокировки огромный, диспропорциональное влияние на производительность, поскольку активные и работающие потоки ожидают состояния, которое никогда не может возникнуть во время работы (поскольку для снятия блокировки требуется другой поток для запуска!).
С другой стороны, следует ожидать, что каждая современная реализация «без сосания» std::mutex
чтобы уже включить крошечный спинлок, прежде чем вернуться к выполнению системного вызова. Но … хотя это разумное предположение, это не гарантируется.
Еще одна нетехническая причина использования спин-блокировки в пользу std::mutex
могут быть условия лицензии. Условия лицензии являются плохим обоснованием для решения о разработке, но, тем не менее, они могут быть очень реальными.
Например, настоящая реализация GCC основана исключительно на pthreads, что подразумевает, что «что-нибудь MinGW», использующее что-либо из стандартной библиотеки потоков, обязательно связывается с winpthreads (без альтернатив). Это означает, что на вас распространяется лицензия winpthreads, что означает, что вы должны воспроизвести их сообщение об авторских правах. Для некоторых людей это является нарушителем.