Mantenha as otimizações locais, torne-as óbvias, documente-as bem e facilite a comparação das versões otimizadas entre si e com a versão não otimizada, tanto em termos de código fonte quanto de desempenho em tempo de execução.
Resposta completa
Se essas otimizações realmente são importantes para o seu produto, você precisa saber não apenas por que as otimizações foram úteis antes, mas também fornecer informações suficientes para ajudar os desenvolvedores a saber se serão úteis no futuro.
Idealmente, você precisa consagrar o teste de desempenho ao seu processo de construção, para descobrir quando as novas tecnologias invalidam as otimizações antigas.
Lembrar:
A primeira regra de otimização de programa: não faça isso.
A Segunda Regra da Otimização de Programas (somente para especialistas!): Não faça isso ainda. "
- Michael A. Jackson
Para saber se agora é a hora, é necessário fazer comparações e testes.
Como você mencionou, o maior problema com código altamente otimizado é que é difícil manter, portanto, na medida do possível, é necessário manter as partes otimizadas separadas das partes não otimizadas. Se você faz isso por meio de vínculos em tempo de compilação, chamadas de função virtual em tempo de execução ou algo intermediário, não importa. O que importa é que, quando você executa seus testes, deseja poder testar todas as versões em que está interessado atualmente.
Eu estaria inclinado a construir um sistema de tal maneira que a versão básica não otimizada do código de produção sempre pudesse ser usada para entender a intenção do código e, em seguida, criar diferentes módulos otimizados ao lado deste contendo a versão ou versões otimizadas, documentando explicitamente onde quer que a versão otimizada difere da linha de base. Ao executar seus testes (unidade e integração), você o executa na versão não otimizada e em todos os módulos otimizados atuais.
Exemplo
Por exemplo, digamos que você tenha uma função Fast Fourier Transform . Talvez você tenha uma implementação algorítmica básica fft.c
e faça o teste fft_tests.c
.
Aí vem o Pentium e você decide implementar a versão de ponto fixo fft_mmx.c
usando as instruções da MMX . Mais tarde, o pentium 3 vem e você decidir adicionar uma versão que usa Streaming SIMD Extensions no fft_sse.c
.
Agora você deseja adicionar o CUDA , fft_cuda.c
mas também com o conjunto de dados de teste usado há anos, a versão CUDA é mais lenta que a versão SSE! Você faz algumas análises e acaba adicionando um conjunto de dados 100 vezes maior e obtém a velocidade esperada, mas agora sabe que o tempo de configuração para usar a versão CUDA é significativo e que, com pequenos conjuntos de dados, você deve usar um algoritmo sem esse custo de instalação.
Em cada um desses casos, você está implementando o mesmo algoritmo, todos devem se comportar da mesma maneira, mas serão executados com diferentes eficiências e velocidades em arquiteturas diferentes (se for que serão executadas). Porém, do ponto de vista do código, você pode comparar qualquer par de arquivos de origem para descobrir por que a mesma interface é implementada de maneiras diferentes e, geralmente, a maneira mais fácil será consultar a versão original não otimizada.
O mesmo vale para uma implementação OOP em que uma classe base que implementa o algoritmo não otimizado e classes derivadas implementam otimizações diferentes.
O importante é manter as mesmas coisas que são as mesmas , de modo que as diferenças são óbvias .