A compilação de um arquivo C ++ leva muito tempo quando comparada com C # e Java. Leva muito mais tempo para compilar um arquivo C ++ do que para executar um script Python de tamanho normal. Atualmente, estou usando o VC ++, mas é o mesmo com qualquer compilador. Por que é isso?
As duas razões pelas quais eu conseguia pensar estavam carregando arquivos de cabeçalho e executando o pré-processador, mas isso não parece explicar o porquê de demorar tanto.
c++
performance
compilation
Dan Goldstein
fonte
fonte
It takes significantly longer to compile a C++ file
- você quer dizer 2 segundos em comparação com 1 segundo? Certamente isso é o dobro do tempo, mas dificilmente significativo. Ou você quer dizer 10 minutos em comparação com 5 segundos? Quantifique.Respostas:
Vários motivos
Arquivos de cabeçalho
Cada unidade de compilação exige que centenas ou mesmo milhares de cabeçalhos sejam (1) carregados e (2) compilados. Normalmente, cada um deles deve ser recompilado para cada unidade de compilação, porque o pré-processador garante que o resultado da compilação de um cabeçalho possa variar entre cada unidade de compilação. (Uma macro pode ser definida em uma unidade de compilação que altera o conteúdo do cabeçalho).
Esse é provavelmente o principal motivo, pois exige que grandes quantidades de código sejam compiladas para cada unidade de compilação e, além disso, todo cabeçalho deve ser compilado várias vezes (uma vez para cada unidade de compilação que o inclui).
Linking
Uma vez compilados, todos os arquivos de objetos precisam ser vinculados. Este é basicamente um processo monolítico que não pode muito bem ser paralelo e precisa processar todo o seu projeto.
Análise
A sintaxe é extremamente complicada de analisar, depende muito do contexto e é muito difícil de desambiguar. Isso leva muito tempo.
Modelos
Em C #,
List<T>
é o único tipo que é compilado, não importa quantas instanciações da Lista você tenha no seu programa. Em C ++,vector<int>
é um tipo completamente separado devector<float>
, e cada um terá que ser compilado separadamente.Acrescente a isso que os modelos compõem uma "sub-linguagem" completa de Turing que o compilador precisa interpretar, e isso pode se tornar ridiculamente complicado. Mesmo o código de metaprogramação de modelos relativamente simples pode definir modelos recursivos que criam dezenas e dezenas de instanciações de modelos. Os modelos também podem resultar em tipos extremamente complexos, com nomes ridiculamente longos, adicionando muito trabalho extra ao vinculador. (Ele precisa comparar muitos nomes de símbolos e, se esses nomes puderem crescer em milhares de caracteres, isso poderá se tornar bastante caro).
E, é claro, eles exacerbam os problemas com os arquivos de cabeçalho, porque os modelos geralmente precisam ser definidos nos cabeçalhos, o que significa que muito mais código deve ser analisado e compilado para cada unidade de compilação. No código C simples, um cabeçalho normalmente contém apenas declarações de encaminhamento, mas muito pouco código real. No C ++, não é incomum que quase todo o código resida nos arquivos de cabeçalho.
Otimização
O C ++ permite algumas otimizações muito drásticas. C # ou Java não permitem que as classes sejam completamente eliminadas (elas precisam estar lá para fins de reflexão), mas mesmo um simples metaprograma de modelo C ++ pode gerar facilmente dezenas ou centenas de classes, todas elas alinhadas e eliminadas novamente na otimização Estágio.
Além disso, um programa C ++ deve ser totalmente otimizado pelo compilador. O programa AC # pode confiar no compilador JIT para executar otimizações adicionais no momento do carregamento, o C ++ não obtém nenhuma "segunda chance". O que o compilador gera é o mais otimizado possível.
Máquina
O C ++ é compilado no código da máquina, o que pode ser um pouco mais complicado do que o uso do Java ou .NET do bytecode (especialmente no caso do x86). (Isso é mencionado por completo apenas porque foi mencionado em comentários e outros. Na prática, é improvável que essa etapa leve mais do que uma fração minúscula do tempo total de compilação).
Conclusão
A maioria desses fatores é compartilhada pelo código C, que na verdade é compilado com bastante eficiência. A etapa de análise é muito mais complicada em C ++ e pode levar muito mais tempo, mas o principal agressor é provavelmente os modelos. Eles são úteis e tornam o C ++ uma linguagem muito mais poderosa, mas também causam prejuízos em termos de velocidade de compilação.
fonte
A desaceleração não é necessariamente a mesma com qualquer compilador.
Eu não usei o Delphi ou o Kylix, mas nos dias de MS-DOS, um programa Turbo Pascal seria compilado quase instantaneamente, enquanto o programa Turbo C ++ equivalente era rastreado.
As duas principais diferenças eram um sistema de módulos muito forte e uma sintaxe que permitia a compilação de passagem única.
Certamente é possível que a velocidade de compilação simplesmente não tenha sido uma prioridade para os desenvolvedores de compiladores C ++, mas também existem algumas complicações inerentes à sintaxe do C / C ++ que dificultam o processamento. (Não sou especialista em C, mas Walter Bright é, e depois de criar vários compiladores comerciais de C / C ++, ele criou a linguagem D. Uma de suas mudanças foi impor uma gramática livre de contexto para facilitar a análise da linguagem .)
Além disso, você notará que geralmente os Makefiles são configurados para que todos os arquivos sejam compilados separadamente em C; portanto, se 10 arquivos de origem usarem o mesmo arquivo de inclusão, esse arquivo de inclusão será processado 10 vezes.
fonte
A análise e a geração de código são realmente bastante rápidas. O verdadeiro problema é abrir e fechar arquivos. Lembre-se, mesmo com os protetores de inclusão, o compilador ainda abriu o arquivo .H e leu cada linha (e depois o ignore).
Um amigo, uma vez (enquanto estava entediado no trabalho), pegou o aplicativo da empresa e colocou tudo - todos os arquivos de origem e cabeçalho - em um grande arquivo. O tempo de compilação caiu de 3 horas para 7 minutos.
fonte
Outro motivo é o uso do pré-processador C para localizar declarações. Mesmo com os protetores de cabeçalho, .h ainda precisa ser analisado repetidamente, toda vez que for incluído. Alguns compiladores oferecem suporte a cabeçalhos pré-compilados que podem ajudar com isso, mas nem sempre são usados.
Veja também: Respostas frequentes sobre C ++
fonte
C ++ é compilado no código da máquina. Então você tem o pré-processador, o compilador, o otimizador e, finalmente, o montador, os quais precisam ser executados.
Java e C # são compilados em código de bytes / IL e a máquina virtual Java / .NET Framework é executada (ou compilada JIT em código de máquina) antes da execução.
Python é uma linguagem interpretada que também é compilada em código de bytes.
Tenho certeza de que existem outros motivos para isso também, mas, em geral, não ter que compilar no idioma nativo da máquina economiza tempo.
fonte
Os maiores problemas são:
1) O cabeçalho infinito reanalisando. Já mencionado. As mitigações (como #pragma uma vez) geralmente funcionam apenas por unidade de compilação, não por compilação.
2) O fato de a cadeia de ferramentas ser frequentemente separada em vários binários (marca, pré-processador, compilador, montador, arquivador, impdef, linker e dlltool em casos extremos) que todos precisam reinicializar e recarregar todo o estado o tempo todo para cada chamada ( compilador, montador) ou todos os arquivos (arquivador, vinculador e dlltool).
Veja também esta discussão em comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078, especialmente esta:
http://compilers.iecc.com/comparch/article/02-07-128
Observe que John, o moderador dos comp.compilers parece concordar, e que isso significa que também deve ser possível obter velocidades semelhantes para C, se alguém integrar a cadeia de ferramentas completamente e implementar cabeçalhos pré-compilados. Muitos compiladores C comerciais fazem isso em algum grau.
Observe que o modelo Unix de fatorar tudo em um binário separado é um tipo de modelo de pior caso para o Windows (com sua lenta criação de processos). É muito perceptível ao comparar os tempos de criação do GCC entre o Windows e o * nix, especialmente se o sistema make / configure também chamar alguns programas apenas para obter informações.
fonte
Construindo C / C ++: o que realmente acontece e por que demora tanto
Uma parte relativamente grande do tempo de desenvolvimento de software não é gasta na gravação, execução, depuração ou até no design de código, mas na espera pela conclusão da compilação. Para agilizar as coisas, primeiro precisamos entender o que está acontecendo quando o software C / C ++ é compilado. As etapas são aproximadamente as seguintes:
Agora, analisaremos cada etapa com mais detalhes, focando em como eles podem ser feitos mais rapidamente.
Configuração
Este é o primeiro passo ao iniciar a construção. Geralmente significa executar um script de configuração ou CMake, Gyp, SCons ou alguma outra ferramenta. Isso pode levar de um segundo a vários minutos para scripts de configuração muito grandes baseados em Autotools.
Este passo acontece relativamente raramente. Ele só precisa ser executado ao alterar configurações ou alterar a configuração de compilação. Com exceção da mudança dos sistemas de construção, não há muito a ser feito para acelerar esse passo.
Arranque da ferramenta de construção
É o que acontece quando você executa o make ou clica no ícone de construção em um IDE (que geralmente é um alias para o make). O binário da ferramenta de construção é iniciado e lê seus arquivos de configuração, bem como a configuração de construção, que geralmente são a mesma coisa.
Dependendo da complexidade e tamanho da compilação, isso pode levar de uma fração de segundo a vários segundos. Por si só, isso não seria tão ruim. Infelizmente, a maioria dos sistemas de build baseados em make faz com que o invocador seja invocado dezenas de centenas de vezes para cada build único. Geralmente, isso é causado pelo uso recursivo de make (o que é ruim).
Deve-se notar que o motivo pelo qual o Make é tão lento não é um bug de implementação. A sintaxe do Makefiles possui algumas peculiaridades que tornam uma implementação realmente rápida quase impossível. Esse problema é ainda mais perceptível quando combinado com a próxima etapa.
Verificação de dependência
Depois que a ferramenta de construção lê sua configuração, ela precisa determinar quais arquivos foram alterados e quais precisam ser recompilados. Os arquivos de configuração contêm um gráfico acíclico direcionado que descreve as dependências de construção. Esse gráfico geralmente é construído durante a etapa de configuração. O tempo de inicialização da ferramenta de compilação e o scanner de dependência são executados em cada compilação. Seu tempo de execução combinado determina o limite inferior no ciclo de edição-compilação-depuração. Para pequenos projetos, esse tempo geralmente leva alguns segundos. Isso é tolerável. Existem alternativas para o Make. O mais rápido deles é o Ninja, construído pelos engenheiros do Google para o Chromium. Se você estiver usando o CMake ou o Gyp para construir, mude para os back-ends Ninja deles. Você não precisa alterar nada nos arquivos de compilação, apenas aproveite o aumento de velocidade. O Ninja não é fornecido na maioria das distribuições, no entanto,
Compilação
Neste ponto, finalmente chamamos o compilador. Cortando alguns cantos, aqui estão os passos aproximados dados.
Ao contrário da crença popular, compilar C ++ não é tão lento assim. O STL é lento e a maioria das ferramentas de compilação usadas para compilar C ++ é lenta. No entanto, existem ferramentas e maneiras mais rápidas de mitigar as partes lentas do idioma.
Usá-los requer um pouco de graxa nos cotovelos, mas os benefícios são inegáveis. Tempos de construção mais rápidos levam a desenvolvedores mais felizes, mais agilidade e, eventualmente, melhor código.
fonte
Uma linguagem compilada sempre exigirá uma sobrecarga inicial maior do que uma linguagem interpretada. Além disso, talvez você não tenha estruturado muito bem o seu código C ++. Por exemplo:
Compila muito mais lentamente que:
fonte
Uma maneira fácil de reduzir o tempo de compilação em projetos C ++ maiores é criar um arquivo de inclusão * .cpp que inclua todos os arquivos cpp no seu projeto e compilá-lo. Isso reduz o problema de explosão do cabeçalho para uma vez. A vantagem disso é que os erros de compilação ainda farão referência ao arquivo correto.
Por exemplo, suponha que você tenha a.cpp, b.cpp e c.cpp. Crie um arquivo: everything.cpp:
Em seguida, compile o projeto criando apenas everything.cpp
fonte
Alguns motivos são:
1) A gramática C ++ é mais complexa que C # ou Java e leva mais tempo para analisar.
2) (Mais importante) O compilador C ++ produz código de máquina e faz todas as otimizações durante a compilação. C # e Java vão apenas pela metade e deixam essas etapas para o JIT.
fonte
A desvantagem que você está recebendo é que o programa é executado um pouquinho mais rápido. Isso pode ser um conforto frio para você durante o desenvolvimento, mas pode ter grande importância quando o desenvolvimento estiver completo e o programa estiver sendo executado pelos usuários.
fonte
A maioria das respostas não é clara ao mencionar que o C # sempre será mais lento devido ao custo de executar ações que no C ++ são executadas apenas uma vez no tempo de compilação, esse custo de desempenho também é impactado devido às dependências do tempo de execução (mais coisas a serem carregadas para poder para executar), sem mencionar que os programas C # sempre terão maior espaço de memória, todos resultando em desempenho mais estreitamente relacionado à capacidade do hardware disponível. O mesmo vale para outros idiomas que são interpretados ou dependem de uma VM.
fonte
Existem dois problemas que podem estar afetando a velocidade com que seus programas em C ++ estão sendo compilados.
POSSÍVEL PROBLEMA # 1 - COMPILAR O CABEÇALHO: (Isso já pode ou não ter sido abordado por outra resposta ou comentário.) O Microsoft Visual C ++ (AKA VC ++) oferece suporte a cabeçalhos pré-compilados, que eu recomendo. Quando você cria um novo projeto e seleciona o tipo de programa que está criando, uma janela do assistente de instalação deve aparecer na tela. Se você pressionar o botão "Próximo>" na parte inferior, a janela o levará a uma página que possui várias listas de recursos; verifique se a caixa ao lado da opção "Cabeçalho pré-compilado" está marcada. (Observação: essa tem sido minha experiência com aplicativos de console do Win32 em C ++, mas pode não ser o caso de todos os tipos de programas em C ++.)
POSSÍVEL PROBLEMA # 2 - O LOCAL QUE ESTÁ ACOMPANHADO: Neste verão, fiz um curso de programação e tivemos que armazenar todos os nossos projetos em pen drives de 8 GB, pois os computadores do laboratório que estávamos usando eram limpos todas as noites à meia-noite, o que apagaria todo o nosso trabalho. Se você estiver compilando em um dispositivo de armazenamento externo para portabilidade / segurança / etc., Pode demorar muitotempo (mesmo com os cabeçalhos pré-compilados que eu mencionei acima) para o seu programa compilar, especialmente se for um programa bastante grande. Meu conselho para você, nesse caso, seria criar e compilar programas no disco rígido do computador que você está usando e sempre que quiser / precisar parar de trabalhar no (s) seu (s) projeto (s) por qualquer motivo, transfira-o para o seu computador externo. dispositivo de armazenamento e clique no ícone “Remover hardware com segurança e ejetar mídia”, que deve aparecer como uma pequena unidade flash atrás de um pequeno círculo verde com uma marca de seleção branca, para desconectá-lo.
Espero que isso ajude você; deixe-me saber se faz! :)
fonte