header files [RESOLVIDO]

1. header files [RESOLVIDO]

Berghetti
berghetti

(usa Debian)

Enviado em 08/04/2020 - 15:05h

Ola,
uma questão aqui...

quando um projeto começa a ficar um muitos arquivos (*.c, *.h), começa ficar chato o gerenciamento de includes.

pensei em criar um único arquivo de cabeçalho e incluir todas as bibliotecas do sistema (leia se, que não são minhas...) que vou utilizar,
e em meus arquivos incluir apenas esse cabeçalho.

não consegui pensar em algum maleficio que isso possa trazer, então gostaria de perguntar se isso pode gerar algum problema?

lembrando que minhas bibliotecas não serão incluídas nesse arquivo (para evitar recompilação desnecessária de todos os arquivos caso altere a minha biblioteca)



  


2. Re: header files [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/04/2020 - 20:12h

berghetti escreveu:

quando um projeto começa a ficar um muitos arquivos (*.c, *.h), começa ficar chato o gerenciamento de includes.

pensei em criar um único arquivo de cabeçalho e incluir todas as bibliotecas do sistema (leia se, que não são minhas...) que vou utilizar,
e em meus arquivos incluir apenas esse cabeçalho.

não consegui pensar em algum maleficio que isso possa trazer, então gostaria de perguntar se isso pode gerar algum problema?


Quanto mais arquivos você inclui, maior o tempo de compilação. Se você colocar muitos cabeçalhos desnecessários, esse tempo vai tender a aumentar. Isso vale para o C, mas é ainda mais grave em C++.

lembrando que minhas bibliotecas não serão incluídas nesse arquivo (para evitar recompilação desnecessária de todos os arquivos caso altere a minha biblioteca)


Para usar essa abordagem que você sugeriu de um modo realmente mais útil, trazendo ganhos em vez de perda de desempenho, você poderia associar esse arquivo a um arquivo de cabeçalhos precompilados, de modo que um #include "meuarquivo.h" vai acabar, na verdade, provocando a leitura de um arquivo precompilado com nome meuarquivo.h.gch ou meuarquivo.pch (dependendo de se você usar GCC ou Visual Studio; outros compiladores podem ter o recurso mas usar outras convenções de nomes).

No entanto, o uso de cabeçalhos precompilados tem suas restrições. Leia a documentação do seu compilador para aprender a usá-los adequadamente.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


3. Re: header files [RESOLVIDO]

Berghetti
berghetti

(usa Debian)

Enviado em 09/04/2020 - 12:25h


não conhecia essa técnica de cabeçalhos pre compilados... bom saber, obrigado!

só achei exemplos de uso para C++ (estou trabalhando em C), creio que como falou, pelo fato de sofrer mais com tempo de compilação,
tentei utilizar aqui apenas para conhecimento e não tive sucesso (usando gcc 7.5.0).

fiz
gcc sys-headers.h 

e ele gerou o arquivo sys-headers.h.ghc

porem não houve diferença no tempo de compilação e caso eu remova o arquivo .h depois de possuir o .h.ghc a compilação da erro
informando que não encontrou o arquivo .h,
como se o gcc não estivesse procurando o h.ghc antes do .h (todos os arquivos estão no mesmo diretório).

está técnica é possível em C também?
apenas por curiosidade, visto que esse parece ser um recurso para projetos grandes... oque não é o caso.

creio então que posso assumir que para projetos pequenos em que o tempo de compilação não seja problema,
incluir cabeçalhos comuns (que não serão editados) em um único arquivo não traga efeitos indesejados !?


4. Re: header files [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 10/04/2020 - 10:33h

berghetti escreveu:

não conhecia essa técnica de cabeçalhos pre compilados... bom saber, obrigado!

só achei exemplos de uso para C++ (estou trabalhando em C), creio que como falou, pelo fato de sofrer mais com tempo de compilação,
tentei utilizar aqui apenas para conhecimento e não tive sucesso (usando gcc 7.5.0).

fiz
gcc sys-headers.h 

e ele gerou o arquivo sys-headers.h.ghc

porem não houve diferença no tempo de compilação


Como foi o teste que você fez?

Eu também nunca tinha usado isso no Linux e com C (já tinha usado há muito tempo, porque vinha habilitado por padrão no Borland C++ com MS-DOS e Windows 3.11; rodando num 80386 de 33MHz, e fazia muita diferença).

No Linux eu fiz o seguinte teste, para um programa trivial em C que inclui um arquivo que, por sua vez, inclui uma lista de outros arquivos de cabeçalhos do sistema (eu mostro o comando que usei para gerar esse arquivo; ficou complexo porque havia uma porção de arquivos que era voltada para C++ ou não era válido na minha máquina). Eu compilei o mesmo arquivo duzentas vezes sem PCHs e depois duzentas vezes com PCHs, e depois repeti essas compilações, tendo apagado e recriado o arquivo com PCHs.

$ export LC_ALL=C
$ cat > test.c << EOF
#include "many-headers.h"

int main(void){
return 0;
}
EOF

$ ( cd /usr/include && ls [a-z]*.h sys/[a-z]*.h; cd /usr/include/x86_64-linux-gnu && ls [a-z]*.h sys/[a-z]*.h ) 2>/dev/null | \
egrep -v '^(curs(esapp|es.|slk)|etip|.*cxx.*|db|libio|gdbm|nc_tparm|sss.*|regexp|tic|xf86drm.*|sys/(vm86|elf))\.h' | sort | \
awk 'BEGIN { print "#ifndef many_headers_h__\n#define many_headers_h__\n\n" } { printf("#include <%s>\n", $0) } END { print "\n\n#endif" }' > many-headers.h

$ time for i in {1..200}; do gcc -c test.c; done

real 0m13.105s
user 0m9.518s
sys 0m3.562s
$ gcc many-headers.h
$ time for i in {1..200}; do gcc -c test.c; done

real 0m3.331s
user 0m2.169s
sys 0m1.182s
$ rm many-headers.h.gch
$ time for i in {1..200}; do gcc -c test.c; done

real 0m13.147s
user 0m9.733s
sys 0m3.390s
$ gcc many-headers.h
$ time for i in {1..200}; do gcc -c test.c; done

real 0m3.337s
user 0m2.133s
sys 0m1.224s


Os números mostram que esse cabeçalho que “inclui tudo”, aplicado à compilação de um programa trivial, provoca um determinado tempo de compilação, e que tal tempo cai perto de 75% quando se usam PCHs. (Para referência, a máquina onde eu testei é um servidor Dell T140 com 40GiB de RAM e processador Xeon(R) E-2144G de 3.60GHz, rodando Ubuntu 18.04.)

Claro que exemplo é artificial ao extremo: certamente há mais cabeçalhos em many-headers.h do que qualquer pessoa precisaria numa situação usual (222 na lista gerada na minha máquina) e um programa principal que não faz nada. Mesmo assim, faz sentido a diferença de tempo. Perceba que os cabeçalhos são incluídos pelo preprocessador como se fosse texto do seu programa. Isso implica que cada declaração de função ou variável global, bem como toda definição de macros, constantes, enumerações e tipos compostos, tem de ser interpretada pelo compilador, convertida para uma representação interna, popular a tabela de símbolos, e todos os demais passos envolvidos numa compilação, começando sempre do zero (i.e. a cada compilação, a tabela de símbolos começa essencialmente vazia, apenas os tipos nativos são reconhecidos, apenas as macros que identificam a versão suportada da linguagem e o tipo do compilador estão definidas etc.). Usando PCHs, o compilador pode pular a etapa de compilar aquele texto fixo em C, e carregar diretamente a representação binária que foi produzida anteriormente a partir daquele texto fixo.

e caso eu remova o arquivo .h depois de possuir o .h.ghc a compilação da erro
informando que não encontrou o arquivo .h,
como se o gcc não estivesse procurando o h.ghc antes do .h (todos os arquivos estão no mesmo diretório).


Não consegui reproduzir isso. Tendo só o many-headers.h.pch funcionou para mim.

$ rm many-headers.h
$ time for i in {1..200}; do gcc -c test.c; done

real 0m3.305s
user 0m2.106s
sys 0m1.218s


está técnica é possível em C também?
apenas por curiosidade, visto que esse parece ser um recurso para projetos grandes... oque não é o caso.


Sim, está disponível.

Projetos maiores tornam mais visível. Note que eu compilei duzentas vezes o programa, e houve uma diferença de 13.1 para 3.3 segundos. Se tivesse sido um vez só, seria a comparação de 0.065s contra 0.016s, que continua sendo uma diferença de 75%, mas ambos executam em menos de um décimo de segundo, que é o tempo de apertar uma tecla, e a gente quase não sente — só sente depois de ter digitado um parágrafo inteiro.

Os fatores que fazem ser mais relevante o uso de PCHs são a quantidade de cabeçalhos nele incluídos, a quantidade de código em cada um desses cabeçalhos e sua complexidade (a complexidade do C geralmente não é muito grande, mas a do C++ pode ser bem maior), a quantidade de arquivos que vão usar os PCHs (i.e. tamanho do projeto) e a velocidade do computador que está fazendo a compilação.

creio então que posso assumir que para projetos pequenos em que o tempo de compilação não seja problema,
incluir cabeçalhos comuns (que não serão editados) em um único arquivo não traga efeitos indesejados !?


O grande lance é saber que não existe almoço grátis.

Pelo que eu entendo da questão original que você levantou, você gostaria de ganhar a conveniência de não ter de ficar gerenciando múltiplas diretivas #include em cada arquivo de código fonte em C que você colocar no seu projeto. Em outras palavras, você quer transferir um custo manual de administração para algum outro lugar, o que é perfeitamente compreensível e aceitável. Custos são sempre transferidos, nunca eliminados. Às vezes, a transferência pode ser vantajosa, noutras vezes pode não ser tão benéfica, e frequentemente pode ser vantajosa por alguns aspectos e menos vantajosa por outros.

Ter um arquivo agregador de cabeçalhos pode lhe poupar esforço durante o desenvolvimento, o que é indubitavelmente bom. Contudo, fazer isso também tem os seguintes efeitos:

  • provavelmente agregador terá componentes que não serão usados pela maioria dos seus programas (bloat ou inchaço, que se reflete em tempo de compilação e consumo de memória);

  • se você quiser levar seus programas para outra máquina, terá de levar também o agregador (gestão do código fonte);

  • ao mudar de máquina, o conteúdo do agregador pode não ser mais o mesmo (portabilidade; por exemplo: se você incluir sockets ou TUI ou GUI no seu agregador, Linux e Windows terão diferenças gritantes e incompatibilidades fundamentais entre si);

  • se outra pessoa ler seu programa, pode não entender de cara o que é aquilo, ou pode sentir falta dos cabeçalhos padronizados ou “tradicionais” (legibilidade).

Note que eu não o estou desencorajando a fazer seu agregador, mas apenas mostrando o que ele implica.

Por fim, eu entendo que se você vai fazer um agregador de cabeçalhos, convém habilitar junto com ele PCHs. Se é para ter bloat, que seja só em tamanho (inclusive em disco, que é o custo inerente do PCH), não em tempo.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


5. Re: header files [RESOLVIDO]

Berghetti
berghetti

(usa Debian)

Enviado em 10/04/2020 - 18:02h

Como foi o teste que você fez?


Creio que achei a causa por não conseguir...
meu projeto esta assim:
headers-system.h

process.h - #include "headers-system.h"
process.c - #include "process.h"


após alterar para:
headers-system.h

process.h
process.c - #include "headers-system.h"


tive os resultados:
$ gcc headers-system.h 
$ time for i in {1..100}; do gcc -c process.c;done

real 0m17,963s
user 0m12,815s
sys 0m4,637s
$ rm headers-system.h.gch
$ time for i in {1..100}; do gcc -c process.c;done

real 0m24,860s
user 0m18,233s
sys 0m5,925s



Não consegui reproduzir isso. Tendo só o many-headers.h.pch funcionou para mim.

Mesma solução acima,
process.h estava chamando outra biblioteca que por sua vez também chamava "headers-system.h"...
parece que vou ter que incluir as bibliotecas nos arquivos .c e não nos .h, como estava fazendo...
poderia dar uma palhinha sobre isso? :) forma de organizar o projeto para evitar esses conflitos.

Pelo que eu entendo da questão original que você levantou, você gostaria de ganhar a conveniência de não ter de ficar gerenciando múltiplas diretivas #include em cada arquivo de código fonte em C que você colocar no seu projeto. Em outras palavras, você quer transferir um custo manual de administração para algum outro lugar, o que é perfeitamente compreensível e aceitável. Custos são sempre transferidos, nunca eliminados. Às vezes, a transferência pode ser vantajosa, noutras vezes pode não ser tão benéfica, e frequentemente pode ser vantajosa por alguns aspectos e menos vantajosa por outros.

Ter um arquivo agregador de cabeçalhos pode lhe poupar esforço durante o desenvolvimento, o que é indubitavelmente bom. Contudo, fazer isso também tem os seguintes efeitos:

• provavelmente agregador terá componentes que não serão usados pela maioria dos seus programas (bloat ou inchaço, que se reflete em tempo de compilação e consumo de memória);

• se você quiser levar seus programas para outra máquina, terá de levar também o agregador (gestão do código fonte);

• ao mudar de máquina, o conteúdo do agregador pode não ser mais o mesmo (portabilidade; por exemplo: se você incluir sockets ou TUI ou GUI no seu agregador, Linux e Windows terão diferenças gritantes e incompatibilidades fundamentais entre si);

• se outra pessoa ler seu programa, pode não entender de cara o que é aquilo, ou pode sentir falta dos cabeçalhos padronizados ou “tradicionais” (legibilidade).

Note que eu não o estou desencorajando a fazer seu agregador, mas apenas mostrando o que ele implica.

Por fim, eu entendo que se você vai fazer um agregador de cabeçalhos, convém habilitar junto com ele PCHs. Se é para ter bloat, que seja só em tamanho (inclusive em disco, que é o custo inerente do PCH), não em tempo.


Ficou claro os pontos a avaliar, obrigado.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner
Linux banner
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts