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

Почему планировщик Linux помещает два потока на одно физическое ядро ​​на процессорах с HyperThreading?

Я читал в нескольких местах, что планировщик Linux по умолчанию с поддержкой hyperthreading на многоядерных машинах, что означает, что если у вас есть машина с 2 реальными ядрами (4 HT), она не будет планировать два занятых потока на логические ядра таким образом, чтобы они оба работали на одних и тех же физических ядрах (что во многих случаях приводило бы к 2-х издержкам).

Но когда я запускаю stress -c 2 (порождает два потока для запуска на 100% процессоре) на моем Intel i5-2520M, он часто планирует (и сохраняет) два потока на HT ядра 1 и 2, которые сопоставляются с одним и тем же физическим ядром. Даже если система не работает в противном случае.

Это также происходит с реальными программами (я использую stress здесь, потому что это позволяет легко воспроизвести), и когда это происходит, моя программа, по понятным причинам, занимает в два раза больше времени для запуска. Настройка привязки вручную с помощью taskset исправляет, что для моей программы, но я ожидал бы, что планировщик, поддерживающий HT, сделает это правильно сам по себе.

Вы можете найти HT- > физическое ядро ​​с помощью egrep "processor|physical id|core id" /proc/cpuinfo | sed 's/^processor/\nprocessor/g'.

Итак, мой вопрос: Почему планировщик помещает мои потоки в одно и то же физическое ядро ​​здесь?


Примечания:

  • Этот вопрос очень похож на этот другой вопрос, ответы на которые говорят, что Linux имеет довольно сложный планировщик потоков, который известен HT. Как описано выше, я не могу наблюдать этот факт (проверьте себя stress -c) и хотел бы знать, почему.
  • Я знаю, что я могу настроить совместимость процессоров вручную для моих программ, например. с помощью инструмента taskset или с помощью функции sched_setaffinity. Это не то, что я ищу, я бы ожидал, что планировщик сам знает, что отображение двух занятых потоков в физическое ядро ​​и оставление одного физического ядра полностью пустым - не очень хорошая идея.
  • Я знаю, что есть некоторые ситуации, в которых вы предпочли бы, чтобы потоки были запланированы на одно и то же физическое ядро ​​и оставили другое ядро ​​свободным, но кажется бессмысленно, что планировщик будет делать примерно 1/4 из этих случаев. Мне кажется, что ядра HT, которые он выбирает, являются полностью случайными, или, возможно, те HT-ядра, которые имели наименьшую активность во время планирования, но это не было бы очень гиперпотоком, учитывая, насколько ясно программы с характеристиками stress извлекайте выгоду из работы на отдельных физических ядрах.
4b9b3361

Ответ 1

Я думаю, что настало время обобщить некоторые знания из комментариев.

Linux-планировщик знает об HyperThreading - информация о нем должна быть прочитана из таблиц ACPI SRAT/SLIT, которые предоставляются BIOS/UEFI, - чем Linux строит домены планировщика.

Домены имеют иерархию - то есть на серверах с двумя процессорами вы получите три уровня доменов: all-cpus, per-cpu-package и per-cpu-core. Вы можете проверить его с помощью /proc/schedstat:

$ awk '/^domain/ { print $1, $2; } /^cpu/ { print $1; }' /proc/schedstat
cpu0
domain0 0000,00001001     <-- all cpus from core 0
domain1 0000,00555555     <-- all cpus from package 0
domain2 0000,00ffffff     <-- all cpus in the system

Часть планировщика CFS - это балансировка нагрузки - зверь, который должен красть задачи из вашего загруженного ядра в другое ядро. Вот его описание из документации ядра:

При этом он проверяет, исчерпал ли текущий домен его промежуток ребалансировки. Если это так, он запускает load_balance() в этом домене. Затем он проверяет родительский sched_domain (если он существует) и родительский элемент родителя и так д.

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

От: https://www.kernel.org/doc/Documentation/scheduler/sched-domains.txt

