Чем отличается занятый цикл с помощью функции Sleep (0) и паузы? - PullRequest
3 голосов
/ 20 сентября 2011

Я хотел бы дождаться события в моем приложении, которое должно произойти немедленно, поэтому я не хочу ставить свою ветку на ожидание и разбудить ее позже.Интересно, в чем разница между использованием Sleep(0) и инструкцией аппаратной паузы.

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

#include <iostream>
using namespace std;
#include <windows.h>

bool t = false;
int main() {
       while(t == false)
       {
              __asm { pause } ;
              //Sleep(0);
       }
}

Ответы [ 2 ]

3 голосов
/ 03 июля 2017

Windows Sleep (0) против инструкции PAUSE

Позвольте мне привести цитату из Справочного руководства по оптимизации архитектур Intel 64 и IA-32.

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

Они обычно называются «спящими циклами» (см. пример #1).Следует отметить, что вызов SwitchToThread также может использоваться.«Спящий цикл» распространен в алгоритмах блокировки и пулах потоков, так как потоки ожидают работы.

Эта конструкция, состоящая в том, чтобы сидеть в узком цикле и вызывать службу Sleep () с параметром 0, фактически является опросом.цикл с побочными эффектами:

  • Каждый вызов Sleep () требует больших затрат на переключение контекста, которое может составлять 10000 + циклов .
  • .Затраты на переходы от кольца 3 к кольцу 0 могут составлять 1000 + циклов .
  • Когда нет другого потока, ожидающего получения контроля, этот цикл ожидания ведет себя к ОСкак высокоактивная задача, требующая ресурсов ЦП, не позволяющая ОС переводить ЦП в состояние пониженного энергопотребления.

Пример # 1.Unoptimized Sleep Loop

while(!acquire_lock())
{ Sleep( 0 ); }
do_work();
release_lock();

Пример # 2.Энергопотребление в режиме Sleep Loop с использованием PAUSE

if (!acquire_lock())
{ /* Spin on pause max_spin_count times before backing off to sleep */
    for(int j = 0; j < max_spin_count; ++j)
    { /* intrinsic for PAUSE instruction*/
        _mm_pause();
        if (read_volatile_lock())
        {
            if (acquire_lock()) goto PROTECTED_CODE;
        }
    }
    /* Pause loop didn't work, sleep now */
    Sleep(0);
    goto ATTEMPT_AGAIN;
}
PROTECTED_CODE:
do_work();
release_lock();

Пример # 2 показывает методику использования инструкции PAUSE для обеспечения энергосбережения в цикле Sleep.

Путем замедления “spin-wait »с инструкцией PAUSE многопоточное программное обеспечение получает:

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

В одном из примеров этоЭта технология позволила увеличить производительность в 4,3 раза, что позволило сэкономить 21% энергии на процессоре и 13% экономии энергии на уровне платформы.

Задержка паузы в микроархитектуре Skylake

Инструкция PAUSE обычно используется с программными потоками, выполняющимися на двух логических процессорах, расположенных в одном процессоре.Ядро, ожидающее снятия блокировки.Такие короткие циклы ожидания, как правило, длятся от десятков до нескольких сотен циклов, поэтому с точки зрения производительности выгоднее ждать, занимая центральный процессор, чем уступая ОС.Когда ожидается, что цикл ожидания будет длиться тысячи и более циклов, предпочтительно уступить операционную систему, вызвав одну из функций API синхронизации ОС, например WaitForSingleObject в ОС Windows.

Инструкция PAUSEпредназначен для:

  • Временно предоставить логическому процессору-брату (готовому к продвижению вперед, выходя из цикла вращения) конкурентоспособные совместно используемые аппаратные ресурсы.Микроархитектурные ресурсы с конкурентным разделением, которые логический процессор-брат может использовать в микроархитектуре Skylake: (1) больше слотов переднего плана в декодировании ICache, LSD и IDQ;(2) Больше слотов исполнения в RS.
  • Экономия энергии, потребляемой ядром процессора, по сравнению с выполнением эквивалентной последовательности команд спин-цикла в следующих конфигурациях: (1) Один логический процессор неактивен (например, ввод C-государство);(2) Оба логических процессора в одном и том же ядре выполняют инструкцию PAUSE;(3) HT отключен (например, с использованием параметров BIOS).

Задержка инструкции PAUSE в микроархитектуре предыдущего поколения составляет около 10 циклов, тогда как в микроархитектуре Skylake она была расширена до 140 циклов..

Увеличенная задержка (позволяющая более эффективно использовать ресурсы микроархитектуры с конкурентным разделением ресурсов для логического процессора, готового к прогрессу вперед) имеет небольшое положительное влияние на производительность (1-2%) для многопоточных приложений.Ожидается, что это окажет незначительное влияние на менее многопоточные приложения, если продвижение вперед не блокируется при выполнении фиксированного числа циклических инструкций PAUSE.

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

Дополнительную информацию по этой проблеме можно найти в разделе "Оптимизация архитектур Intel 64 и IA-32".Справочное руководство »и« Руководство разработчика программного обеспечения для архитектуры Intel 64 и IA-32 »вместе с примерами кода.

My Opinion

Лучше, чтобы логика программы выполнялась таким образом, чтобы ниSleep (0) и инструкция PAUSE не нужны.Другими словами, избегайте циклов «ожидания вращения» вообще.Вместо этого используйте высокоуровневые функции синхронизации, такие как WaitForMultipleObjects(), SetEvent() и т. Д.Такие высокоуровневые функции синхронизации являются лучшим способом написания программ.Если вы анализируете доступные инструменты (в вашем распоряжении) с точки зрения производительности, эффективности и энергосбережения - функции более высокого уровня являются лучшим выбором.Хотя они также страдают от дорогостоящих переключений контекста и переходов от кольца 3 к кольцу 0, эти расходы редки и более чем разумны по сравнению с тем, что вы потратили бы в целом на все объединенные циклы паузы с "ожиданием вращения" или циклыwith with Sleep (0).

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

Последовательный опрос устройств или изменений файлов или состояний в рабочем процессе логики программы может привести к тому, что компьютер будет потреблять больше энергии,сделать упор на память и шину и обеспечить ненужные сбои страниц (используйте диспетчер задач в Windows, чтобы увидеть, какие приложения генерируют большинство сбоев страниц в режиме ожидания, ожидая ввода данных пользователем в фоновом режиме - это наиболее неэффективные приложения, поскольку онииспользуя вышеупомянутый опрос).Минимизируйте опрос (включая спин-циклы) всякий раз, когда это возможно, и используйте идеологию, управляемую событиями, и / или структуру, если таковая имеется - это лучшая практика, которую я настоятельно рекомендую.Ваше приложение должно буквально спать все время, ожидая нескольких событий, настроенных заранее.

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

1 голос
/ 20 сентября 2011

Sleep - системный вызов, который позволяет ОС перепланировать время ЦП для любого другого процесса, если таковой имеется, прежде чем разрешить вызывающей стороне продолжить (даже если параметр равен 0).

__asm {pause}; не является переносимым.

Ну, Sleep - ни то, ни другое, но не на уровне ЦП, а на уровне системных библиотек.

...