¿Cómo maneja tiempos de compilación cada vez más largos cuando trabaja con plantillas?

13

Utilizo Visual Studio 2012 y él tiene casos en los que agregamos parámetros de plantillas a una clase "solo" para introducir un "punto de costura" para que en la prueba unitaria podamos reemplazar esas partes con objetos simulados.

¿Cómo suele introducir puntos de costura en C ++: usando interfaces y / o mezclas basadas en algunos criterios con interfaces implícitas usando también parámetros de plantillas? Una razón para preguntar esto también es porque al compilar a veces un solo archivo C ++ (que incluye archivos de plantillas, que también podría incluir otras plantillas) da como resultado que se genere un archivo objeto que demora entre 5 y 10 segundos en un equipo desarrollador .

Por lo que yo entiendo, el compilador de VS tampoco es particularmente rápido en la compilación de plantillas, y debido al modelo de inclusión de plantillas (prácticamente incluye la definición de la plantilla en cada archivo que la usa indirectamente y posiblemente vuelva a instanciar esa plantilla cada vez que modifique algo que no tiene nada que ver con esa plantilla) podría tener problemas con los tiempos de compilación (al hacer una compilación incremental).

¿Cuáles son sus formas de manejar el tiempo de compilación incremental (y no solo) cuando trabaja con plantillas (además de un compilador mejor / más rápido :-)).

Ghita
fuente
1
La inyección de dependencia de @RobertHarvey se realiza utilizando parámetros de plantilla. En el código de producción donde instancia estos, tengo tiempos de compilación lentos.
Ghita
55
¿Estás usando C ++ 11? ver en.wikipedia.org/wiki/C%2B%2B11#Extern_template
mike30
2
Como Andrei Alexandrescu ha escrito "Diseño moderno de C ++", muchos programadores de C ++ piensan que deben usar plantillas para todo y dejar que el compilador maneje lo más posible. Eso generalmente conduce a los efectos que está describiendo. Anteriormente (y actualmente todavía para programadores que usan otros lenguajes), estaba absolutamente bien no usar plantillas y manejar cosas como la inyección de dependencia con mecánica de tiempo de ejecución, incluso cuando esto necesita algunos ciclos de CPU más para el usuario final (que casi nunca notará ) Honestamente, estoy seguro de que Robert es 100% correcto y esa es la forma en que piensas al respecto.
Doc Brown
1
@Ghita: en mi humilde opinión, el uso de meta programación de plantillas a menudo es solo una forma de optimización prematura (y a veces simplemente exagerada), siempre y cuando no escribas libs como STL con requisitos comparables. Cambia un poco de ganancia de rendimiento por tiempos de compilación más grandes, menos mantenimiento y muchos mensajes de error difíciles de entender. El uso de "plantillas externas" puede ayudarlo ahora a corto plazo, pero si estuviera en su lugar, también pensaría en mejoras a largo plazo.
Doc Brown
44
@DocBrown. Por el contrario, se podría decir que evitar plantillas para mejorar el rendimiento de la compilación es una optimización prematura. Las plantillas son las abstracciones ideales para muchos problemas.
mike30

Respuestas:

9

Si los parámetros de sus plantillas solo pueden asumir un conjunto finito (y pequeño) de valores, puede mover su definición en un archivo fuente y usar una instanciación explícita .

Por ejemplo, en aaa.husted solo declara las funciones de la plantilla fy g:

template <int n>
int f();

template <class T>
void g(int a);

Suponga que el nparámetro de plantilla solo puede ser 1, 3, 6 y el Tparámetro de plantilla solo puede ser int, longy void *.

Luego los define aaa.cppasí:

template <int n>
int f()
{
    ...
}

template <class T>
void g(int a)
{
    ...
}

template int f<1>();
template int f<3>();
template int f<6>();

template void g<int>(int a);
template void g<long>(int a);
template void g<void *>(int a);

De esta manera, el compilador crea una instancia de la plantilla para los parámetros dados al compilar aaa.cpp. Al compilar el código del cliente, se supone que las definiciones existen en algún lugar, y el enlazador se encargará de eso.

#include "aaa.h"

int main()
{
    f<1>();
    f<3>();
    f<6>();

    g<int>(5);
    g<long>(5);
    g<void *>(5);
}

También puede crear instancias de clases explícitamente. El inconveniente es que no puede usar fo gcon otros parámetros de plantilla.

#include "aaa.h"

int main()
{
    f<5>();
}

resultados en

undefined reference to `int f<5>()'

Utilicé esta técnica en un proyecto donde pocas clases complejas dependían de un conjunto pequeño (<10) de parámetros de plantilla entera, y redujo significativamente el tiempo de compilación (ya que el compilador no tenía que analizar las definiciones de plantilla complejas al compilar el código del cliente) . Por supuesto, puede obtener mejoras menores, dependiendo del código real.

Claudio
fuente
2

Una vez utilicé una solución extraña para un problema similar: incluir el STL condujo a tiempos de compilación como varios segundos por archivo fuente, sin importar cuán pequeño fuera. Así que incluí todos mis archivos fuente en un archivo maestro y el tiempo de compilación por archivo apenas cambió ... lo que significó una aceleración del factor 20+ ya que solo tenía que compilar un solo archivo.

Para mantener limpio el diseño, seguí manteniendo un archivo MAKE, pero nunca lo usé (excepto para verificar que todavía funciona).

maaartinus
fuente
0

Solíamos hacer una gran tarea para construir nuestros encabezados precompilados y plantillas precompiladas durante la noche, y solo compilamos contra esos al día siguiente.

Ti Strga
fuente