Quelle est la meilleure approche pour coder dans un environnement de compilation lente

15

J'avais l'habitude de coder en C # dans un style TDD - écrire / ou changer un petit morceau de code, recompiler en 10 secondes toute la solution, relancer les tests et encore. Facile...

Cette méthodologie de développement a très bien fonctionné pour moi pendant quelques années, jusqu'à l'année dernière où j'ai dû revenir au codage C ++ et j'ai vraiment l'impression que ma productivité a considérablement diminué depuis. Le C ++ en tant que langage n'est pas un problème - j'ai eu beaucoup d'expérience pour les développeurs C ++ ... mais dans le passé.

Ma productivité est toujours correcte pour de petits projets, mais elle empire quand avec l'augmentation de la taille du projet et une fois que le temps de compilation atteint 10+ minutes, cela devient vraiment mauvais. Et si je trouve l'erreur, je dois recommencer la compilation, etc. C'est tout simplement frustrant.

J'ai donc conclu que dans de petits morceaux (comme avant) n'est pas acceptable - toutes les recommandations comment puis-je me mettre dans la vieille habitude de coder pendant environ une heure, lors de la révision manuelle du code (sans compter sur un compilateur C # rapide) et ne recompile / réexécute les tests unitaires qu'une fois toutes les deux heures.

Avec un C # et TDD, il était très facile d'écrire un code de manière évolutive - après une douzaine d'itérations, quelle que soit la merde avec laquelle je commençais, je finissais par un bon code, mais cela ne fonctionne plus pour moi (dans une compilation lente environnement).

moucheron
la source
stackoverflow.com/questions/5078409/… Ceux-ci devraient probablement être fusionnés / en un seul endroit.
Ben L
Voir laso stackoverflow.com/questions/373142/… pour accélérer les temps de compilation C ++.
Eclipse
2
Commencez un combat à l'épée . ; p
Steven Jeuris
La dernière fois que j'ai utilisé C ++ sérieusement, en utilisant des en-têtes précompilés, le temps de compilation a été réduit d'un facteur quatre.
gnasher729

Réponses:

16

Plusieurs choses me viennent à l'esprit:

  1. Utilisez la compilation distribuée . Vous pouvez le faire avec GCC ("distCC"?) Ou VC ( IncrediBuild de Xoreax n'est pas exactement bon marché, mais vaut chaque centime dépensé.).

  2. Divisez votre projet en bibliothèques chargées dynamiquement et essayez soigneusement de minimiser les dépendances à leur sujet. Les petits exécutables se lient beaucoup plus rapidement.

  3. Programmez contre de petits projets de test plutôt que contre toute la grande application.

  4. Utilisez la programmation par méta-modèle pour exécuter des algorithmes au moment de la compilation . Oui, cela augmentera en fait les temps de compilation, mais cela réduira également les délais nécessaires pour les tests: si la compilation est correcte, c'est fait.

  5. Investissez dans du matériel . Plus de noyaux de CPU (dans votre machine ou dans d'autres) feront merveille avec la compilation distribuée, et beaucoup de mémoire plus un disque rapide (SSD au lieu de HDD) vous aideront beaucoup. Si vous avez un système 64 bits et des quantités obscènes de RAM, la compilation sur un disque RAM peut fournir une augmentation de vitesse incroyable.

sbi
la source
1
d'après mon expérience, les caches de compilation sont en fait une meilleure idée que la compilation distribuée.
pqnet
En parlant de disque RAM par rapport à SSD, j'ai été assez surpris que cela n'apporte pas autant d'augmentation de la vitesse de compilation. Mon projet actuel (Java sur Android) se compile à partir d'un état propre en ~ 45 secondes à partir du SSD et en ~ 40 secondes à partir du disque RAM (et l'ensemble de la chaîne d'outils est sur le disque RAM, pas seulement les sources). Pas une augmentation spectaculaire, je dirais.
Haspemulator
10

Une autre solution technique non encore mentionnée par d'autres est le passage aux disques SSD au lieu des disques durs standard. Dans un projet précédent sur lequel j'ai travaillé, les SSD ont réduit les temps de construction de 30 minutes à 3.

Bien sûr, ils sont coûteux. Pour votre patron, calculez le prix du temps perdu du développeur par rapport au prix de l'investissement ponctuel. L'investissement est probablement rentabilisé en quelques mois.

Péter Török
la source
6
C'est intéressant. Cela signifie que 90% du temps de construction est une latence de disque d'E / S.
Mike Dunlavey
Je n'ai pas vu une diminution de 90%, mais substantielle. Cela ne semble pas accélérer la compilation, mais cela accélère définitivement la liaison. Si vous apportez de petites modifications à un grand projet (il n'y a donc pas beaucoup de compilation dans une modification) et que vous effectuez une nouvelle liaison, vous pourriez très bien l'obtenir. (Il s'agit de Visual Studio 2008, utilisant C ++.)
David Thornley
1
C'est une bonne idée. En outre, le montage d'une partie de la RAM sur votre système de fichiers fonctionnerait rapidement et c'est bon marché.
Goran Jovic
2
Ramdisk est encore plus rapide (et moins cher).
SK-logic
1
@John: Oui, un compilateur bien écrit devrait (à mon humble avis) être lié aux E / S.
Mike Dunlavey
3

Plus de planification, codez en plus gros morceaux, écrivez des tests d'intégration au lieu de tests unitaires et exécutez la suite de tests build + pendant la nuit.

Nemanja Trifunovic
la source
3

Les longs temps de compilation sont parfois un problème, mais la modularisation déjà mentionnée peut aider à surmonter cela (principalement).

Beaucoup plus grave est coincé dans un environnement où vous ne pouvez pas du tout compiler, où chaque changement de code doit être soumis à un autre département sur un autre continent pour être appliqué à l'environnement de test / développement, un processus qui peut prendre des jours.

Je travaille maintenant dans un tel environnement, et ce système m'a déjà coûté plus d'une semaine (et le projet n'a qu'un budget de 4 semaines au total avant que l'argent ne soit épuisé) juste pour obtenir la version initiale de nos modifications. (puis ils ont fait des erreurs qui ont empêché une partie des fichiers d'être récupérés par le serveur d'applications, nous examinons donc plusieurs jours de retard). Chaque modification mineure maintenant (disons que nous trouvons quelque chose dans les tests qui doit être corrigé, comme une condition d'erreur manquée) peut entraîner un retard d'un autre jour ou plus.

Dans de telles conditions, vous essayez de vous assurer qu'il n'y a aucune erreur avant même d'essayer de compiler votre code. J'ai l'impression de revenir à la programmation mainframe, où nous avions 5 minutes de temps CPU par mois disponibles pour tous les travaux de compilation et de test.

jwenting
la source
1
C'est une situation d'enfer. Je me souviens des jours du mainframe. Nous avons fait beaucoup de "vérification de bureau" du code. C'est incroyable de voir combien de choses ont été faites de cette façon, comme des simulations sans fin de vols vers la lune.
Mike Dunlavey
3

Je me souviens facilement quand les builds prenaient du temps. Quelques approches atténuantes:

  • Construisez le système en combinant des bibliothèques ou des DLL. De cette façon, lorsque vous modifiez du code, la seule partie qui doit être recompilée est votre partie.
  • Le nombre de points dans le code que vous devez modifier pour implémenter une fonctionnalité affecte non seulement la quantité de modifications que vous avez à faire, mais la fréquence à laquelle vous mettez des bogues, amplifiant la boucle de compilation-débogage-modification-compilation. Tout ce qui réduit la redondance du code, comme DRY, aide.
  • Si vous êtes dans le débogueur et que vous pouvez modifier, recompiler et continuer sans quitter le débogueur, cela est vraiment utile.
Mike Dunlavey
la source
2

10+ minutes pour une compilation? Sérieusement?

Utilisez-vous un IDE qui fait la construction incrémentielle (par exemple Eclipse)? Sinon, vous devriez probablement l'être, il fera la compilation de base en quelques secondes plutôt qu'en quelques minutes.

Ou parlez-vous de choses d'intégration, où vous devez créer l'intégralité de l'application pour tester votre changement? Si c'est le cas, regardez des tests plus petits pour vous assurer que les principaux bogues sont hors de votre code avant d'avoir à faire la construction complète.

TrueDub
la source
5
10 minutes est petit. J'ai travaillé pour un projet qui a pris une heure pour compiler et lier à partir de zéro sur une machine monocœur, des PCH et tout. Bien sûr, à l'exception des versions automatiques, personne ne le construirait sur un seul cœur de processeur, mais quand même ... Si vous deviez changer quelque chose dans un en-tête qui est inclus un peu partout (manipulation de chaînes, gestion des erreurs), vous pourriez devenir fou.
sbi
2
Utilisé (il y a des années) sur un système qui prenait, pour une version complète, 48 heures à compiler. Bien sûr, une construction complète n'a été commencée que vendredi soir, avec un peu de chance, lorsque nous serons revenus au bureau lundi. Nous avons plutôt construit de petits modules selon les besoins (disons une seule DLL).
jwenting le
2
-1 à tous les commentaires ci-dessus si je pouvais +1 à TrueDub. Oui, si vous recompilez tout, cela peut prendre très longtemps. Cependant, si la gestion des dépendances est envisagée, des projets de recompilation entiers de 10 heures peuvent avoir des recompilations incrémentielles en moins d'une minute, ce qui est la norme. Vous devriez tous avoir honte de perdre du temps à vos employeurs à attendre vos recompilations lorsque l'application d'un peu d'intelligence vous fera gagner beaucoup de temps.
Dunk
2
Et si vous travaillez dans une petite boutique qui se trouve sur un projet à plusieurs MLoC, cela signifie qu'une partie considérable du code est ancienne, lancée il y a une décennie comme plusieurs petits projets, où la vitesse de compilation n'a jamais été un problème, et la gestion des dépendances est abominablement mauvaise. Alors allez-vous dire à une telle entreprise de jeter tout cela et de passer une autre décennie à le réécrire?
sbi
2
@Dunk: C'était une petite entreprise, avec moins d'une douzaine de développeurs travaillant sur un projet multi-MLoC. C'est très différent de centaines de développeurs faisant, vous ne pouvez pas ré-écrire quoi que ce soit à partir de zéro, parce que vous aurez besoin des années pour le faire. Autant que je détestais l'idée, investir dans la compilation distribuée était économiquement faisable, mais pas la réécriture. Oh, et j'ai hérité de ces interfaces. :-xJe n'étais pas là il y a dix ans, quand ils ont été imaginés. (J'ai changé beaucoup de ce code pour utiliser TMP, pour trouver plus d'erreurs lors de la compilation, et moins sur le terrain.)
sbi
2

D'abord, pourquoi faut-il autant de temps pour compiler en premier lieu?

  • Votre environnement (IDE, make, peu importe) prend-il en charge les builds incrémentiels? Assurez-vous que vous recompilez uniquement les modifications, plutôt que le tout.
  • Si vous avez une machine multicœur, votre IDE peut prendre en charge la compilation parallèle. Je sais pertinemment que Visual Studio fait cela. Apparemment, gcc aussi. Obtenez donc une meilleure machine et activez la compilation parallèle.
  • Pensez à utiliser des en-têtes précompilés.
  • Si vous essayez tout cela et que la compilation est encore lente, passez en revue votre code. Recherchez les dépendances inutiles. Incluez-vous un en-tête où une déclaration à terme serait suffisante? Pensez à utiliser l'idiome PIMPL pour réduire la dépendance aux en-têtes.

Si après tout cela, votre temps de construction est encore lent, décomposez le problème: créez de nombreux petits projets de test et travaillez sur chacun individuellement. Assurez-vous que vous disposez d'un système de construction nocturne automatisé qui effectue une nouvelle vérification, construit tout et exécute automatiquement tous les tests unitaires.

Enfin, si cela vous prend encore beaucoup de temps pour tester vos modifications, réfléchissez-y davantage. Assurez-vous de faire une différence dans votre système de contrôle de version et examinez attentivement toutes les modifications avant de tester. En bref, cela ressemble beaucoup au développement de systèmes embarqués, où le délai d'exécution d'un test est long et votre capacité à examiner l'état du système est limitée.

Cela m'amène à une autre pensée: instrumentez votre code pour utiliser la journalisation. De cette façon, vous pourrez voir quel est le problème sans reconstruire et relancer une douzaine de fois.

Dima
la source
2
Je ne sais pas si GCC prend en charge les compilations parallèles autant que le fait que make ou des outils similaires démarrent plusieurs copies de GCC si vous le dites aussi.
Zachary K
1
+1 pour PIMPL. J'ai travaillé sur un projet où les temps de construction sont devenus incontrôlables. Dans ce projet, je ne me suis pas soucié du nombre d'autres en-têtes inclus dans chaque en-tête. Lors de mon prochain projet, je me suis efforcé de minimiser cela en utilisant largement PIMPL. Les temps de construction continuent d'être excellents même si le deuxième projet est probablement deux fois plus grand que le premier.
Jason B
1

Vous avez probablement besoin d'une approche à plusieurs volets:

1) Systèmes de construction plus rapides. Autant de cœurs / ram / disque rapide que vous pouvez vous le permettre. Pour les projets C ++ plus importants, vous constaterez que le disque est souvent un limiteur, alors assurez-vous d'en avoir de rapides.

