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

Критическая секция всегда быстрее?

Я отлаживал многопоточное приложение и нашел внутреннюю структуру CRITICAL_SECTION. Я нашел элемент данных LockSemaphore из CRITICAL_SECTION интересным.

Похоже, что LockSemaphore - это событие auto-reset (не семафор, как следует из названия), и операционная система создает это событие молча, когда первый раз поток ожидает на Critcal Section, который заблокирован каким-то другим потоком,

Теперь мне интересно, критическая секция всегда быстрее? Событие - это объект ядра, и каждый объект Critical section связан с объектом события, а затем Critical Section может быть быстрее по сравнению с другими объектами ядра, такими как Mutex? Кроме того, как внутренний объект события влияет на производительность раздела Critical?

Вот структура CRITICAL_SECTION:

struct RTL_CRITICAL_SECTION
{
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;
};
4b9b3361

Ответ 1

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

[Обратите внимание, что если он уже заблокирован другим потоком, то это не имеет значения почти так быстро, как быстро.]

Причина, по которой это происходит быстро, заключается в том, что перед входом в ядро ​​он использует эквивалент InterlockedIncrement в одном из полей LONG (возможно, в поле LockCount), и если он преуспеет, то он считает замок заработал, не войдя в ядро.

API-интерфейс InterlockedIncrement, я думаю, реализован в пользовательском режиме как код LOCK INC... другими словами, вы можете получить неоспоримый критический раздел без какого-либо преобразования кольца в ядро.

Ответ 2

В работе с эффективностью несколько вещей попадают в категорию "всегда":) Если вы реализуете что-то самостоятельно, подобное критической секции ОС с использованием других примитивов, то вероятность того, что в большинстве случаев будет медленнее.

Лучший способ ответить на ваш вопрос - это измерение производительности. Как объекты ОС выполняются, очень зависит от сценария. Например, критические разделы обычно считаются "быстрыми", если утверждение низкое. Они также считаются быстрыми, если время блокировки меньше времени вращения спина.

Самое главное, чтобы определить, является ли конфликт в критическом разделе фактором ограничения первого порядка в вашем приложении. Если нет, то просто используйте критический раздел normaly и работайте над вашими приложениями в качестве основного узкого места (или шеи).

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

  • Осторожно установите счетчик блокировки спина для ваших "горячих" критических разделов. Если производительность имеет первостепенное значение, то работа здесь стоит того. Помните, что при блокировке спина пользовательский режим переходит на переход ядра, он потребляет процессорное время с яростной скоростью - при вращении ничего больше не может использовать это время процессора. Если блокировка удерживается достаточно долго, то прямая нить будет фактической блокировкой, освобождая этот процессор для выполнения других работ.
  • Если у вас есть шаблон читателя/автора, рассмотрите возможность использования блокировки Slim Reader/Writer (SRW). Недостатком здесь является то, что они доступны только для продуктов Vista и Windows Server 2008 и более поздних версий.
  • Вы можете использовать переменные состояния в своем критическом разделе, чтобы минимизировать опрос и утверждение, пробуждая потоки только тогда, когда это необходимо. Опять же, они поддерживаются продуктами Vista и Windows Server 2008 и более поздними версиями.
  • Рассмотрите возможность использования Блокированные одиночные списки (SLIST) - они эффективны и не блокируются. Еще лучше, они поддерживаются в продуктах XP и Windows Server 2003 и более поздних версиях.
  • Изучите свой код - вы можете разбить "горячую" блокировку путем рефакторинга некоторого кода и использования блокированной операции или SLIST для синхронизации и связи.

В заключение - сценарии настройки, в которых есть конфликт, могут быть сложными (но интересными!). Сосредоточьтесь на измерении производительности ваших приложений и понимании того, где ваши горячие пути. Инструменты xperf в Инструмент производительности Windows - ваш друг здесь:) Мы только что выпустили версию 4.5 в Microsoft Windows SDK для Windows 7 и .NET Framework 3.5 SP1 (ИСО здесь, веб-установщик здесь), Вы можете найти форум для инструментов xperf здесь. V4.5 полностью поддерживает Win7, Vista, Windows Server 2008 - все версии.

Ответ 4

Критические разделы будут вращаться короткое время (несколько мс) и продолжать проверять, свободен ли замок. После того, как счетчик спинов истечет, он снова вернется к событию ядра. Поэтому в случае, когда держатель блокировки быстро выходит из системы, вам никогда не придется делать дорогостоящий переход к коду ядра.

EDIT: Пошел и нашел комментарии в моем коде: видимо, MS Heap Manager использует число оборотов 4000 (целые приращения, а не мс)

Ответ 5

Здесь можно посмотреть:

Если нет конкуренции, то блокировка спина очень быстро по сравнению с переходом в режим ядра для Mutex.

Когда есть конкуренция, CriticalSection немного дороже, чем использование Mutex напрямую (из-за дополнительной работы по обнаружению состояния spinlock).

Таким образом, это сводится к средневзвешенному значению, где веса зависят от особенностей вашего шаблона вызова. При этом, если у вас мало споров, то CriticalSection - это большая победа. Если, с другой стороны, у вас постоянно есть много споров, тогда вы будете платить очень небольшое наказание за использование Mutex напрямую. Но в этом случае то, что вы получили, переключившись на Mutex, невелико, так что вам, вероятно, будет лучше попытаться уменьшить конфликт.

Ответ 6

Критический раздел быстрее, чем mutex, потому что критический раздел не является объектом ядра. Это часть глобальной памяти текущего процесса. Mutex фактически находится в ядре, и для создания объекта mutext требуется переключатель ядра, но в случае критического раздела нет. Несмотря на то, что критический раздел выполняется быстро, при использовании критических секций будет существовать переключатель ядра, когда потоки ждут состояния. Это связано с тем, что планирование потоков происходит со стороны ядра.