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

SwitchToThread vs Sleep (1)

Мне интересно, какая фактическая разница между вызовом Thread.Sleep(1) и вызовом SwitchToThread (если мы игнорируем, что он в настоящее время не отображается BCL).

Джо Даффи упоминает в свой пост, который:

"API-интерфейс kernel32! SwitchToThread не обнаруживает проблем, с которыми работают Sleep (0) и Sleep (1)." (относительно поведения планировщика)

Почему спать не будет так же, как SwitchToThread? Почему эта дифференциация существует и для чего она хороша? (если вообще..)

4b9b3361

Ответ 1

Есть два отличия. Первое упоминается в документах MSDN для SwitchToThread:

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

Сон (0) позволит запускать потоки других процессоров.

SwitchToThread возвращает только контекст планирования потока. Сон, с другой стороны, имеет несколько условий, для которых он ждет. Документы для SleepEx подробно изложите это:

* An I/O completion callback function is called
* An asynchronous procedure call (APC) is queued to the thread.
* The time-out interval elapses

Это приведет к нескольким потокам.

В общем, Sleep (0) будет с большей вероятностью давать таймлис и будет ВСЕГДА уступать ОС, даже если нет других ожидающих потоков. Поэтому добавление Sleep (0) в цикле будет использовать процессор с 100% (на ядро) до почти 0% во многих случаях. SwitchToThread не будет, если другой поток не ждет среза времени.

Ответ 2

SwitchToThread() - это "более умная" версия Sleep (0). Это плохо документировано, но, по моему мнению, оно работает следующим образом:

  • когда в состоянии ready есть другие потоки (т.е. есть больше потоков, которые нужно запустить, чем доступные логические процессоры), и эти потоки имеют тот же самый или более высокий приоритет, чем поток который вызывает SwitchToThread(), он ведет себя как так же, как Sleep (0) - т.е. уступает логический процессор одному из этих потоков при дорогостоящей стоимости контекстного переключателя;
  • когда в состоянии ready есть потоки с более низким приоритетом, он просто выходит, т.е. поток, который вызвал SwitchToThread(), продолжает выполнение без каких-либо затрат на контекстный переключатель или 3 на звонок 0 (это не выходит из пользовательского режима) - это вопреки тому, как ведет себя Sleep (0), который всегда уступает управлению даже тем нисходящим потокам;
  • когда в состоянии ready нет потоков, SwitchToThread() также просто выходит из режима сна (0) - поэтому, если вы сделаете это в цикле, вы просто получаете 100% нагрузку текущего логического процессора, то есть сжигание мощности.

Сон (1) совпадает с Sleep (0), но с задержкой в ​​1 миллисекунду. Эта задержка в 1 миллисекунду освобождает логический процессор и не сжигает никакой мощности. SwitchToThread, наоборот, никогда не испытывает никакой задержки.

Так что лучше сравнить SwitchToThread с Sleep (0), а не с Sleep (1), потому что Sleep (1) совпадает с Sleep (0) + delay 1 миллисекундой.

Я заимствовал некоторые идеи по этой проблеме из "Справочного руководства по оптимизации архитектуры Intel 64 и IA-32" и "Руководство разработчиков программного обеспечения для архитектуры Intel 64 и IA-32", которое позволяет вызывать некоторые инструкции CPU [t23 > также доступны как встроенные средства) через SwitchToThread() или Sleep (0), если ваше ожидание очень короткое. Обратите внимание, что SwitchToThread() или Sleep (0) являются почти немедленными, а Sleep (1) длится как минимум одна миллисекунда.

Следует также учитывать следующее:

  • Каждый вызов функции Sleep() или SwitchToThread() испытывает дорогостоящую стоимость контекстного переключателя, который может быть 10000+ циклов.
  • Он также страдает от стоимости кольца 3 для переключения 0 переходов, которые могут быть 1000+ циклов.
  • SwitchToThread() или Sleep (0) могут быть бесполезны, если нити не находятся в состоянии ready, но Sleep (1) ожидает хотя бы одну миллисекунду, независимо от того, существуют ли другие потоки в `ready ' или нет.

Если ваш цикл ожидания имеет тенденцию быть очень коротким, рассмотрите сначала выполнение некоторых некоторых инструкций CPU pause. За счет замедления "отжимания" с некоторыми инструкциями CPU pause перед вызовом SwitchToThread() или сна() многопользовательское программное обеспечение получает:

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

Однако, если вы собираетесь вызывать Sleep (1), который работает как минимум на одну миллисекунду, которая очень длинная с точки зрения циклов процессора, чем вы ожидаете, что ваш цикл ожидания будет очень длинным, поэтому инструкции pause будет бесполезным в этом случае.

Когда цикл ожидания, как ожидается, будет длиться долго, желательно перейти к операционной системе, вызвав одну из функций API синхронизации ОС, такую ​​как WaitForSingleObject в ОС Windows, но не SwitchToThread() или Sleep (0) или Сон (1), так как они очень расточительны при длительных ожиданиях. Кроме того, Sleep (1) работает очень медленно, и функции синхронизации ОС, такие как WaitForSingleObject или EnterCriticalSection, будут реагировать намного быстрее, и они более дружественны к ресурсам.

Мой вывод: лучше не использовать Sleep (0) или Sleep (1) или SwitchToThread(). Избегайте циклов "отжимать-ждать" любой ценой. Используйте функции синхронизации высокого уровня, такие как WaitForMultipleObjects(), SetEvent() и т.д. - они лучше всего подходят для условий производительности, эффективности и энергосбережения. Хотя они также страдают от дорогостоящих переключателей контекста и кольца 3, чтобы переместиться на 0 переходов, эти расходы являются нечастыми и более чем разумными, по сравнению с тем, что вы потратили бы в циклах "spin-wait" с помощью Sleep() или SwitchToThread().

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

Последовательный опрос для устройств или файла или другого источника данных для изменений состояния может привести к тому, что компьютер потребляет больше энергии, ставит нагрузку на память и системную шину и обеспечивает ненужные ошибки страницы (используйте диспетчер задач в Windows чтобы увидеть, какие приложения производят большинство ошибок страницы, находясь в режиме ожидания - это самые неэффективные приложения, поскольку они используют "опрос" ). Минимизируйте опрос, когда это возможно, и используйте способ написания приложений, основанный на событиях. Это лучшая практика, которую я очень рекомендую. Ваше приложение должно буквально спать все время, ожидая нескольких событий, установленных заранее. Хорошим примером приложения, управляемого событиями, является Nginx под Linux. Возьмите пример с опросом для изменений источника питания. Если операционная система предоставляет услуги уведомлений (даже сообщение WM_) для различных изменений состояния устройства, таких как переход источника питания от переменного тока к аккумулятору, используйте эти службы уведомлений вместо опроса для изменений состояния устройства. Такой подход уменьшает накладные расходы для кода, чтобы опросить состояние источника питания, поскольку код может получать уведомления асинхронно, когда происходят изменения состояния.

В отличие от некоторых людей, Sleep (0) не снижает потребление ЦП до нуля. Он освобождает исполнение для других потоков, которые находятся в состоянии "ready", но если таких потоков нет, они просто теряют тысячи циклов процессора и потребляют 100% циклов процессора текущих потоков, а также fooobar.com/info/137911/... - и я также просто повторно проверил это снова - цикл Sleep (0) потребляет 100% процессор текущего потока на 64-разрядной версии Windows 10.