Подтвердить что ты не робот

Spinlocks, насколько они полезны?

Как часто вы на самом деле используете спин-блоки в своем коде? Насколько распространено это сталкиваться с ситуацией, когда использование цикла занятости фактически превосходит использование блокировок?
Лично, когда я пишу какой-то код, который требует безопасности потоков, я склонен сравнивать его с разными примитивами синхронизации, и, насколько это возможно, похоже, что использование блокировок дает лучшую производительность, чем использование spindlock. Независимо от того, как мало времени я держу в замке, количество споров, которые я получаю при использовании шпиндельных заслонок, намного больше, чем количество, которое я получаю от использования блокировок (конечно, я запускаю свои тесты на многопроцессорной машине).

Я понимаю, что он с большей вероятностью встретит спин-блокировку в коде "низкого уровня", но мне интересно узнать, полезно ли вам использовать его даже при более высоком уровне программирования?

4b9b3361

Ответ 1

Это зависит от того, что вы делаете. В общем случае код приложения, вы хотите избежать spindlock.

В низкоуровневых материалах, где вы удерживаете блокировку только для нескольких инструкций, а латентность важна, спинлок-мат лучше, чем блокировка. Но эти случаи редки, особенно в тех приложениях, где обычно используется С#.

Ответ 2

В С# "Spin locks" были, по моему опыту, почти всегда хуже, чем блокировка - это редкое явление, когда блокировки спина превосходят блокировку.

Однако это не всегда так..NET 4 добавляет структуру System.Threading.SpinLock. Это обеспечивает преимущества в ситуациях, когда блокировка удерживается в течение очень короткого времени и многократно захватывается. Из документов MSDN в Структуры данных для параллельного программирования:

В сценариях, где ожидается, что блокировка ожидается короткой, SpinLock обеспечивает лучшую производительность, чем другие блокировки.

Блокировки спинов могут превосходить другие блокирующие механизмы в тех случаях, когда вы делаете что-то вроде блокировки через дерево - если у вас есть только блокировки на каждом node в течение очень короткого периода времени, они могут выполнить традиционный замок. Я столкнулся с этим в движке рендеринга с многопоточным обновлением сцены, в какой-то момент - блокировки спинов профилированы, чтобы превзойти блокировку с помощью Monitor.Enter.

Ответ 3

Для моей работы в режиме реального времени, особенно с драйверами устройств, я использовал их честно. Оказывается, что (когда последний раз я это приурочил), ожидая, что объект синхронизации, такой как семафор, привязанный к аппаратным прерываниям, пожевает не менее 20 микросекунд, независимо от того, сколько времени на самом деле требуется для прерывания. Единая проверка зарегистрированного аппаратного регистра памяти с последующей проверкой на RDTSC (для обеспечения тайм-аута, чтобы вы не запирали машину) находилась в диапазоне высоких nannosecond (в основном, в шуме). Для аппаратного подтверждения соединения, которое не должно занимать много времени, действительно сложно преодолеть спин-блокировку.

Ответ 4

My 2c: Если ваши обновления удовлетворяют некоторым критериям доступа, они являются хорошими кандидатами-спин-блокировками:

  • быстро, т.е. у вас будет время для получения спин-блокировки, выполнения обновлений и освобождения спин-блокировки в одном кванте потока, чтобы вы не получили предварительный удержание при удерживании спин-блокировки li >
  • локализованный все данные, которые вы обновляете, предпочтительно находятся в одной только одной загруженной странице, вы не хотите пропустить TLB, пока вы держите спин-блокировку, и вы определенно не хотите, чтобы сводка ошибок страницы читать!
  • атомный вам не нужен никакой другой блокировки для выполнения операции, т.е. никогда не ждите блокировок под спин-блокировкой.

Для чего-либо, что имеет потенциал для выхода, вы должны использовать известную структуру блокировки (события, мьютексы, семафоры и т.д.).

Ответ 5

