Контекст переключается намного медленнее в новых ядрах Linux

99

Мы планируем обновить ОС на наших серверах с Ubuntu 10.04 LTS до Ubuntu 12.04 LTS. К сожалению, кажется, что задержка для запуска потока, который стал работоспособным, значительно увеличилась с ядра 2.6 до ядра 3.2. На самом деле в получаемые нами значения задержки трудно поверить.

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

Итак, в двух словах, в результате мы получаем поток для потоковой связи через измерение задержки переменной состояния раз в секунду.

В ядре 2.6.32 эта задержка составляет порядка 2,8–3,5 мкс, что вполне разумно. В ядре 3.2.0 эта задержка увеличилась примерно до 40–100 мкс. Я исключил любые различия в оборудовании между двумя хостами. Они работают на идентичном оборудовании (процессоры X5687 {Westmere-EP} с двумя сокетами, работающие на частоте 3,6 ГГц с отключенными гиперпоточностью, ускорением и всеми состояниями C). Тестовое приложение изменяет сродство потоков, чтобы запускать их на независимых физических ядрах одного и того же сокета (т. Е. Первый поток выполняется на ядре 0, а второй поток - на ядре 1), поэтому отскока потоков на ядра или подпрыгивание / связь между сокетами.

Единственное различие между двумя хостами заключается в том, что на одном работает Ubuntu 10.04 LTS с ядром 2.6.32-28 (поле быстрого переключения контекста), а на другом - последняя версия Ubuntu 12.04 LTS с ядром 3.2.0-23 (медленный контекст распределительная коробка). Все настройки BIOS и оборудование идентичны.

Были ли какие-либо изменения в ядре, которые могли бы объяснить это нелепое замедление в том, сколько времени требуется для запланированного запуска потока?

Обновление: если вы хотите запустить тест на своем хосте и сборке Linux, я разместил код в pastebin для вашего ознакомления. Скомпилировать с помощью:

g++ -O3 -o test_latency test_latency.cpp -lpthread

Запустите (при условии, что у вас есть хотя бы двухъядерный блок):

./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1

Обновление 2 : после долгих поисков параметров ядра, сообщений об изменениях ядра и личных исследований я понял, в чем проблема, и опубликовал решение в качестве ответа на этот вопрос.