2) Plus de modularisation du projet. Décomposez les choses afin que les changements ne puissent pas facilement provoquer une recompilation complète de tout. Franchement, poussez autant de choses de base que possible dans des fichiers dll / so séparés afin qu'une partie du projet puisse être complètement séparée du reste.

3) Builds incrémentiels / builds distribués / mise en cache selon votre environnement. Sur certains systèmes, distcc (bâtiment distribué) et ccache (mise en cache de choses partiellement construites) peuvent économiser beaucoup de temps de compilation.

4) Assurez-vous que votre build peut être bien parallélisé. Dans un environnement de makefile en particulier, il n'est pas difficile de se retrouver dans une situation où vous avez accidentellement configuré les Makefiles de telle sorte que vous ne pouvez pas faire de construction parallèle.

Michael Kohne
la source
0

La journalisation étendue et la validation interne ont été utiles pour les longs délais d'exécution. Une fois votre génération terminée, une seule exécution peut révéler un grand nombre de problèmes possibles à la fois.

Lorsqu'il s'agit d'algorithmes ou de comptabilité plutôt complexes, il peut être utile d'inclure une version très simplifiée en parallèle avec la «vraie». Dans toute exécution, vous disposez de données de référence utiles.

Joris Geer
la source
0

Ce que @sbi et @Michael Kohne ont dit.

Consacrez du temps et de l'énergie au processus de construction lui-même. Il était une fois un produit majestueux et mature qui a pris plus d'une heure pour une construction complète. Beaucoup de temps et d'énergie ont été consacrés à réparer ce que les dépendances de construction prétendaient être, puis à réparer / réduire ce qu'elles étaient réellement. Le temps de construction est tombé à environ 30 minutes.

Changer les outils de construction l'a laissé tomber davantage. Pour un projet en plusieurs parties, 'scons' peut faire toutes les compilations avant de faire des liens. 'make' à l'aide de plusieurs makefiles effectue la compilation d'un seul projet avant les liens de ce projet, puis passe à autre chose.

Cela nous a amenés au point que toutes les commandes de compilation individuelles pouvaient être exécutées massivement en parallèle. 'distcc' sur les machines lentes, make / scons -j8 sur les machines multicœurs. Cela a réduit les versions complètes à quelques minutes.

Sous un autre jour, créez un processus de construction nocturne automatisé. De cette façon, si quelque chose de problématique est commis dans votre référentiel source, la première personne à arriver au travail, à voir et à résoudre le problème, peut empêcher plusieurs personnes de (re) faire plusieurs builds en échec.

Rick Berge
la source