Comment gérez-vous des temps de compilation de plus en plus longs lorsque vous travaillez avec des modèles?

13

J'utilise Visual Studio 2012 et il a des cas où nous avons ajouté des paramètres de modèles à une classe "juste" afin d'introduire un "point de couture" afin que dans le test unitaire nous puissions remplacer ces parties par des objets fictifs.

Comment introduisez-vous habituellement des points de couture en C ++: en utilisant des interfaces et / ou un mélange basé sur certains critères avec des interfaces implicites en utilisant également des paramètres de modèles? Une raison de le demander est également parce que lors de la compilation parfois d'un seul fichier C ++ (qui inclut des fichiers de modèles, qui pourraient également inclure d'autres modèles), un fichier objet est généré qui prend de l'ordre d'environ 5 à 10 secondes sur une machine de développeur .

Le compilateur VS n'est pas non plus particulièrement rapide sur la compilation de modèles pour autant que je sache, et en raison du modèle d'inclusion de modèles (vous incluez pratiquement la définition du modèle dans chaque fichier qui l'utilise indirectement et éventuellement réinstanciez ce modèle chaque fois que vous modifiez quelque chose qui n'a rien à voir avec ce modèle), vous pourriez avoir des problèmes avec les temps de compilation (lors de la compilation incrémentielle).

Quelles sont vos façons de gérer le temps de compilation incrémentiel (et pas seulement) lorsque vous travaillez avec des modèles (en plus d'un compilateur meilleur / plus rapide :-)).

Ghita
la source
1
L'injection de dépendances @RobertHarvey est effectuée à l'aide de paramètres de modèle. Dans le code de production où j'instancie ces derniers, j'ai des temps de compilation lents.
Ghita
5
Utilisez-vous C ++ 11? voir en.wikipedia.org/wiki/C%2B%2B11#Extern_template
mike30
2
Comme Andrei Alexandrescu a écrit "Design C ++ moderne", beaucoup de programmeurs C ++ pensent qu'ils doivent utiliser des modèles pour tout et tout et laisser le compilateur gérer autant que possible. Cela conduit généralement aux effets que vous décrivez. Autrefois (et actuellement toujours pour les programmeurs utilisant d'autres langages), il était absolument acceptable de ne pas utiliser de modèles et de gérer des choses comme l'injection de dépendances avec des mécanismes d'exécution, même lorsque cela nécessite plus de cycles CPU pour l'utilisateur final (ce qu'il ne remarquera presque jamais ). Honnêtement, je suis sûr que Robert a 100% raison et c'est comme ça que vous pensez.
Doc Brown
1
@Ghita: À mon humble avis, l'utilisation de la méta-programmation de modèles n'est souvent qu'une forme d'optimisation prématurée (et parfois simplement excessive) - dans la mesure où vous n'écrivez pas de bibliothèques comme la STL avec des exigences comparables. Vous échangez un gain de performances pour des temps de compilation plus longs, moins de maintenabilité et beaucoup de messages d'erreur difficiles à comprendre. L'utilisation de «modèles externes» peut vous aider maintenant à court terme, mais si j'étais à votre place, je penserais également à des améliorations à long terme.
Doc Brown
4
@DocBrown. À l'inverse, vous pourriez dire qu'éviter les modèles pour améliorer les performances de génération est une optimisation prématurée. Les modèles sont les abstractions idéales pour de nombreux problèmes.
mike30

Réponses:

9

Si vos paramètres de modèles peuvent uniquement supposer un ensemble fini (et petit) de valeurs, vous pouvez déplacer leur définition dans un fichier source et utiliser une instanciation explicite .

Par exemple, aaa.hvous déclarez uniquement les fonctions du modèle fet g:

template <int n>
int f();

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

Supposons que le nparamètre de modèle ne peut être que 1, 3, 6 et que le Tparamètre de modèle ne peut être que int, longet void *.

Ensuite, vous les définissez aaa.cppcomme ceci:

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 cette façon, le compilateur instancie le modèle pour les paramètres donnés lors de la compilation aaa.cpp. Lors de la compilation du code client, il suppose que les définitions existent quelque part et l'éditeur de liens s'en occupera.

#include "aaa.h"

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

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

Vous pouvez également instancier explicitement des classes de modèle. L'inconvénient est que vous ne pouvez pas utiliser fou gavec d'autres paramètres de modèle.

#include "aaa.h"

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

résulte en

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

J'ai utilisé cette technique dans un projet où peu de classes complexes dépendaient d'un petit (<10) ensemble de paramètres de modèle entier, et cela réduisait considérablement le temps de compilation (car le compilateur n'avait pas à analyser les définitions de modèle complexes lors de la compilation du code client) . Bien sûr, vous pouvez obtenir des améliorations moindres, selon le code réel.

Claudio
la source
2

Une fois, j'ai utilisé une solution étrange pour un problème similaire: y compris le plomb STL pour compiler des temps comme plusieurs secondes par fichier source - peu importe sa taille. J'ai donc inclus tous mes fichiers source dans un fichier maître et le temps de compilation par fichier n'a guère changé ... ce qui signifiait une accélération du facteur 20+ car je n'avais qu'un seul fichier à compiler.

Afin de garder le design propre, j'ai continué à maintenir un makefile, mais je ne l'ai jamais utilisé (sauf pour vérifier qu'il fonctionne toujours).

maaartinus
la source
0

Nous avions l'habitude de lancer une grosse tâche pour construire nos en-têtes précompilés et nos modèles précompilés du jour au lendemain, et simplement construire contre ceux du lendemain.

Ti Strga
la source