Всегда ли критическая секция быстрее? - PullRequest
23 голосов
/ 12 мая 2009

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

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

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

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

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

Ответы [ 6 ]

36 голосов
/ 12 мая 2009

Когда они говорят, что критическая секция «быстрая», они имеют в виду «дешевая, если она не заблокирована другим потоком».

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

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

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

26 голосов
/ 12 мая 2009

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

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

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

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

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

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

4 голосов
/ 12 мая 2009

CriticalSections быстрее, но InterlockedIncrement / InterlockedDecrement больше. Посмотрите этот пример использования реализации LightweightLock полная копия .

3 голосов
/ 12 мая 2009

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

РЕДАКТИРОВАТЬ: Пошел и нашел некоторые комментарии в моем коде: по-видимому, диспетчер кучи MS использует счетчик вращения 4000 (целые приращения, а не мс)

1 голос
/ 06 января 2015

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

1 голос
/ 14 мая 2009

Вот способ взглянуть на это:

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

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

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

...