GNU make: должно ли количество заданий равняться количеству процессорных ядер в системе? - PullRequest
76 голосов
/ 23 марта 2010

Кажется, есть некоторые противоречия по поводу того, должно ли число заданий в GNU make быть равно числу ядер, или можно оптимизировать время сборки, добавив одну дополнительную работу, которая может быть поставлена ​​в очередь, в то время как другие "работать".

Лучше ли использовать -j4 или -j5 в четырехъядерной системе?

Вы видели (или делали) какие-либо тесты, которые поддерживают один или другой?

Ответы [ 8 ]

50 голосов
/ 23 марта 2010

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

Мой личный опыт (на 2-ядерном MacBook Pro) заключается в том, что -j2 значительно быстрее, чем -j1, но кроме этого (-j3, -j4 и т. Д.) Ощутимого ускорения нет. Так что для моей среды "работа == количество ядер", кажется, хороший ответ. (YMMV) * ​​1003 *

48 голосов
/ 17 сентября 2013

Я запустил свой домашний проект на своем 4-ядерном ноутбуке с гиперпоточностью и записал результаты. Это довольно сложный для компиляции проект, но в конце он включает в себя модульное тестирование продолжительностью 17,7 секунды. Компиляции не очень интенсивные; доступно очень много памяти, а если нет, то на быстром SSD.

1 job        real   2m27.929s    user   2m11.352s    sys    0m11.964s    
2 jobs       real   1m22.901s    user   2m13.800s    sys    0m9.532s
3 jobs       real   1m6.434s     user   2m29.024s    sys    0m10.532s
4 jobs       real   0m59.847s    user   2m50.336s    sys    0m12.656s
5 jobs       real   0m58.657s    user   3m24.384s    sys    0m14.112s
6 jobs       real   0m57.100s    user   3m51.776s    sys    0m16.128s
7 jobs       real   0m56.304s    user   4m15.500s    sys    0m16.992s
8 jobs       real   0m53.513s    user   4m38.456s    sys    0m17.724s
9 jobs       real   0m53.371s    user   4m37.344s    sys    0m17.676s
10 jobs      real   0m53.350s    user   4m37.384s    sys    0m17.752s
11 jobs      real   0m53.834s    user   4m43.644s    sys    0m18.568s
12 jobs      real   0m52.187s    user   4m32.400s    sys    0m17.476s
13 jobs      real   0m53.834s    user   4m40.900s    sys    0m17.660s
14 jobs      real   0m53.901s    user   4m37.076s    sys    0m17.408s
15 jobs      real   0m55.975s    user   4m43.588s    sys    0m18.504s
16 jobs      real   0m53.764s    user   4m40.856s    sys    0m18.244s
inf jobs     real   0m51.812s    user   4m21.200s    sys    0m16.812s

Основные результаты:

  • Масштабирование до количества ядер увеличивает производительность почти линейно. Реальное время сократилось с 2,5 минут до 1,0 минуты (в 2,5 раза быстрее), но время, необходимое для компиляции, увеличилось с 2,11 до 2,50 минут. Система едва заметила дополнительную нагрузку в этом бите.
  • Масштабирование от количества ядер до числа потоков значительно увеличило пользовательскую нагрузку с 2,50 до 4,38 минут. Это почти удвоение вероятнее всего потому, что другие экземпляры компилятора хотели использовать одни и те же ресурсы ЦП одновременно. Система становится все более загруженной запросами и переключением задач, что приводит к 17,7 секундам использованного времени. Преимущество составляет около 6,5 секунд при времени компиляции 53,5 секунды, что обеспечивает ускорение на 12%.
  • Масштабирование от числа потоков до числа двойных потоков не дало существенного ускорения. Время 12 и 15, скорее всего, статистические аномалии, которые вы можете игнорировать. Общее время, затрачиваемое на увеличение, очень незначительно, как и системное время. Оба, скорее всего, связаны с повышенным переключением задач. В этом нет никакой пользы.

Мое предположение прямо сейчас: если вы делаете что-то еще на своем компьютере, используйте количество ядер. Если вы этого не сделаете, используйте счетчик потоков. Превышение этого не показывает никакой выгоды. В какой-то момент они станут ограничены в памяти и из-за этого рухнут, что сделает компиляцию намного медленнее. Линия «inf» была добавлена ​​гораздо позже, что дало мне подозрение, что для 8+ заданий было некоторое тепловое удушение. Это показывает, что для этого размера проекта не существует никакого ограничения памяти или пропускной способности. Это небольшой проект, учитывая 8 ГБ памяти для компиляции.

30 голосов
/ 23 марта 2010

Я лично использую make -j n, где n - это «количество ядер» + 1.

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

В любом случае, вы должны быть осторожны, потому что некоторые производственные цепочки просто не совместимы с опцией --jobs и могут привести к неожиданным результатам.Если вы испытываете странные ошибки зависимости, просто попробуйте make без --jobs.

7 голосов
/ 23 марта 2010

В конечном итоге вам нужно будет сделать несколько тестов, чтобы определить наилучшее число для вашей сборки, но помните, что ЦП - не единственный ресурс, который имеет значение!

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

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

Не могу сказать, что я специально пробовал # ядер + 1 , но в наших системах (Intel i7 940, 4 многопоточных ядра, много ОЗУ и накопители VelociRaptor) и нашей сборке (большой - масштабирование сборки C ++, которая поочередно связана с процессором и вводом / выводом) разница между -j4 и -j8 очень мала. (Может быть, на 15% лучше ... но далеко не в два раза лучше.)

