Compilando com g ++ usando vários núcleos


174

Pergunta rápida: qual é o sinalizador do compilador que permite ao g ++ gerar várias instâncias de si mesmo para compilar projetos grandes mais rapidamente (por exemplo, 4 arquivos de origem por vez para uma CPU com vários núcleos)?


Isso realmente vai ajudar? Todos os meus trabalhos de compilação são vinculados à E / S, e não à CPU.
Brian Knoblauch

5
Mesmo que eles estejam vinculados à E / S, provavelmente você poderá manter a carga de E / S mais alta quando os bits pesados ​​da CPU estiverem acontecendo (com apenas uma instância de g ++, haverá interrupções) e possivelmente obter eficiência de E / S se o planejador tiver mais opções sobre o que ler do disco a seguir. Minha experiência foi que o uso criterioso de make -jquase sempre resulta em alguma melhoria.
Flexo

1
@BrianKnoblauch Mas na minha máquina (real ou no VirtualBox), está ligada à CPU, descobri que a CPU está ocupada pelo comando 'top' ao compilar.
大宝剑

1
Mesmo se eles estiverem vinculados à E / S, podemos usar o sinalizador do gcc '-pipe' para reduzir a dor.
大宝剑

Respostas:


240

Você pode fazer isso com o make - com o gnu make é o sinalizador -j (isso também ajudará em uma máquina uniprocessadora).

Por exemplo, se você deseja 4 trabalhos paralelos do make:

make -j 4

Você também pode executar o gcc em um pipe com

gcc -pipe

Isso fará o pipeline dos estágios de compilação, o que também ajudará a manter os núcleos ocupados.

Se você tiver máquinas adicionais disponíveis, verifique o distcc , que também compila essas compilações.


36
Seu número -j deve ser 1,5x o número de núcleos que você possui.
22630 Mark Beckwith

2
Obrigado. Continuei tentando passar "-j #" para o gcc via CFLAGS / CPPFLAGS / CXXFLAGS. Eu tinha esquecido completamente que "-j #" era um parâmetro para o GNU make (e não para o GCC).
chriv

33
Por que a opção -j para o GNU Make precisa ser 1,5 x o número de núcleos da CPU?
Bitek

28
O número 1.5 é devido ao problema de ligação de E / S observado . É uma regra de ouro. Cerca de 1/3 dos trabalhos aguardam E / S, portanto, os trabalhos restantes usarão os núcleos disponíveis. Um número maior que os núcleos é melhor e você pode até chegar a 2x . Veja também: Gnu faz -jargumentos
ruído artless 31/07

4
@JimMichaels Pode ser porque as dependências estão mal definidas no seu projeto (um destino começa a ser construído mesmo que as dependências ainda não estejam prontas), para que apenas uma compilação sequencial acabe sendo bem-sucedida.
Antonio

42

Não existe esse sinalizador, e ter um deles é executado contra a filosofia do Unix de fazer com que cada ferramenta execute apenas uma função e a execute bem. Gerar processos do compilador é conceitualmente o trabalho do sistema de compilação. O que você provavelmente está procurando é o sinalizador -j (jobs) para o GNU make, a la

make -j4

Ou você pode usar pmake ou sistemas de fabricação paralela similares.



3
"Pediatria Unix não ajuda" Ainda bem que não era pediatria, editor anônimo. Revertida. Os revisores prestam mais atenção ao que você está fazendo.
Lightness Races in Orbit

12

As pessoas mencionaram, makemas bjamtambém apóiam um conceito semelhante. O uso de bjam -jxinstrui o bjam a criar xcomandos simultâneos.

Usamos os mesmos scripts de compilação no Windows e Linux e essa opção reduz pela metade o tempo de compilação nas duas plataformas. Agradável.


9

makefará isso por você. Investigue as opções -je -lna página de manual. Eu não acho que g++é paralelizável.


