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.