Если я ухожу на обед, я буду использовать -j8, но если я хочу использовать свою систему для чего-то еще, пока она собирается, я буду использовать меньшее число. :)

4 голосов
/ 13 июля 2013

Я только что получил процесс Athlon II X2 Regor с Foxconn M / B и 4 ГБ памяти G-Skill.

Я поставил свои 'cat / proc / cpuinfo' и 'free' в конце этого, чтобы другие могли видеть мои спецификации. Это двухъядерный процессор Athlon II x2 с 4 ГБ оперативной памяти.

uname -a on default slackware 14.0 kernel is 3.2.45.

Я скачал исходный код ядра (linux-3.2.46) в / archive4;

извлек это (tar -xjvf linux-3.2.46.tar.bz2);

cd'd в каталог (cd linux-3.2.46);

и скопировал конфигурацию ядра по умолчанию поверх (cp /usr/src/linux/.config .);

использовал make oldconfig для подготовки конфигурации ядра 3.2.46;

затем запустил make с различными заклинаниями -jX.

Я протестировал время каждого запуска, выполнив команду make после времени, например, 'время делает -j2'. Между каждым запуском я запускаю 'rm -rf' дерево linux-3.2.46 и реэкстрагирую его, копирую /usr/src/linux/.config по умолчанию в каталог, запускаю make oldconfig, а затем снова делаю мой тест 'make -jX' .

обычный "make":

real    51m47.510s
user    47m52.228s
sys     3m44.985s
bob@Moses:/archive4/linux-3.2.46$

как указано выше, но с make -j2

real    27m3.194s
user    48m5.135s
sys     3m39.431s
bob@Moses:/archive4/linux-3.2.46$

как указано выше, но с make -j3

real    27m30.203s
user    48m43.821s
sys     3m42.309s
bob@Moses:/archive4/linux-3.2.46$

как указано выше, но с make -j4

real    27m32.023s
user    49m18.328s
sys     3m43.765s
bob@Moses:/archive4/linux-3.2.46$

как указано выше, но с make -j8

real    28m28.112s
user    50m34.445s
sys     3m49.877s
bob@Moses:/archive4/linux-3.2.46$

'cat / proc / cpuinfo' дает:

bob@Moses:/archive4$ cat /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 16
model           : 6
model name      : AMD Athlon(tm) II X2 270 Processor
stepping        : 3
microcode       : 0x10000c8
cpu MHz         : 3399.957
cache size      : 1024 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 2
apicid          : 0
initial apicid  : 0
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmo
v pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rd
tscp lm 3dnowext 3dnow constant_tsc nonstop_tsc extd_apicid pni monitor cx16 p
opcnt lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowpre
fetch osvw ibs skinit wdt npt lbrv svm_lock nrip_save
bogomips        : 6799.91
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management: ts ttp tm stc 100mhzsteps hwpstate

processor       : 1
vendor_id       : AuthenticAMD
cpu family      : 16
model           : 6
model name      : AMD Athlon(tm) II X2 270 Processor
stepping        : 3
microcode       : 0x10000c8
cpu MHz         : 3399.957
cache size      : 1024 KB
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2
apicid          : 1
initial apicid  : 1
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmo
v pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rd
tscp lm 3dnowext 3dnow constant_tsc nonstop_tsc extd_apicid pni monitor cx16 p
opcnt lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowpre
fetch osvw ibs skinit wdt npt lbrv svm_lock nrip_save
bogomips        : 6799.94
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management: ts ttp tm stc 100mhzsteps hwpstate

«бесплатные» выходы:

bob@Moses:/archive4$ free
             total       used       free     shared    buffers     cached
Mem:       3991304    3834564     156740          0     519220    2515308
3 голосов
/ 07 октября 2015

Просто как ссылка:

Из раздела Spawning Multiple Build Jobs в LKD :

, где n - количество созданных заданийОбычная практика - порождать одно или два задания на процессор.Например, на двухпроцессорной машине можно сделать

$ make j4

1 голос
/ 15 июня 2019

Оба не ошибаются.Чтобы быть в мире с самим собой и с автором компилируемого вами программного обеспечения (на самом уровне программного обеспечения применяются различные многопоточные / однопоточные ограничения), я предлагаю вам использовать:

make -j`nproc`

Примечания: nproc это команда linux, которая возвращает количество ядер / потоков (современный процессор), доступных в системе.Помещая его под галочки `, как указано выше, номер передается команде make.

Дополнительная информация: Как кто-то упоминал, использование всех ядер / потоков для компиляции программного обеспечения может буквально задушить ваш ящик почти до смерти (быть неотзывчивым) иможет даже занять больше времени, чем использование меньшего количества ядер.Как я видел, один пользователь Slackware, размещенный здесь, написал, что у него был двухъядерный процессор, но он все еще проводил тестирование до j 8, которое перестало быть другим при j 2 (только 2 аппаратных ядра, которые может использовать процессор).Таким образом, чтобы избежать отсутствия ответа, я предлагаю вам запустить его так:

make -j`nproc --ignore=2`

Это передаст вывод nproc в make и вычтет 2 ядра из его результата.

1 голос
/ 20 июля 2012

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

...