Tutorial: Solucionar problemas de impacto do arquivo de cabeçalho no tempo de compilação
Use os modos de exibição Arquivos Incluídos e Árvore de Inclusão do Build Insights para solucionar problemas do impacto dos arquivos #include
nos tempos de compilação do C e C++.
Pré-requisitos
- Visual Studio 2022 17.8 ou superior.
- O Build Insights do C++ será habilitado por padrão se você instalar o desenvolvimento de Área de Trabalho com a carga de trabalho do C++ usando o instalador do Visual Studio:
A lista de componentes instalados é mostrada. O Build Insights do C++ está realçado e selecionado, o que significa que está instalado.
Ou o desenvolvimento de Jogos com a carga de trabalho do C++:
A lista de componentes instalados é mostrada. O Build Insights do C++ está realçado e selecionado, o que significa que está instalado.
Visão geral
O Build Insights, agora integrado ao Visual Studio, ajuda você a otimizar os tempos de compilação, especialmente para projetos grandes, como jogos AAA. Quando um arquivo de cabeçalho grande é analisado e, especialmente quando é analisado repetidamente, há um impacto no tempo de compilação.
O Build Insights fornece a análise no modo de exibição Arquivos Incluídos, que ajuda a diagnosticar o impacto da análise dos arquivos #include
no seu projeto. Ele exibe o tempo necessário para analisar cada arquivo de cabeçalho e uma exibição das relações entre arquivos de cabeçalho.
Neste artigo, saiba como usar os modos de exibição Arquivos Incluídos e Árvore de Inclusão do Build Insights para identificar os arquivos de cabeçalho mais dispendiosos a serem analisados e como otimizar o tempo de compilação criando um arquivo de cabeçalho pré-compilado.
Definir opções de build
Antes de coletar dados do Build Insights, defina as opções de compilação para o tipo de compilação que você deseja medir. Por exemplo, se você estiver preocupado com o tempo de compilação de depuração x64, defina a compilação como Depurar e x64:
Na lista suspensa Configurações da Solução, escolha Depurar.
Na lista suspensa Plataformas da Solução, escolha x64.
A lista suspensa Configuração da Solução será mostrada. Ela tem opções para o gerenciador de Depuração, Versão e Configuração. A lista suspensa Plataforma da Solução está definida como x64.
Executar o Build Insights
Em um projeto de sua escolha e usando as opções de compilação da Depurar definidas na seção anterior, execute o Build Insights escolhendo no menu principal Compilar>Executar o Build Insights na Seleção>Recompilar. Você também pode clicar com o botão direito do mouse em um projeto no gerenciador de soluções e escolher Executar o Build Insights>Recompilar. Escolha Recompilar, em vez de Compilar, para medir o tempo de compilação de todo o projeto e não apenas para os poucos arquivos que podem estar sujos no momento.
Quando a compilação for concluída, um arquivo de ETL (Log de Rastreamento de Eventos) será aberto. Ele será salvo na pasta apontada pela variável de ambiente do Windows TEMP
. O nome gerado é baseado no tempo de coleta.
Modo de exibição de Arquivos Incluídos
O arquivo de rastreamento mostra o tempo de compilação, que para este exemplo foi de 16,404 segundos. A Sessão de Diagnóstico é o tempo geral necessário para executar a sessão do Build Insights. Escolha a guia Arquivos Incluídos.
Essa exibição mostra o tempo gasto processando arquivos #include
.
Na coluna de caminho do arquivo, vários arquivos com um ícone de fogo estão realçados porque eles levam mais de 10% do tempo de compilação para analisar. winrtHeaders.h é o maior com 8,581 segundos ou 52,3% do tempo de compilação de 16,404 segundos.
Na coluna Caminho do Arquivo, alguns arquivos têm um ícone de fogo ao lado deles para indicar que eles ocupam 10% ou mais do tempo de compilação.
A coluna Tempo [s, %] mostra quanto tempo levou para compilar cada função no WCTR (tempo de responsabilidade do relógio de parede). Essa métrica distribui o tempo de relógio de parede necessário para analisar arquivos com base no uso de threads paralelos. Por exemplo, se dois threads diferentes estiverem analisando dois arquivos diferentes simultaneamente em um período de um segundo, o WCTR de cada arquivo será registrado como 0,5 segundos. Isso reflete a participação proporcional de cada arquivo do tempo total de compilação, levando em conta os recursos que cada um consumiu durante a execução paralela. Assim, o WCTR fornece uma melhor medida do impacto que cada arquivo tem no tempo geral de compilação nos ambientes em que várias atividades de compilação ocorrem simultaneamente.
A coluna Contagem de Análise mostra quantas vezes o arquivo de cabeçalho foi analisado.
O primeiro arquivo de cabeçalho realçado nesta lista é winrtHeaders.h
, que leva 8,581 segundos do tempo de compilação geral de 16,404 segundos ou 52,3% do tempo de compilação. O próximo mais dispendioso é Windows.UI.Xaml.Interop.h
e, em seguida, Windows.Xaml.h
.
Para ver qual arquivo inclui winrtHeaders.h
, clique na divisa ao lado dele. A coluna Contagem de Análise pode ser útil apontando quantas vezes um arquivo de cabeçalho é incluído por outros arquivos. Talvez um arquivo de cabeçalho seja incluído várias vezes, o que pode ser um sinal de que ele é um bom candidato para um arquivo de cabeçalho pré-compilado ou refatoração.
A coluna Unidade de Tradução mostra qual arquivo estava sendo processado quando o arquivo incluído foi processado. Neste exemplo, winrtHeaders.h
foi incluído enquanto Grapher.cpp
era compilado:
Um arquivo de ETL de exemplo mostrando os arquivos inclusos para um projeto de exemplo. Na coluna de caminho do arquivo, winrtHeaders.h está selecionado e expandido. Leva 8,219 segundos para compilar, o que representa 50,1% do tempo de compilação. Seu nó filho é Grapher.cpp, que também está listado como a unidade de tradução".
A coluna de unidade de tradução pode ajudar a determinar qual arquivo estava sendo compilado nos casos em que um arquivo de cabeçalho é incluído muitas vezes e você deseja descobrir onde isso acontece mais.
Sabemos que winrtHeaders.h
é dispendioso de analisar, mas podemos aprender mais.
Modo de Exibição de Árvore de Inclusão
Nesse modo de exibição, os nós filhos são os arquivos incluídos pelo nó pai. Isso pode ajudar a entender as relações entre arquivos de cabeçalho e identificar oportunidades para reduzir o número de vezes que um arquivo de cabeçalho é analisado.
Selecione a guia Árvore de Inclusão no arquivo de ETL para ver o modo de exibição de Árvore de Inclusão:
Mostra a árvore de inclusão de um projeto. Na coluna de caminho do arquivo, cada arquivo que inclui outros arquivos é listado, juntamente com quantos arquivos ele inclui e o tempo para analisá-lo.
Nesse modo de exibição, a coluna Caminho do Arquivo mostra cada arquivo que inclui outros arquivos. A Contagem de Inclusão lista quantos arquivos esse arquivo de cabeçalho inclui. O tempo para analisar esse arquivo é listado e, quando expandido, lista o tempo para analisar cada arquivo de cabeçalho individual que esse arquivo de cabeçalho inclui.
Anteriormente, vimos que a análise winrtHeaders.h
é demorada. Na caixa de texto Filtrar Arquivos, se inserirmos winrtHeaders.h
, podemos filtrar a exibição apenas para as entradas que contêm winrtHeaders.h
no nome. Clicar na divisa ao lado de winrtHeaders.h
mostra quais arquivos ele inclui:
A coluna de caminho do arquivo lista cada arquivo que inclui outros arquivos, juntamente com quantos arquivos ele inclui e o tempo necessário para analisá-lo. winrtHeaders.h está selecionado e expandido para mostrar os arquivos que ele inclui. Windows.UI.Xaml.Interop.h é um desses arquivos e está expandido para mostrar Windows.UI.Xaml.Interop.h, que é expandido para mostrar os arquivos de cabeçalho que ele inclui.
Vemos que winrtHeaders.h
inclui Windows.UI.Xaml.Interop.h
. Lembre-se do modo de exibição de Arquivos Incluídos que isso também foi demorado para analisar. Clique na divisa ao lado de Windows.UI.Xaml.Interop.h
para ver que ela inclui Windows.UI.Xaml.h
, que inclui outros 21 arquivos de cabeçalho, dois dos quais também estão na lista de ativação.
Ter identificado alguns dos arquivos de cabeçalho mais dispendiosos para analisar e ver que winrtHeaders.h
é responsável por trazê-los sugere que podemos usar um cabeçalho pré-compilado para tornar a inclusão de winrtHeaders.h
mais rápida.
Melhorar o tempo de compilação com cabeçalhos pré-compilados
Como sabemos no modo de exibição de Arquivos Incluídos que winrtHeaders.h
é demorado de analisar e, como sabemos no modo de exibição de Árvore de Inclusão que winrtHeaders.h
inclui vários outros arquivos de cabeçalho que são demorados para analisar, criamos um PCH (arquivo de cabeçalho pré-compilado) para acelerar isso analisando-os apenas uma vez em um PCH.
Adicionamos um pch.h
para incluir winrtHeaders.h
, que seria assim:
#ifndef CALC_PCH
#define CALC_PCH
#include <winrtHeaders.h>
#endif // CALC_PCH
Os arquivos PCH devem ser compilados antes que possam ser usados. Portanto, adicionamos um arquivo ao projeto, chamado pch.cpp
arbitrariamente, que inclui pch.h
. Ele contém uma linha:
#include "pch.h"
Em seguida, definimos nosso projeto para usar o PCH. Isso é feito nas propriedades do projeto em C/C++>Cabeçalhos Pré-Compilados e definindo Cabeçalho Pré-Compilado como Usar (/Yu) e Arquivo de Cabeçalho Pré-Compilado como pch.h.
Cabeçalho Pré-Compilado é definido como: Usar (/Yu). O Arquivo de Cabeçalho Pré-Compilado é definido como pch.h.
Para usar o PCH, ele é incluído como a primeira linha nos arquivos de origem que usam winrtHeaders.h
. Ele deve vir antes de qualquer outro arquivo de inclusão. Ou, para simplificar, poderíamos modificar as propriedades do projeto para incluir pch.h
no início de cada arquivo na solução, definindo a propriedade do projeto: C/C++>Avançado>Arquivo de Inclusão Forçada como: pch.h
O Arquivo de Inclusão Forçada está definido como pch.h.
Como o PCH inclui winrtHeaders.h
, podemos remover winrtHeaders.h
de todos os arquivos que o incluem no momento. Ele não é estritamente necessário porque o compilador percebe que winrtHeaders.h
já está incluído e não o analisa novamente. Alguns desenvolvedores preferem manter o arquivo de origem #include
para maior clareza ou, caso haja a possibilidade de que o PCH seja refatorado e não inclua mais esse arquivo de cabeçalho.
Teste as mudanças
Primeiro, limpamos o projeto para verificar se estamos comparando a criação dos mesmos arquivos de antes. Para limpar apenas um projeto, clique com o botão direito do mouse no projeto no Gerenciador de Soluções e escolha Somente projeto>Somente limpar o <nome prj>.
Como este projeto agora usa um PCH (cabeçalho pré-compilado), não queremos medir o tempo gasto na criação do PCH porque isso só acontece uma vez. Fazemos isso carregando o arquivo pch.cpp
e escolhendo Ctrl+F7 para compilar apenas esse arquivo. Também é possível compilar esse arquivo clicando em pch.cpp
com o botão direito do mouse no Gerenciador de Soluções e escolhendo Compile
.
Agora, executamos novamente o Build Insights no Gerenciador de Soluções clicando com o botão direito do mouse no projeto e escolhendo Somente Projeto>Executar o Build Insights na Compilação. Você também pode clicar com o botão direito do mouse em um projeto no gerenciador de soluções e escolher Executar o Build Insights>Compilar. Não queremos Recompilar desta vez porque isso recriará o PCH, o que não queremos medir. Limpamos o projeto anteriormente, o que significa que um build normal compila todos os arquivos de projeto que queremos medir.
Quando os arquivos de ETL são exibidos, vemos que o tempo de compilação passou de 16,404 segundos para 6,615 segundos. Coloque winrtHeaders.h
na caixa de filtro e nada será exibido. Isso ocorre porque o tempo gasto analisando-o agora é insignificante, pois ele está sendo puxado pelo cabeçalho pré-compilado.
Este exemplo usa cabeçalhos pré-compilados porque eles são uma solução comum antes do C++20. No entanto, a partir do C++20, há outras maneiras, mais rápidas, menos frágeis, de incluir arquivos de cabeçalho, como unidades de cabeçalho e módulos. Para obter mais informações, confira Comparar unidades de cabeçalho, módulos e cabeçalhos pré-compilados.
Navegar entre exibições
Há alguns recursos de navegação para os modos de exibição Arquivos Incluídos e Árvore de Inclusão:
- Clique duas vezes em um arquivo (ou pressione Enter) em Arquivos Incluídos ou Árvore de Inclusão para abrir o código-fonte desse arquivo.
- Clique com o botão direito do mouse em um arquivo de cabeçalho para localizar esse arquivo na outra exibição. Por exemplo, no modo de exibição Arquivo Incluído, clique com o botão direito do mouse em
winrtHeaders.h
e escolha Localizar na Árvore de Inclusão para vê-lo no modo de exibição de Árvore de Inclusão.
Ou, você pode clicar com o botão direito do mouse em um arquivo no modo de exibição de Árvore de Inclusão para ir até ele no modo de exibição de Arquivos Incluídos.
Dicas
- Você pode usar a opção Arquivo>Salvar como no arquivo de ETL em um local mais permanente para manter um registro do tempo de compilação. Em seguida, você pode compará-lo com compilações futuras para ver se suas alterações estão melhorando o tempo de compilação.
- Se você fechar inadvertidamente a janela do Build Insights, reabra-a encontrando o arquivo
<dateandtime>.etl
na sua pasta temporária. A variável de ambiente do WindowsTEMP
fornece o caminho da pasta de arquivos temporários. - Para pesquisar os dados do Build Insights com o WPA (Windows Performance Analyzer), clique no botão Abrir no WPA na parte inferior direita da janela ETL.
- Arraste as colunas para alterar a ordem das colunas. Por exemplo, você pode preferir mover a coluna Tempo para ser a primeira coluna. Você pode ocultar colunas clicando com o botão direito do mouse no cabeçalho da coluna e desmarcando as colunas que não deseja ver.
- Os modos de exibição de Arquivos Incluídos e Árvore de Inclusão fornecem uma caixa de filtro para localizar um arquivo de cabeçalho no qual você está interessado. Ele faz correspondências parciais no nome que você fornece.
- Às vezes, o tempo de análise relatado para um arquivo de cabeçalho é diferente dependendo de qual arquivo o inclui. Isso pode ocorrer devido à interação de diferentes
#define
s que afetam quais partes do cabeçalho são expandidas, cache de arquivo e outros fatores do sistema. - Se você esquecer o que o modo de exibição de Arquivos Incluídos ou Árvore de Inclusão está tentando mostrar, passe o mouse sobre a guia para ver uma dica de ferramenta que descreve a exibição. Por exemplo, se você passar o mouse sobre a guia Árvore de Inclusão, a dica de ferramenta diz: "Exibição que mostra estatísticas de inclusão para cada arquivo em que os nós filhos são os arquivos incluídos pelo nó pai".
- Você pode ver casos (como
Windows.h
) em que a duração agregada de todas as vezes para um arquivo de cabeçalho é maior do que a duração de toda a compilação. O que está acontecendo é que os cabeçalhos estão sendo analisados em vários threads ao mesmo tempo. Se dois threads passarem simultaneamente um segundo analisando um arquivo de cabeçalho, isso corresponde a 2 segundos de tempo de compilação, embora apenas um segundo do tempo do relógio de parede tenha passado. Para obter mais informações, confira o WCTR (tempo de responsabilidade do relógio de parede).
Solução de problemas
- Se a janela do Build Insights não for exibida, faça uma recompilação, em vez de uma compilação. A janela do Build Insights não será exibida se nada realmente for compilado, o que pode ser o caso se nenhum arquivo foi alterado desde a última compilação.
- Se um arquivo de cabeçalho no qual você está interessado não aparecer nos modos de exibição de Arquivos Incluídos ou Árvore de Inclusão, ele não foi compilado ou seu tempo de compilação não será significativo o suficiente para ser listado.
Confira também
Comparar unidades de cabeçalho, módulos e cabeçalhos pré-compilados
Build Insights no vídeo do Visual Studio – Pure Virtual C++ 2023
Compilações C++ mais rápidas e simplificadas: uma nova métrica para o tempo
Tutorial: solucionar problemas da função embutida no tempo de compilação
Tutorial: vcperf e Windows Performance Analyzer