Parfois, il y a 1% de code suffisamment intensif en calcul qui nécessite le type d'optimisation de bas niveau le plus lourd. Les exemples sont le traitement vidéo, le traitement d'image et toutes sortes de traitement du signal, en général.
Les objectifs sont de documenter et d'enseigner les techniques d'optimisation, afin que le code ne devienne pas impossible à maintenir et sujette à suppression par les nouveaux développeurs. (*)
(*) Nonobstant la possibilité que l'optimisation particulière soit complètement inutile dans certains futurs CPU imprévisibles, de sorte que le code sera supprimé de toute façon.
Étant donné que les offres logicielles (commerciales ou open source) conservent leur avantage concurrentiel en ayant le code le plus rapide et en utilisant la toute dernière architecture CPU, les rédacteurs de logiciels ont souvent besoin de modifier leur code pour le faire fonctionner plus rapidement tout en obtenant la même sortie pendant un certain temps. tâche, liste tolérant une petite quantité d’erreurs d’arrondi.
En règle générale, un rédacteur de logiciels peut conserver de nombreuses versions d'une fonction en tant que documentation de chaque réécriture d'optimisation / algorithme qui a lieu. Comment mettre ces versions à disposition des autres pour étudier leurs techniques d'optimisation?
En relation:
Réponses:
Réponse courte
Gardez les optimisations locales, rendez-les évidentes, documentez-les bien et simplifiez la comparaison des versions optimisées entre elles et avec la version non optimisée, à la fois en termes de code source et de performances d'exécution.
Réponse complète
Si de telles optimisations sont vraiment importantes pour votre produit, vous devez non seulement savoir pourquoi les optimisations étaient utiles auparavant, mais également fournir suffisamment d'informations pour aider les développeurs à savoir si elles seront utiles à l'avenir.
Idéalement, vous devez inscrire les tests de performances dans votre processus de génération afin de savoir quand les nouvelles technologies invalident les anciennes optimisations.
Rappelles toi:
Afin de savoir si le moment est venu, il faut des analyses comparatives et des tests.
Comme vous le mentionnez, le plus gros problème avec le code hautement optimisé est qu'il est difficile à maintenir, donc, dans la mesure du possible, vous devez garder les portions optimisées séparées des portions non optimisées. Que vous le fassiez via la liaison au moment de la compilation, les appels de fonctions virtuelles d'exécution ou quelque chose entre les deux, cela n'a pas d'importance. Ce qui devrait être important, c'est que lorsque vous exécutez vos tests, vous souhaitez pouvoir tester toutes les versions qui vous intéressent actuellement.
Je serais enclin à construire un système de telle manière que la version de base non optimisée du code de production puisse toujours être utilisée pour comprendre l' intention du code, puis à construire différents modules optimisés à côté de celui-ci contenant la ou les versions optimisées, documentant explicitement partout la version optimisée diffère de la ligne de base. Lorsque vous exécutez vos tests (unitaires et d'intégration), vous les exécutez sur la version non optimisée et sur tous les modules optimisés actuels.
Exemple
Par exemple, supposons que vous ayez une fonction de transformation de Fourier rapide . Peut-être que vous avez une implémentation algorithmique de base
fft.c
et des testsfft_tests.c
.Vient ensuite le Pentium et vous décidez d'implémenter une version à virgule fixe en
fft_mmx.c
utilisant les instructions MMX . Plus tard, le pentium 3 arrive et vous décidez d'ajouter une version qui utilise les extensions Streaming SIMD dansfft_sse.c
.Maintenant, vous voulez ajouter CUDA , donc vous ajoutez
fft_cuda.c
, mais trouvez qu'avec le jeu de données de test que vous utilisez depuis des années, la version CUDA est plus lente que la version SSE! Vous faites une analyse et finissez par ajouter un jeu de données 100 fois plus grand et vous obtenez la vitesse que vous attendez, mais maintenant vous savez que le temps de configuration pour utiliser la version CUDA est important et qu'avec de petits jeux de données, vous devriez utiliser un algorithme sans ce coût d'installation.Dans chacun de ces cas, vous implémentez le même algorithme, tous doivent se comporter de la même manière, mais s'exécuteront avec des efficacités et des vitesses différentes sur différentes architectures (si elles s'exécutent). Du point de vue du code cependant, vous pouvez comparer n'importe quelle paire de fichiers source pour savoir pourquoi la même interface est implémentée de différentes manières et généralement, le plus simple sera de se référer à la version originale non optimisée.
Il en va de même pour une implémentation OOP où une classe de base qui implémente l'algorithme non optimisé et des classes dérivées implémentent différentes optimisations.
L'important est de garder les mêmes choses qui sont les mêmes , pour que les différences soient évidentes .
la source
Plus précisément, puisque vous avez pris l'exemple du traitement vidéo et image, on peut conserver le code dans la même version mais actif ou inactif selon le contexte.
Bien que vous ne l'ayez pas mentionné, je suppose
C
ici.La manière la plus simple dans le
C
code, on fait l'optimisation (et s'applique également lorsque l'on essaie de rendre les choses portables) est de garderLorsque vous activez
#define OPTIMIZATION_XYZ_ENABLE
pendant la compilation dans Makefile, tout fonctionne en conséquence.Habituellement, couper quelques lignes de code au milieu des fonctions peut devenir compliqué quand il y a trop de fonctions optimisées. Par conséquent, dans ce cas, on définit différents pointeurs de fonction pour effectuer une fonction spécifique.
le code principal s'exécute toujours via un pointeur de fonction comme
Mais les pointeurs de fonction sont définis en fonction du type d'exemple (par exemple ici la fonction idct est optimisée pour différentes architectures CPU.
vous devriez voir le code libjpeg et le code libmpeg2 et vous pouvez être ffmpeg pour de telles techniques.
la source
En tant que chercheur, je finis par écrire un peu du code "goulot d'étranglement". Cependant, une fois mis en production, il incombe aux développeurs de l'intégrer dans le produit et de fournir un support ultérieur. Comme vous pouvez l'imaginer, il est de la plus haute importance de communiquer clairement quoi et comment le programme est censé fonctionner.
J'ai trouvé qu'il y a trois ingrédients essentiels pour réussir cette étape
Pour la première étape, j'écris toujours un petit livre blanc qui documente l'algorithme. Le but ici est de le rédiger afin qu'une autre personne puisse l'implémenter à partir de zéro en utilisant uniquement le livre blanc. S'il s'agit d'un algorithme bien connu et publié, il suffit de donner les références et de répéter les équations clés. S'il s'agit d'un travail original, vous devrez être un peu plus explicite. Cela vous dira ce que le code est censé faire .
L'implémentation réelle qui est transférée au développement doit être documentée de manière à ce que toutes les subtilités soient rendues explicites. Si vous acquérez des verrous dans un ordre particulier pour éviter un blocage, ajoutez un commentaire. Si vous parcourez les colonnes plutôt que les lignes d'une matrice en raison de problèmes de cohérence du cache, ajoutez un commentaire. Si vous faites quelque chose, même légèrement intelligent, commentez-le. Si vous pouvez garantir le livre blanc et que le code ne sera jamais séparé (via VCS ou un système similaire), vous pouvez vous référer au livre blanc. Le résultat peut facilement être supérieur à 50% de commentaires. Ça va. Cela vous dira pourquoi le code fait ce qu'il fait.
Enfin, vous devez être en mesure de garantir l'exactitude face aux changements. Heureusement, nous sommes un outil pratique dans les plates-formes de tests automatisés et d'intégration continue . Ceux-ci vous diront ce que le code fait réellement .
Ma recommandation la plus chaleureuse serait de ne lésiner sur aucune des étapes. Vous en aurez besoin plus tard;)
la source
Je crois que cela peut être mieux résolu par des commentaires complets du code, au point que chaque bloc de code significatif a des commentaires explicatifs au préalable.
Les commentaires doivent inclure des citations des spécifications ou du matériel de référence matériel.
Utilisez la terminologie et les noms d'algorithme à l'échelle de l'industrie, le cas échéant - par exemple, «l'architecture X génère des interruptions CPU pour les lectures non alignées, de sorte que ce périphérique de Duff se remplisse jusqu'à la limite d'alignement suivante».
J'utiliserais un nom de variable en face à face pour ne pas mal comprendre ce qui se passe. Pas hongrois, mais des choses comme «foulée» pour décrire la distance en octets entre deux pixels verticaux.
Je voudrais également compléter cela avec un court document lisible par l'homme qui a des diagrammes de haut niveau et une conception de bloc.
la source