Вы можете отслеживать действия балансировки нагрузки путем сравнения чисел в /proc/schedstat. Я написал script для этого: schedstat.py

Счетчик alb_pushed показывает, что балансировка нагрузки была успешно перенесена:

Sun Apr 12 14:15:52 2015              cpu0    cpu1    ...    cpu6    cpu7    cpu8    cpu9    cpu10   ...
.domain1.alb_count                                    ...      1       1                       1  
.domain1.alb_pushed                                   ...      1       1                       1  
.domain2.alb_count                              1     ...                                         
.domain2.alb_pushed                             1     ...

Однако логика балансировки нагрузки сложна, поэтому трудно определить, какие причины могут помешать ей хорошо выполнять свою работу и как они связаны с счетчиками schedstat. Ни я, ни @thatotherguy не могут воспроизвести вашу проблему.

Я вижу две возможности для этого поведения:

  • У вас есть агрессивная политика энергосбережения, которая пытается сохранить одно ядро ​​для снижения энергопотребления процессора.
  • Вы действительно столкнулись с ошибкой с подсистемой планирования, чем вы должны пойти в LKML и тщательно поделиться своими выводами (включая mpstat и schedstat данные)

Ответ 2

Я не могу воспроизвести это на 3.13.0-48 с моим процессором Intel (R) Xeon (R) E5-1650 0 @3.20GHz.

У меня есть 6 ядер с гиперпотоком, где логическое ядро ​​N отображает физическое ядро ​​N mod 6.

Здесь типичный вывод top с stress -c 4 в двух столбцах, так что каждая строка является одним физическим ядром (я оставил несколько ядер, потому что моя система не простаивает):

%Cpu0  :100.0 us,   %Cpu6  :  0.0 us, 
%Cpu1  :100.0 us,   %Cpu7  :  0.0 us, 
%Cpu2  :  5.9 us,   %Cpu8  :  2.0 us, 
%Cpu3  :100.0 us,   %Cpu9  :  5.7 us, 
%Cpu4  :  3.9 us,   %Cpu10 :  3.8 us, 
%Cpu5  :  0.0 us,   %Cpu11 :100.0 us, 

Вот он после убийства и перезапуска stress:

%Cpu0  :100.0 us,   %Cpu6  :  2.6 us, 
%Cpu1  :100.0 us,   %Cpu7  :  0.0 us, 
%Cpu2  :  0.0 us,   %Cpu8  :  0.0 us, 
%Cpu3  :  2.6 us,   %Cpu9  :  0.0 us, 
%Cpu4  :  0.0 us,   %Cpu10 :100.0 us, 
%Cpu5  :  2.6 us,   %Cpu11 :100.0 us, 

Я делал это несколько раз и не видел ни одного экземпляра, где 4 потока по 12 логическим ядрам планировались бы на одном физическом ядре.

С -c 6 Я имею тенденцию получать такие результаты, когда Linux, по-видимому, помогает в планировании других процессов на своих физических ядрах. Тем не менее, они распределены лучше, чем вероятность:

%Cpu0  : 18.2 us,   %Cpu6  :  4.5 us, 
%Cpu1  :  0.0 us,   %Cpu7  :100.0 us, 
%Cpu2  :100.0 us,   %Cpu8  :100.0 us, 
%Cpu3  :100.0 us,   %Cpu9  :  0.0 us, 
%Cpu4  :100.0 us,   %Cpu10 :  0.0 us, 
%Cpu5  :100.0 us,   %Cpu11 :  0.0 us, 

Ответ 3

Процитировав свой опыт с двумя дополнительными процессорами, которые, казалось, работали правильно, i7-2600 и Xeon E5-1620; Это может быть длинный, но как насчет обновления микрокода процессора? Он может включать в себя что-то, чтобы устранить проблему, если это внутреннее поведение процессора.

Загрузка микрокода Intel CPU: http://intel.ly/1aku6ak

Также см. здесь: https://wiki.archlinux.org/index.php/Microcode