Mantenga las optimizaciones locales, hágalas obvias, documentelas bien y facilite la comparación de las versiones optimizadas entre sí y con la versión no optimizada, tanto en términos de código fuente como de rendimiento en tiempo de ejecución.
Respuesta completa
Si tales optimizaciones son realmente importantes para su producto, entonces necesita saber no solo por qué las optimizaciones fueron útiles antes, sino también proporcionar suficiente información para ayudar a los desarrolladores a saber si serán útiles en el futuro.
Idealmente, debe incorporar las pruebas de rendimiento en su proceso de compilación, para saber cuándo las nuevas tecnologías invalidan las optimizaciones antiguas.
Recuerda:
La primera regla de optimización del programa: no lo hagas.
La segunda regla de optimización de programas (¡solo para expertos!): No lo hagas todavía ".
- Michael A. Jackson
Para saber si ahora es el momento, se requiere evaluación comparativa y pruebas.
Como mencionó, el mayor problema con el código altamente optimizado es que es difícil de mantener, por lo que, en la medida de lo posible, debe mantener las porciones optimizadas separadas de las porciones no optimizadas. Ya sea que haga esto mediante la vinculación en tiempo de compilación, las llamadas a funciones virtuales en tiempo de ejecución o algo intermedio no debería importar. Lo que debería importar es que cuando ejecuta sus pruebas, desea poder probar todas las versiones en las que está interesado actualmente.
Me inclinaría a construir un sistema de tal manera que la versión básica no optimizada del código de producción siempre pudiera usarse para comprender la intención del código, luego construir diferentes módulos optimizados junto con este que contenga la versión o versiones optimizadas, documentando explícitamente donde sea La versión optimizada difiere de la línea base. Cuando ejecuta sus pruebas (unidad e integración), lo ejecuta en la versión no optimizada y en todos los módulos optimizados actuales.
Ejemplo
Por ejemplo, supongamos que tiene una función de Transformación rápida de Fourier . Quizás tengas una implementación algorítmica básica fft.cy pruebas en fft_tests.c.
Luego viene el Pentium y usted decide implementar la versión de punto fijo al fft_mmx.cusar las instrucciones MMX . Más tarde, el Pentium 3 llega y decide añadir una versión que utiliza las extensiones Streaming SIMD en fft_sse.c.
Ahora desea agregar CUDA , así que agrega fft_cuda.c, pero descubra que con el conjunto de datos de prueba que ha estado utilizando durante años, ¡la versión CUDA es más lenta que la versión SSE! Hace un análisis y termina agregando un conjunto de datos que es 100 veces más grande y obtiene la aceleración que espera, pero ahora sabe que el tiempo de configuración para usar la versión CUDA es significativo y que con conjuntos de datos pequeños debe usar un algoritmo sin ese costo de instalación.
En cada uno de estos casos, está implementando el mismo algoritmo, todos deberían comportarse de la misma manera, pero funcionarán con diferentes eficiencias y velocidades en diferentes arquitecturas (si es que funcionan). Sin embargo, desde el punto de vista del código, puede comparar cualquier par de archivos de origen para averiguar por qué la misma interfaz se implementa de diferentes maneras y, por lo general, la forma más fácil será volver a consultar la versión original no optimizada.
Lo mismo ocurre con una implementación OOP donde una clase base que implementa el algoritmo no optimizado y las clases derivadas implementan diferentes optimizaciones.
Lo importante es mantener las mismas cosas que son iguales , para que las diferencias sean obvias .