Михаил Гольдштейн
источник
1
просто предположение, но, может быть, изменение параметра с /proc/sys/kernel/*может сработать? Если вы найдете что-то, что работает, поместите эту конфигурацию /etc/sysctl.confили файл, /etc/sysctl.d/чтобы она сохранялась при перезагрузках.
Карлос Кампдеррос,
1
Я сравнил / proc / sys / kernel между двумя хостами, но не вижу значимых различий, особенно в любых элементах конфигурации, связанных с планированием.
Михаил Гольдштейн
Я смутно припоминаю слух о том, что RDTSC не обязательно должным образом синхронизируется между ядрами, но я ожидал, что если бы это было проблемой, вы бы увидели обратное время. Вы пробовали поиграть с привязками для запуска обоих потоков на одном ядре и посмотреть, что произойдет?
Дэвид Гивен
На новых ядрах Intel RDTSC безупречно работает с ядрами, особенно с ядрами одного процессора (т. Е. Одного сокета). Интересно, что если оба потока выполняются на одном ядре, задержки снижаются до 4-10 мкс на более новом ядре и прибл. 3 нас на старом ядре.
Михаил Гольдштейн
Просто общий комментарий - полагаться на синхронизацию TSC в лучшем случае сомнительно, хотя в вашем конкретном случае, поскольку вы используете два ядра на одном физическом чипе, это действительно должно сработать.
twalberg

Ответы:

95

Решение проблемы с производительностью пробуждения из- за плохого потока в последних ядрах связано с переключением на intel_idleдрайвер cpuidle с acpi_idleдрайвера, который использовался в старых ядрах. К сожалению, intel_idleдрайвер игнорирует конфигурацию BIOS пользователя для C-состояний и танцует под свою собственную настройку . Другими словами, даже если вы полностью отключите все состояния C в BIOS вашего ПК (или сервера), этот драйвер все равно будет принудительно включать их во время периодов кратковременного бездействия, что почти всегда происходит, если не выполняется синтетический тест, потребляющий все ядро ​​(например, стресс ) это работает. Вы можете отслеживать переходы состояний C, а также другую полезную информацию, связанную с частотами процессора, с помощью замечательного инструмента Google i7z на большинстве совместимого оборудования.

Чтобы узнать, какой драйвер cpuidle в настоящее время активен в вашей установке, просто поместите current_driverфайл в следующий cpuidleраздел /sys/devices/system/cpu:

cat /sys/devices/system/cpu/cpuidle/current_driver

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

В Ubuntu 12.04 вы можете сделать это, добавив их в GRUB_CMDLINE_LINUX_DEFAULTзапись /etc/default/grubи запустив update-grub. Необходимо добавить следующие параметры загрузки:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

Вот кровавые подробности о том, что делают три варианта загрузки:

Установка intel_idle.max_cstateв ноль либо вернет ваш драйвер cpuidle к acpi_idle(по крайней мере, согласно документации опции), либо полностью отключит его. На моем ящике он полностью отключен (т. Е. Отображение current_driverфайла /sys/devices/system/cpu/cpuidleдает результат none). В этом случае второй вариант загрузки processor.max_cstate=0не нужен. Однако в документации указано, что установка max_cstate на ноль для intel_idleдрайвера должна вернуть ОС к acpi_idleдрайверу. Поэтому на всякий случай поставил второй вариант загрузки.

Эта processor.max_cstateопция устанавливает максимальное состояние C для acpi_idleдрайвера равным нулю, надеясь, что оно также отключится. У меня нет системы, на которой я мог бы это протестировать, потому что intel_idle.max_cstate=0полностью выбивает драйвер cpuidle на всем доступном мне оборудовании. Однако, если ваша установка возвращает вас из intel_idleв acpi_idleтолько с первым вариантом загрузки, сообщите мне, processor.max_cstateсделал ли второй вариант то, что было задокументировано в комментариях, чтобы я мог обновить этот ответ.

Наконец, последний из трех параметров, idle=pollнастоящий боров власти. Он отключит C1 / C1E, что удалит последний оставшийся бит задержки за счет гораздо большего энергопотребления, поэтому используйте его только тогда, когда это действительно необходимо. Для большинства это будет излишним, поскольку задержка C1 * не так уж велика. Используя мое тестовое приложение, работающее на оборудовании, которое я описал в исходном вопросе, задержка увеличилась с 9 до 3 мкс. Это, безусловно, значительное сокращение для приложений с высокой задержкой (например, финансовая торговля, высокоточная телеметрия / отслеживание, сбор данных с высокой частотой и т. Д.), Но может не окупить потерю электроэнергии для подавляющего большинства настольные приложения. Единственный способ узнать наверняка - это профилировать улучшение производительности вашего приложения по сравнению с

Обновить:

После проведения дополнительных испытаний с различными idle=*параметрами, я обнаружил , что установка idleв mwaitслучае , если поддерживаются аппаратными средствами является гораздо лучшей идеей. Кажется, что использование MWAIT/MONITORинструкций позволяет процессору войти в C1E без какой-либо заметной задержки, добавляемой ко времени пробуждения потока. Благодаря этому idle=mwaitвы получите более idle=pollнизкие температуры процессора (по сравнению с ), меньшее потребление энергии и при этом сохраните отличные низкие задержки цикла ожидания при опросе. Поэтому мой обновленный рекомендуемый набор параметров загрузки для низкой задержки пробуждения потока ЦП на основе этих результатов:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=mwait

Использование idle=mwaitвместо idle=pollможет также помочь с запуском Turbo Boost (помогая ЦП оставаться ниже его TDP [Thermal Design Power]) и гиперпоточности (для которых MWAIT является идеальным механизмом, позволяющим не использовать все физическое ядро, в то же время время избегая более высоких состояний C). Однако это еще предстоит доказать в ходе тестирования, и я буду продолжать это делать.

Обновление 2:

Опция mwaitпростоя была удалена из новых ядер 3.x (спасибо пользователю ck_ за обновление). Это оставляет нам два варианта:

idle=halt- Должен работать так же хорошо mwait, но проверьте, что это так с вашим оборудованием. Эта HLTинструкция почти эквивалентна MWAITподсказке с состоянием 0. Проблема заключается в том, что для выхода из состояния HLT требуется прерывание, в то время как запись в память (или прерывание) может использоваться для выхода из состояния MWAIT. В зависимости от того, что ядро ​​Linux использует в своем цикле ожидания, это может сделать MWAIT потенциально более эффективным. Итак, как я сказал test / profile и посмотрите, соответствует ли он вашим потребностям в задержке ...

и

idle=poll - Вариант с максимальной производительностью за счет энергии и тепла.

Михаил Гольдштейн
источник
Извините, но почему вы ожидали, что состояния C будут управляться прошивкой? Состояния приостановки - это состояния времени выполнения, и они изначально управляются ОС. Как вы обнаружили, если вы не хотите приостанавливать выполнение, не используйте его.
Энди Росс
6
Извините, но состояния C, EIST и C1E можно отключить в BIOS. Я ожидаю, что ОС будет соблюдать мои настройки BIOS. Это особенно верно, учитывая ужасающие инструменты и документацию в этом случае.
Майкл Гольдштейн,
4
Отключил через биос, может быть. Я не знаю ничего в соответствующей спецификации, которая требует этого. Извините, но "ожидание" чего-либо от BIOS будет вас постоянно укусить. Лучшее, что может делать прошивка на современном ПК, - это ничего. Извините, что вы были удивлены, но, честно говоря, это ошибка пользователя. Ваш тест измерял время приостановки и возобновления.
Энди Росс
19
Одна из ролей выбора функции BIOS - включать / отключать устройства. В некоторых случаях этот выбор принудительно выбирается ОС (например, USB на материнской плате, eSATA и сетевые карты). В других случаях ожидается, что ОС будет уважать ваши пожелания (например, EIST, состояния C, Hyperthreading, Execute Disable, AES-NI, виртуализация и т. Д.). BIOS предоставляет единую центральную поверхность выбора устройств / функций, нейтральную для ОС. Это позволяет пользователю устанавливать несколько (возможно, сильно отличающихся) ОС на хосте, которые используют одни и те же аппаратные функции. Однако этот ответ субъективен, поэтому придется согласиться, чтобы не согласиться.
Майкл Гольдштейн,
1
idle = mwait больше не поддерживается в последнем ядре 3.x lkml.org/lkml/2013/2/10/21. Есть ли альтернативный совет?
ck_ 08
8

Возможно, медленнее стал фьютекс, строительный блок для переменных состояния. Это прольет свет:

strace -r ./test_latency 0 1 &> test_latency_strace & sleep 8 && killall test_latency

затем

for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done

который покажет микросекунды, затраченные на интересные системные вызовы, отсортированные по времени.

В ядре 2.6.32

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000140 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000129 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000124 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000119 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000106 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000103 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000102 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000125 futex(0x7f98ce4c0b88, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000042 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000038 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000030 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000029 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 0
 0.000028 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000027 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000018 futex(0x7fff82f0ec3c, FUTEX_WAKE_PRIVATE, 1) = 0
nanosleep
 0.000027 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, 0x7fff82f0eb40) = ? ERESTART_RESTARTBLOCK (To be restarted)
 0.000017 nanosleep({1, 0}, {1, 0}) = 0
rt_sig
 0.000045 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000040 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000038 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000033 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigaction(SIGRT_1, {0x37f8c052b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000027 rt_sigaction(SIGRTMIN, {0x37f8c05370, [], SA_RESTORER|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000023 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0

В ядре 3.1.9

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000129 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000126 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000122 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000115 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000114 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000112 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000109 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000139 futex(0x3f8b8f2fb0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000043 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000041 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000036 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
nanosleep
 0.000025 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000022 nanosleep({1, 0}, {0, 3925413}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
 0.000021 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
rt_sig
 0.000045 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000044 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000043 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000040 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000038 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000037 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigaction(SIGRT_1, {0x3f892067b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000024 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigaction(SIGRTMIN, {0x3f89206720, [], SA_RESTORER|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0

Я нашел этот отчет об ошибке 5-летней давности, который содержит тест производительности «пинг-понг», который сравнивает

  1. однопоточный мьютекс libpthread
  2. переменная условия libpthread
  3. простые старые сигналы Unix

Мне пришлось добавить

#include <stdint.h>

чтобы скомпилировать, что я сделал с помощью этой команды

g++ -O3 -o condvar-perf condvar-perf.cpp -lpthread -lrt

В ядре 2.6.32

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29085 us; per iteration:   29 ns / 9.4e-05 context switches.
c.v. ping-pong test   elapsed:  4771993 us; per iteration: 4771 ns / 4.03 context switches.
signal ping-pong test elapsed:  8685423 us; per iteration: 8685 ns / 4.05 context switches.

В ядре 3.1.9

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26811 us; per iteration:   26 ns / 8e-06 context switches.
c.v. ping-pong test   elapsed: 10930794 us; per iteration: 10930 ns / 4.01 context switches.
signal ping-pong test elapsed: 10949670 us; per iteration: 10949 ns / 4.01 context switches.

Я пришел к выводу, что переключение контекста между ядром 2.6.32 и 3.1.9 действительно замедлилось, хотя и не так сильно, как в ядре 3.2. Я понимаю, что это еще не ответ на ваш вопрос, буду копать дальше.

Изменить: я обнаружил, что изменение приоритета процесса в реальном времени (оба потока) улучшает производительность 3.1.9, чтобы соответствовать 2.6.32. Однако установка того же приоритета в 2.6.32 замедляет его ... поймите, я рассмотрю это подробнее.

Вот мои результаты сейчас:

В ядре 2.6.32

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29629 us; per iteration:   29 ns / 0.000418 context switches.
c.v. ping-pong test   elapsed:  6225637 us; per iteration: 6225 ns / 4.1 context switches.
signal ping-pong test elapsed:  5602248 us; per iteration: 5602 ns / 4.09 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29049 us; per iteration:   29 ns / 0.000407 context switches.
c.v. ping-pong test   elapsed: 16131360 us; per iteration: 16131 ns / 4.29 context switches.
signal ping-pong test elapsed: 11817819 us; per iteration: 11817 ns / 4.16 context switches.
$ 

В ядре 3.1.9

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26830 us; per iteration:   26 ns / 5.7e-05 context switches.
c.v. ping-pong test   elapsed: 12812788 us; per iteration: 12812 ns / 4.01 context switches.
signal ping-pong test elapsed: 13126865 us; per iteration: 13126 ns / 4.01 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    27025 us; per iteration:   27 ns / 3.7e-05 context switches.
c.v. ping-pong test   elapsed:  5099885 us; per iteration: 5099 ns / 4 context switches.
signal ping-pong test elapsed:  5508227 us; per iteration: 5508 ns / 4 context switches.
$ 
amdn
источник
Я запускал его на Fedora и CentOS, у меня нет Ubuntu. Я выложу свои результаты.
amdn
Хорошо, я запустил его на обоих хостах (т.е. на разных ядрах), и результаты почти не различаются. Таким образом, этот тест не выявил никаких различий. Время обращения фьютекса отличается четвертым знаком после запятой - незначительное снижение производительности. Подождите, это целые числа в секундах? Я только что видел, как вы опубликовали свои результаты, и они кажутся похожими на мои ...
Майкл Гольдштейн
Хорошо, это исключает реализацию фьютекса - мы вернулись к вашей теории переключения контекста ... не стесняйтесь удалять этот ответ, поскольку он действительно относится к комментариям ... Мне просто нужна возможность форматировать команды.
amdn
Да, время указывается в секундах ... вызовы фьютекса, которые длятся дольше секунды, предназначены для потока, ожидающего выполнения условия.
amdn
Итак, что, если вы что-нибудь почерпнете из результатов?
Михаил Гольдштейн
1

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

intel_pstate=disable

Кайл Брандт
источник