огда нить в многопроцессорной системе ожидает освобождения блокировки, существуют две альтернативы: переключиться на другую нить или немного подождать.
Переключаясь на другую нить мы неизбежно теряем тысячи циклов процессора на переключение контекста, а это, в основном, сохранение регистров процессора выполнявшейся нити в память, обход списка нитей, выбор новой нити и загрузка регистров для нее. Тут еще возникают накладные расходы связанные с загрузкой новых данных из памяти в кэши процессора — новая нить, скорее всего, будет пользоваться другими данными в других страницах и другими участками кода.
На однопроцессорной системе нам ничего больше не остается — никто, кроме нашего процессора не освободит блокировку. Однако, в многопроцессорной системе это не так.
Если наша программа старается сократить время захвата блокировок, то велика вероятность, что освобождение произойдет раньше, чем мы успеем переключиться на другую нить, выполнить ее, а затем переключиться обратно. Как минимум, время одного переключение контекста было потрачено впустую.
В таком случае самым разумным было бы просто подождать в цикле. Блокировки, с которыми так обращаются, называются spin locks.
Spin locks — не панацея, и вот почему:
- На однопроцессорных имитаторах многопроцессорных систем (Pentium IV, а также процессы, исполняющиеся на одном ядре Niagara) своим циклом мы можем отнимать вычислительные ресурсы другого виртуального процессора и замедлять освобождение блокировки. К счастью, разработчики процессоров уже знают об этой проблеме, а разработчики блокировок активно этим пользуются. См, например, команду pause для Pentium IV.
- Если среднее время освобождение блокировки больше, чем время переключения контекста, то лучше сразу уступить процессор другому
- Нить занимает процессор, снижая свой эффективный приоритет. Особенности реализации планировщика Solaris иногда могут приводить к тому, что эта нить будет остановлена на произвольное время из-за высокоприоритетных нитей, берущие небольшие кванты времени (см. баг 6518490).
- Когда мы крутимся, ожидая блокировку A, захваченную другим процессором, а он, в свою очередь, крутится, ожидая блокировку B, то мы, с большой вероятностью отнимаем время у процесса, который захватил B. Если количество таких транзитивных блокировок равно количеству процессоров, то случится тупик (deadlock).
Исходя из вышесказанного, мы понимаем, что программа, применяющая spin locking должна обладать рядом эвристик, определяющих его целесообразность.
HotSpot тоже использует spin locks. В коде есть множество настраиваемых параметров. Перечислим эвристики, которые применяет Hotspot по умолчанию:
- Количество нитей, крутящихся в ожидании одной блокировки ограничено количеством процессоров (-XX:SyncKnobs=MaxSpinners=n)
- Если ожидаемая блокировка, захвачена нитью, которая сейчас не выполняется на процессоре, то крутиться не нужно (OState=0|1)
- Если блокировка поменяла хозяина, пока нить крутилась, то дальше крутиться не стоит. То же, если мы заметили освободившуюся блокировку, но ее захватили перед нашим CAS.
- Типичное время кручения до удачного захвата блокировки обычно небольшое. Т.е., на практике вероятность захвата блокировки в процессе кручения снижается. Поэтому HotSpot экспоненциально увеличивает паузу между попытками захвата блокировки. Кстати, тот же принцип используется в сети Ethernet после возникновения коллизии. Он называется «exponential backoff».
- Для каждого объекта Hotspot устанавливает количество попыток кручения. Это значение изменяется в зависимости от результата кручения. Если мы смогли захватить блокировку в spin lock, то в следующий раз нам дадут больше попыток (параметры Bonus, BonusB). Если не сумели — количество попыток будет уменьшено (Penalty). Если кто-то перехватил блокировку, которая шла нам в руки, то будет назначен штраф (OXPenalty) или HotSpot перестанет крутиться. Детали этого алгоритма вы сможете посмотреть в исходниках.
На сегодня всё. Спасибо за внимание!
Кирилл Широков
опубликовал vmrobot ( янв 20 2008, 06:29:09 AM MSK ) Permalink Комментарии [3]

извините за offtopic, кирилл в каком часовом поясе вы обитаете?
опубликовал anonymous Январь 23, 2008 at 07:58 PM MSK #
У меня такой вопрос. Каким образом нужно производить анализ приложения, работающего в HotSpot, чтобы прийти к выводу о необходимости переключения подобной настойки.
Профилирование и оценка времени ожидания блокировок?
опубликовал Blazkowicz Февраль 22, 2008 at 02:05 PM MSK #
посмотрев на команду PAUSE, как рекомендовано в статье, вычитал что Интел рекомендует синх.переменную иметь размером 128 байт для пент4 :
Thus, the optimal organization for a Pentium 4 processor is to have the synchronization variable alone on a 128-byte line.
а как этого добиться в ява-коде? так что бы выравнивание сработало?
спасибо,
опубликовал Alex Февраль 14, 2009 at 01:13 AM MSK #