Em alguns ambientes, a compilação será mais rápida se incluirmos apenas os arquivos de cabeçalho necessários. Em outros ambientes, a compilação será otimizada se todos os arquivos de origem puderem usar a mesma coleção primária de cabeçalhos (alguns arquivos podem ter cabeçalhos adicionais além do subconjunto comum). O ideal é que os cabeçalhos sejam construídos de forma que várias operações #include não tenham efeito. Pode ser bom cercar as instruções #include com verificações para a proteção de inclusão do arquivo a ser incluído, embora isso crie uma dependência do formato dessa proteção. Além disso, dependendo do comportamento de cache de arquivos do sistema, um #include desnecessário cujo alvo acaba sendo completamente # definido pode não demorar muito.
Outra coisa a considerar é que se uma função leva um ponteiro para uma estrutura, pode-se escrever o protótipo como
void foo (struct BAR_s * bar);
sem uma definição para BAR_s tendo que estar no escopo. Uma abordagem muito útil para evitar inclusões desnecessárias.
PS - em muitos de meus projetos, haverá um arquivo que se espera que cada módulo inclua #include, contendo coisas como typedefs para tamanhos inteiros e algumas estruturas e uniões comuns [por exemplo
typedef union {
sem sinal longo l;
curto sem sinal lw [2];
char lb sem sinal [4];
} U_QUAD;
(Sim, eu sei que teria problemas se mudasse para uma arquitetura big-endian, mas como meu compilador não permite estruturas anônimas em uniões, usar identificadores nomeados para os bytes dentro da união exigiria que eles fossem acessados como theUnion.b.b1 etc. o que parece bastante irritante.