InitializeCriticalSectionAndSpinCount оптимальный SpinCount (пользовательский режим)

Я не совсем понимаю документацию для InitializeCriticalSectionAndSpinCount: http://msdn.microsoft.com/en-us/library/windows/desktop/ms683476(v=vs.85).aspx

В нем говорится: «Вы можете значительно улучшить производительность, выбрав небольшое количество вращений ...»

Однако, поскольку ожидание счетчика выполняется быстрее, чем ожидание объекта, не имеет ли смысла иметь максимально возможное значение SpinCount? Что мне не хватает? Спасибо.

(Я использую его внутри C DLL, используемого многопоточным приложением)

Вот код критической секции, постоянно вызываемой большим количеством потоков:

int g_slots[256] = {0};
...
slot = 256;
EnterCriticalSection(&g_LockHandle);
while (slot-- > 0)
{
    if (g_slots[slot] == 0)
    {
        g_slots[slot] = spid;
        break;
    }
}
LeaveCriticalSection(&g_LockHandle);

Последующие комментарии:

Для всех, кто заинтересован, вот мои неофициальные результаты тестирования на 4-ядерном сервере под управлением Windows 2008 R2: при выполнении сверхбыстрой операции, такой как проверка и увеличение одной переменной, Interlocked выигрывает безоговорочно. На втором месте находится CriticalSection+SpinCount с низким количеством вращений (например, 16), за которым следует старая добрая CriticalSection. Однако при сканировании массива (например, целых чисел) Interlocked занимает третье место после CriticalSection (со счетчиком SpinCount или без него). CriticalSection+high SpinCount был самым медленным во всех случаях.

Нил Вейхер www.netlib.com


person Neil Weicher    schedule 10.08.2014    source источник
comment
Что касается кода в вашем обновлении, вы, вероятно, можете сделать это наиболее эффективно с помощью CAS. Но все зависит от того, чего именно пытается достичь код. Мы можем только догадываться об этом из фрагмента кода, приведенного здесь.   -  person David Heffernan    schedule 10.08.2014
comment
Спинвейты должны быть настроены, вам нужен анализатор параллелизма, чтобы получить наилучшее значение. Вы не можете не слепо предполагать, что вращение в течение очень долгого времени всегда лучше, даже если выглядит так, будто CS удерживается очень короткое время. Время от времени поток, владеющий CS, теряет процессор. Он не вернется в течение многих миллисекунд, когда машина загружена. Если вы будете ждать слишком долго, вы уменьшите эффективность, потому что ваш вращающийся поток не дает уступленному потоку возможности восстановить процессор.   -  person Hans Passant    schedule 10.08.2014


Ответы (2)


То, что на самом деле говорит документация, с моим акцентом на текст, который вы удалили, это:

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

Таким образом, выбор количества вращений очень сильно зависит от продолжительности критического участка.

Ты спрашиваешь:

Однако, поскольку ожидание счетчика выполняется быстрее, чем ожидание объекта, не имеет ли смысла иметь максимально возможное значение SpinCount?

Это просто неправда, что вращение быстрее, чем блокировка. Для длительного критического участка лучше вообще избегать вращения. Если существует вероятность того, что блокировка не будет снята в течение значительного периода времени, наилучшей политикой является немедленная блокировка и ожидание получения блокировки. Даже для короткой секции возможно, что поток, который удерживает блокировку, не запланирован для запуска, и в этом случае вращение явно приводит к расточительному использованию ресурсов ЦП.

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

person David Heffernan    schedule 10.08.2014
comment
Да, это критический участок очень короткой продолжительности, который постоянно выполняется несколькими потоками. Как количество вращений так или иначе влияет на производительность? Предполагая, что критический участок очень короткий, в чем недостаток большого числа вращений? - person Neil Weicher; 10.08.2014
comment
Я думаю, что это описано в последних двух абзацах. Если вы вращаетесь в течение длительного времени, вы расточительно потребляете ресурсы процессора. Вместо этого процессор может делать что-то полезное. Тот факт, что раздел короткий, не означает, что он всегда быстро заканчивается. Поток с блокировкой может не выполняться, когда другой поток запрашивает блокировку. - person David Heffernan; 10.08.2014
comment
Я добавил код для критической секции выше. Будет ли это требовать высокого или низкого количества вращений? Вы говорите, что пока какой-то поток крутится, другие потоки не работают? - person Neil Weicher; 10.08.2014
comment
@Neilw Вы говорите, что пока какой-то поток вращается, другие потоки не работают? Нет, я вовсе этого не говорю. Я говорю, что поток может удерживать блокировку и не работать. В этом случае крутить замок бессмысленно. Это просто задерживает поток, для которого запланирована повторная блокировка. Все время, пока ваш поток крутится, процессор тратится впустую. Я подозреваю, что для кода в вашем вопросе лучше всего подойдет подход без блокировки. - person David Heffernan; 10.08.2014
comment
Почему без блокировки? При достаточном количестве потоков велика вероятность того, что два потока получат один и тот же слот. - person Neil Weicher; 10.08.2014
comment
Я имею в виду синхронизацию без блокировки. КАС например. en.wikipedia.org/wiki/Сравнить-и-обменять - person David Heffernan; 10.08.2014
comment
Я думаю, что вы, похоже, не понимаете, что каждый раз, когда вы вращаетесь, вы останавливаете использование ЦП для продвижения вперед. Вращение может быть полезно, чтобы избежать затрат на переключение контекста, но вы никогда не должны вращаться столько времени, сколько требуется для переключения контекста. - person David Heffernan; 10.08.2014
comment
Вы имеете в виду InterlockedCompareExchange? Как вы думаете, это будет более быстрый механизм в этом случае? Возможно, я попытаюсь сделать сравнение производительности. Кроме того, есть ли у вас рекомендации по количеству вращений в приведенном выше коде? - person Neil Weicher; 10.08.2014
comment
@Neilw Трудно дать рекомендацию по количеству вращений. Вам нужно будет тщательно сравнить. Может зависеть от всяких факторов. Тип оборудования, количество процессоров, количество потоков, частота получения блокировки, что еще работает на машине. Нереально ожидать хороших советов по оптимизации производительности от людей, которые даже не знают, что делает ваша программа. Какое профилирование вы уже сделали? - person David Heffernan; 10.08.2014
comment
Я собираюсь сравнить три методологии (InterlockedCompareExchange, InitializeCriticalSectionAndSpinCount и InitializeCriticalSection) и опубликую некоторые цифры. - person Neil Weicher; 11.08.2014
comment
Хорошо, вот что я обнаружил на 4-ядерной машине с Win2008R2: при выполнении сверхбыстрых операций, таких как проверка и увеличение одной переменной, Interlocked выигрывает безоговорочно. На втором месте находится CriticalSection+SpinCount с низким количеством вращений (например, 16), за которым следует старая добрая CriticalSection. Однако при сканировании массива (например, целых чисел) Interlocked занимает третье место после CriticalSection (со счетчиком SpinCount или без него). Дайте мне знать, если вы хотите увидеть цифры. CriticalSection+high SpinCount был самым медленным во всех случаях. - person Neil Weicher; 11.08.2014
comment
Вам нужна еще помощь здесь? - person David Heffernan; 11.08.2014
comment
Нет, я просто представил свои выводы. Спасибо. - person Neil Weicher; 11.08.2014

Я согласен с утверждением «Вы можете значительно улучшить производительность, выбрав небольшое количество вращений».

Когда я тестировал свой класс пула объектов, который использует InitializeCriticalSectionAndSpinCount на 8-ядерном ПК, лучшим значением было меньше 20. Чем больше число циклов вращения, тем медленнее он работает.

Это мой вывод по этому результату теста:

  • Чем меньше количество спинов, тем меньше бессмысленной обработки потребляется.
  • Очень маленькое количество вращений хорошо работает для подпрограмм, где гарантируется O(1) (например, без цикла).

Я не думаю, что количество спинов должно быть больше тысячи. Счетчик спинов занят ожиданием. Он не только потребляет мощность ЦП, но также потребляет большую часть полосы пропускания между ЦП и ОЗУ, поэтому может привести к истощению трафика между другими ЦП и ОЗУ.

person Hyunjik Bae    schedule 16.08.2014
comment
Спасибо - мои выводы совпадают с вашими: очень низкое количество вращений. Неожиданностью для меня стало то, что при поиске по таблице критические секции работали лучше, чем InterlockedCompareExchange, для чего угодно, кроме очень маленькой таблицы. - person Neil Weicher; 17.08.2014