Один вариант использования спиновых замков - если вы ожидаете очень низкого соперничества, но их будет много. Если вам не нужна поддержка рекурсивной блокировки, спин-блокировка может быть реализована в одном байте, и если конфликт очень низок, отходы цикла процессора незначительны.

Для практического использования я часто имею массивы тысяч элементов, где обновления для разных элементов массива могут безопасно выполняться параллельно. Шансы двух потоков, пытающихся обновить один и тот же элемент одновременно, очень малы (низкое соперничество), но мне нужен один замок для каждого элемента (у меня их будет много). В этих случаях я обычно выделяю массив ubytes того же размера, что и массив, который я обновляю параллельно, и реализую spinlocks inline как (на языке программирования D):

while(!atomicCasUbyte(spinLocks[i], 0, 1)) {}
    myArray[i] = newVal;
atomicSetUbyte(spinLocks[i], 0);

С другой стороны, если бы мне пришлось использовать обычные блокировки, мне пришлось бы выделять массив указателей на объекты, а затем выделять объект Mutex для каждого элемента этого массива. В сценариях, подобных описанным выше, это просто расточительно.

Ответ 6

Если у вас есть критический код производительности, и вы определили, что он должен быть быстрее, чем в настоящее время, и вы определили, что решающим фактором является скорость блокировки, тогда было бы неплохо попробовать спин-блокировку. В других случаях, зачем беспокоиться? Нормальные замки легче использовать правильно.

Ответ 7

Обратите внимание на следующие пункты:

  • Большинство реализаций мьютекса немного сжимаются, прежде чем поток фактически незапланирован. Из-за этого трудно сравнить тезисы мьютексов с чистыми прямыми замками.

  • Несколько потоков, "максимально быстрых" в одной и той же спин-блокировке, будут поглощать всю полосу пропускания и резко снизить эффективность вашей программы. Вам нужно добавить крошечное "спящее" время, добавив noop в ваш циферблат.

Ответ 8

Вам вряд ли когда-либо понадобится использовать спинлоки в коде приложения, если что-то вам следует избегать.

Я не могу по какой-либо причине использовать спин-блокировку в коде С#, работающем на нормальной ОС. Занятые блокировки в основном представляют собой отходы на уровне приложений - вращение может привести к использованию всего тайм-листа процессора, а блокировка немедленно вызовет контекстный переключатель, если это необходимо.

Высокопроизводительный код, в котором у вас есть nr нитей = nr процессоров/ядер, может пригодиться в некоторых случаях, но если вам нужна оптимизация производительности на этом уровне, вы, вероятно, сделаете следующую игру третьего поколения, работая над встроенной ОС с плохими примитивами синхронизации, создавая ОС/драйвер или в любом случае не используя С#.

Ответ 9

Я использовал блокировку спина для фазы стоп-фазы сборщика мусора в проекте HLVM, потому что они легкие и это игрушечная виртуальная машина. Однако спин-блокировки могут быть контрпродуктивными в этом контексте:

Одна из главных ошибок в сборщике мусора сборщика Glasgow Haskell Compiler настолько раздражает, что у него есть имя, последнее замедление ядра", Это является прямым следствием их ненадлежащего использования спин-локов в их GC и ускоряется в Linux из-за его планировщика, но на самом деле эффект можно наблюдать, когда другие программы конкурируют за процессорное время.

Эффект ясен на втором графике здесь и можно увидеть, воздействуя не только на последнее ядро ​​здесь, где программа Haskell видит ухудшение производительности за пределами только 5 ядер.

Ответ 10

Всегда помните эти моменты при использовании спинлокса:

  • Быстрое выполнение пользовательского режима.
  • Синхронизирует потоки внутри одного процесса или нескольких процессов, если они находятся в общей памяти.
  • Не возвращается, пока объект не принадлежит.
  • Не поддерживает рекурсию.
  • Расходует 100% ресурсов ЦП при "ожидании".

Я лично видел так много тупиков только потому, что кто-то думал, что будет хорошей идеей использовать спинлок.

Будьте очень, очень осторожны при использовании спинлок

(Я не могу подчеркнуть это достаточно).