+1 para mencionar a -lopção (não inicia um novo trabalho, a menos que todos os trabalhos anteriores tenham sido encerrados). Caso contrário, parece que o trabalho do vinculador começa com nem todos os arquivos de objeto criados (como algumas compilações ainda estão em andamento), para que o trabalho do vinculador falhe.
NGI

8

Se estiver usando make, emita com -j. De man make:

  -j [jobs], --jobs[=jobs]
       Specifies the number of jobs (commands) to run simultaneously.  
       If there is more than one -j option, the last one is effective.
       If the -j option is given without an argument, make will not limit the
       number of jobs that can run simultaneously.

E, principalmente, se você deseja criar um script ou identificar o número de núcleos que você tem disponível (dependendo do seu ambiente e se você executa em muitos ambientes, isso pode mudar bastante), você pode usar a onipresente função Python cpu_count():

https://docs.python.org/3/library/multiprocessing.html#multiprocessing.cpu_count

Como isso:

make -j $(python3 -c 'import multiprocessing as mp; print(int(mp.cpu_count() * 1.5))')

Se você está perguntando por 1.5que citarei o ruído artless do usuário em um comentário acima:

O número 1.5 é devido ao problema de ligação de E / S observado. É uma regra de ouro. Cerca de 1/3 dos trabalhos aguardam E / S, portanto, os trabalhos restantes usarão os núcleos disponíveis. Um número maior que os núcleos é melhor e você pode até chegar a 2x.


5
A maioria dos usuários de Linux provavelmente preferirá os mais curtos: make -j`nproc` com nprocno GNU Coreutils.
Ciro Santilli escreveu:

Se você estiver usando um SSD, a E / S não será um problema. Apenas para criar o comentário de Ciro acima, você pode fazer o seguinte: make -j $(( $(nproc) + 1 ))(certifique-se de colocar espaços onde os tenho).
Ed K

Boa sugestão usando python, em sistemas onde nprocnão está disponível, por exemplo, em manylinux1contêineres, economiza tempo adicional ao evitar a execução yum update/ yum install.
hoefling


3

Não tenho certeza sobre o g ++, mas se você estiver usando o GNU Make, "make -j N" (onde N é o número de threads que o make pode criar) permitirá que o make execute vários trabalhos do g ++ ao mesmo tempo (por muito tempo porque os arquivos não dependem um do outro).


2
não N não é o número de threads! Muitas pessoas não entendem isso, mas -j Ndizem quantos processos ao mesmo tempo devem ser gerados, não threads. Essa é a razão pela qual não é tão eficiente quanto a MS cl -MT(realmente multithread).
precisa saber é o seguinte

2

Paralelo GNU

Eu estava fazendo um benchmark de compilação sintética e não me incomodei em escrever um Makefile, então usei:

sudo apt-get install parallel
ls | grep -E '\.c$' | parallel -t --will-cite "gcc -c -o '{.}.o' '{}'"

Explicação:

  • {.} pega o argumento de entrada e remove sua extensão
  • -t imprime os comandos que estão sendo executados para nos dar uma ideia do progresso
  • --will-cite remove a solicitação de citação do software se você publicar resultados usando-o ...

parallel é tão conveniente que eu poderia até fazer um registro de data e hora:

ls | grep -E '\.c$' | parallel -t --will-cite "\
  if ! [ -f '{.}.o' ] || [ '{}' -nt '{.}.o' ]; then
    gcc -c -o '{.}.o' '{}'
  fi
"

xargs -Ptambém pode executar tarefas em paralelo, mas é um pouco menos conveniente fazer a manipulação de extensão ou executar vários comandos com ela: Chamando vários comandos através do xargs

A ligação paralela foi solicitada em: O gcc pode usar vários núcleos ao vincular?

TODO: Acho que li em algum lugar que a compilação pode ser reduzida à multiplicação de matrizes, então talvez também seja possível acelerar a compilação de arquivo único para arquivos grandes. Mas não consigo encontrar uma referência agora.

Testado no Ubuntu 18.10.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.