Контекст перемикається набагато повільніше в нових 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 ГГц, із гіперточенням, швидкісним кроком та вимкненими всіма станами С). Тестовий додаток змінює спорідненість потоків для запуску їх на незалежних фізичних ядрах одного і того ж сокета (тобто, перший потік запускається на Core 0, а другий потік працює в Core 1), тому немає підстрибування потоків на сердечники або підстрибування / зв'язок між розетками.

Єдина відмінність між двома хостами полягає в тому, що на одному працює Ubuntu 10.04 LTS з ядром 2.6.32-28 (швидкий контекстний комутатор), а в іншому працює остання версія Ubuntu 12.04 LTS з ядром 3.2.0-23 (повільний контекст вимикач). Усі налаштування та апаратне забезпечення BIOS однакові.

Чи були якісь зміни в ядрі, які могли б пояснити це смішне сповільнення, за скільки часу потрібно запланувати запуск потоку?

Оновлення: Якщо ви хочете запустити тест на вашій хості та Linux збірці, я опублікував код на пастібіну для вашої ознайомлення. Зібрати:

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 / ядро ​​між двома хостами, але не бачу значущих відмінностей, особливо в будь-яких елементах конфігурації, пов’язаних із плануванням.
Майкл Голдштейн

Я туманно згадую чутку про те, що 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 [теплової конструкції потужності]) та гіперпереборці (для яких MWAIT є ідеальним механізмом не споживання всього фізичного ядра, одночасно час уникати вищих станів С). Однак це все ще доведено на тестуванні, що я продовжуватиму робити.

Оновлення 2:

mwaitВаріант простоює був видалений з нових ядер 3.x (спасибі користувача ck_ для поновлення). Це залишає два варіанти:

idle=halt- Має працювати так само добре mwait, але протестуйте, щоб переконатися, що це стосується вашого обладнання. HLTІнструкція майже еквівалентна MWAITз державним натяком 0. Проблема полягає в тому , що переривання потрібно , щоб вийти зі стану ГВР, в той час як запис в пам'ять (або переривання) може бути використаний , щоб вийти зі стану MWAIT. Залежно від того, що використовує ядро ​​Linux у своєму циклі очікування, це може зробити MWAIT потенційно більш ефективним. Отже, як я вже сказав тест / профіль, і подивіться, чи відповідає він вашим потребам затримки ...

і

idle=poll - Найвищий показник продуктивності, за рахунок енергії та тепла.


Вибачте, але чому ви очікували, що держави C будуть керуватися прошивкою? Суспендовані стани - це стани виконання, і ними керує ОС за допомогою дизайну. Як ви виявили, якщо ви не хочете призупинити виконання, не використовуйте його.
Енді Росс

6
Вибачте, але стан C, EIST та C1E можна вимкнути в BIOS. Я очікую, що ОС поважатиме мої настройки BIOS. Це особливо вірно, враховуючи жахливий інструментарій та документацію в цьому випадку.
Майкл Голдштейн

4
Можливо, вимкнено через ваш біос Я не знаю нічого у відповідній специфікації, яка цього вимагає. Вибачте, але "очікування" що- небудь від BIOS збирається вас кусати неодноразово. Найкраще, що прошивка може зробити в сучасному ПК - це нічого. Вибачте, що ви були здивовані, але, чесно кажучи, це помилка користувача. Ваш показник вимірював час призупинення та відновлення.
Енді Росс

19
Однією з ролей вибору функцій BIOS є включення / відключення пристроїв. У деяких випадках ці вибори примусові в ОС (наприклад, на материнській платі USB, eSATA та NIC). В інших очікується, що ОС поважає ваші побажання (наприклад, EIST, стани C, Hyperthreading, Execute Disable, AES-NI, Virtualization тощо). BIOS забезпечує єдину центральну поверхню вибору пристрою / функції, яка є нейтральною для ОС. Це дозволяє користувачеві встановити на хості кілька (можливо, значно різних) ОС, які використовують однакові функції обладнання. Однак ця відповідь суб’єктивна, тому доведеться погодитися з не згодою.
Майкл Голдштейн

1
idle = mwait більше не підтримується в останній 3.x ядрі lkml.org/lkml/2013/2/10/21 якісь альтернативні поради?
ck_

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. однонитковий лишай
  2. змінна умова ліфтової нитки
  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.
$ 

Я запускав це на Fedora і CentOS, не маючи Ubuntu. Я опублікую свої результати.
amdn

Гаразд, я розмістив його на обох хостах (тобто, і різних ядрах), і результати майже не розбігаються. Отже, цей тест не виділив жодних відмінностей. Час виклику futex відрізняється четвертим десятковим знаком - незначним зниженням продуктивності. Помилка зачекайте, цілі цифри за секунди? Щойно я бачив, як ви опублікували свої результати, і вони виглядають схожими на мої ...
Майкл Голдштейн

Гаразд, це виключає реалізацію futex - ми повертаємося до вашої теорії переключення контексту .... сміливо видаляйте цю відповідь, оскільки вона дійсно належить до коментарів ... Мені просто хотілося можливість форматування команд.
amdn

Так, час у секундах ... дзвінки до футексу, які тривають довше секунди, є потоком, що очікує умови.
amdn

Отже, що робити, якщо що-небудь ви не отримуєте від результатів?
Майкл Голдштейн

1

Можливо, ви також побачите процесори, що натискають внизу в останніх процесах і ядра Linux через драйвер pstate, який відокремлений від c- state . Отже, для того, щоб відключити це, ви маєте такий параметр ядра:

intel_pstate=disable

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.