Question:
Le consensus de l'industrie du logiciel est qu'un code propre et simple est fondamental pour la viabilité à long terme de la base de code et de l'organisation qui en est propriétaire. Ces propriétés permettent de réduire les coûts de maintenance et d'augmenter la probabilité de poursuite de la base de code.
Cependant, le code SIMD est différent du code d'application général, et j'aimerais savoir s'il existe un consensus similaire concernant un code propre et simple s'appliquant spécifiquement au code SIMD.
Contexte de ma question.
J'écris beaucoup de code SIMD (instruction unique, données multiples) pour diverses tâches de traitement et d'analyse d'images. Récemment, j'ai également dû porter un petit nombre de ces fonctions d'une architecture (SSE2) à une autre (ARM NEON).
Le code est écrit pour un logiciel sous emballage, il ne peut donc pas dépendre de langages propriétaires sans droits de redistribution illimités tels que MATLAB.
Un exemple de structure de code typique:
- Utilisation du type de matrice d' OpenCV (
Mat
) pour toute la gestion de la mémoire, du tampon et de la durée de vie. - Après avoir vérifié la taille (dimensions) des arguments d'entrée, les pointeurs vers l'adresse de début de chaque ligne de pixels sont pris.
- Le nombre de pixels et les adresses de début de chaque ligne de pixels de chaque matrice d'entrée sont passés dans certaines fonctions C ++ de bas niveau.
- Ces fonctions C ++ de bas niveau utilisent les intrinsèques SIMD (pour l' architecture Intel et ARM NEON ), en chargeant et en sauvegardant les adresses de pointeurs brutes.
- Caractéristiques de ces fonctions C ++ de bas niveau:
- Exclusivement unidimensionnel (consécutif en mémoire)
- Ne traite pas des allocations de mémoire.
(Chaque allocation, y compris les temporaires, est gérée par le code externe à l'aide des fonctionnalités OpenCV.) - La plage de longueurs de noms des symboles (intrinsèques, noms de variables, etc.) est d'environ 10 à 20 caractères, ce qui est assez excessif.
(Se lit comme un babillage techno.) - La réutilisation des variables SIMD est déconseillée car les compilateurs sont assez bogués dans l'analyse correcte du code qui n'est pas écrit dans le style de codage "à affectation unique".
(J'ai déposé plusieurs rapports de bogues du compilateur.)
Quels aspects de la programmation SIMD feraient en sorte que la discussion diffère du cas général? Ou, pourquoi SIMD est-il différent?
En termes de coût de développement initial
- Il est bien connu que le coût de développement initial du code SIMD C ++ avec de bonnes performances est d'environ 10x - 100x (avec une large marge) par rapport au code C ++ écrit de manière non conventionnelle.
- Comme indiqué dans les réponses à Choisir entre performance et code lisible / plus propre? , la plupart du code (y compris le code écrit par hasard et le code SIMD) n'est initialement ni propre ni rapide .
- Les améliorations évolutives des performances du code (en code scalaire et SIMD) sont déconseillées (car elles sont considérées comme une sorte de retouche logicielle ), et le coût et les avantages ne sont pas suivis.
En termes de propension
(par exemple le principe de Pareto, alias la règle 80-20 )
- Même si le traitement d'image ne comprend que 20% d'un système logiciel (en termes de taille de code et de fonctionnalité), le traitement d'image est relativement lent (lorsqu'il est considéré comme un pourcentage du temps CPU passé), prenant plus de 80% du temps.
- Cela est dû à l'effet de la taille des données: une taille d'image typique est mesurée en mégaoctets, tandis que la taille typique des données non-image est mesurée en kilo-octets.
- Dans le code de traitement d'image, un programmeur SIMD est formé pour reconnaître automatiquement le code à 20% comprenant les points chauds en identifiant la structure de boucle dans le code C ++. Ainsi, du point de vue d'un programmeur SIMD, 100% du "code qui compte" est un goulot d'étranglement des performances.
- Souvent dans un système de traitement d'image, plusieurs points d'accès existent et occupent des proportions comparables de temps. Par exemple, il peut y avoir 5 points d'accès occupant chacun (20%, 18%, 16%, 14%, 12%) du temps total. Pour obtenir un gain de performances élevé, tous les hotspots doivent être réécrits dans SIMD.
- Cela se résume comme la règle du saut de ballon: un ballon ne peut pas être sauté deux fois.
- Supposons qu'il y ait des ballons, disons 5 d'entre eux. La seule façon de les décimer est de les faire éclater un par un.
- Une fois le premier ballon sauté, les 4 ballons restants représentent désormais un pourcentage plus élevé du temps total d'exécution.
- Pour faire de nouveaux gains, il faut alors faire éclater un autre ballon.
(Cela va à l'encontre de la règle d'optimisation 80-20: un bon résultat économique peut être obtenu après la cueillette des 20% de fruits les plus bas.)
En termes de lisibilité et de maintenance
Le code SIMD est manifestement difficile à lire.
- Cela est vrai même si l'on suit toutes les meilleures pratiques d'ingénierie logicielle, par exemple le nommage, l'encapsulation, la constance (et la mise en évidence des effets secondaires), la décomposition des fonctions, etc.
- Cela est vrai même pour les programmeurs SIMD expérimentés.
Le code SIMD optimal est très tordu (voir remarque) par rapport à son code prototype C ++ équivalent.
- Il existe de nombreuses façons de déformer le code SIMD, mais seulement 1 tentative sur 10 permettra d'obtenir des résultats rapidement acceptables.
- (C'est-à-dire dans les airs de gains de performances 4x-10x afin de justifier un coût de développement élevé. Des gains encore plus élevés ont été observés dans la pratique.)
(Remarque)
Il s'agit de la thèse principale du projet MIT Halide - citant le titre de l'article textuellement:
"découplage des algorithmes des plannings pour une optimisation aisée des pipelines de traitement d'image"
En termes d'applicabilité directe
- Le code SIMD est strictement lié à une architecture unique. Chaque nouvelle architecture (ou chaque élargissement des registres SIMD) nécessite une réécriture.
- Contrairement à la majorité du développement logiciel, chaque morceau de code SIMD est généralement écrit dans un seul but qui ne change jamais.
(À l'exception du portage vers d'autres architectures.) - Certaines architectures conservent une parfaite compatibilité descendante (Intel); certains échouent par une quantité insignifiante (ARM AArch64, remplaçant
vtbl
parvtblq
) mais qui est suffisant pour empêcher la compilation de certains codes.
En termes de compétences et de formation
- Les connaissances préalables requises pour former correctement un nouveau programmeur à l'écriture et à la maintenance du code SIMD ne sont pas claires.
- Les diplômés du collégial qui ont appris les programmes SIMD à l'école semblent le mépriser et le rejeter comme une carrière peu pratique.
- Le démontage-lecture et le profilage de performances de bas niveau sont cités comme deux compétences fondamentales pour écrire du code SIMD hautes performances. Cependant, on ne sait pas comment former systématiquement les programmeurs à ces deux compétences.
- L'architecture CPU moderne (qui diffère considérablement de ce qui est enseigné dans les manuels) rend la formation encore plus difficile.
En termes d'exactitude et de coûts liés aux défauts
- Une seule fonction de traitement SIMD est en fait suffisamment cohérente pour que l'on puisse établir l'exactitude en:
- Appliquer des méthodes formelles (avec stylo et papier) , et
- Vérification des plages d'entiers en sortie (avec le code prototype et effectuées en dehors de l'exécution) .
- Le processus de vérification est cependant très coûteux (consacre 100% de temps à l'examen du code et 100% de temps à la vérification du modèle de prototype), ce qui triple le coût de développement déjà coûteux du code SIMD.
- Si un bogue parvient d'une manière ou d'une autre à passer à travers ce processus de vérification, il est presque impossible de "réparer" (corriger) sauf pour remplacer (réécrire) la fonction défectueuse suspectée.
- Le code SIMD souffre des défauts évidents du compilateur C ++ (optimisation du générateur de code).
- Le code SIMD généré à l'aide de modèles d'expression C ++ souffre également grandement des défauts du compilateur.
En termes d'innovations disruptives
De nombreuses solutions ont été proposées par les universités, mais peu connaissent une utilisation commerciale généralisée.
- MIT Halide
- Stanford Darkroom
- NT2 (Numerical Template Toolbox) et le Boost.SIMD associé
Les bibliothèques à usage commercial répandu ne semblent pas être fortement compatibles SIMD.
- Les bibliothèques open source semblent tièdes pour SIMD.
- Récemment, j'ai cette observation de première main après avoir profilé un grand nombre de fonctions API OpenCV, à partir de la version 2.4.9.
- De nombreuses autres bibliothèques de traitement d'images que j'ai présentées ne font pas non plus un usage intensif de SIMD, ou manquent les véritables hotspots.
- Les bibliothèques commerciales semblent éviter complètement SIMD.
- Dans certains cas, j'ai même vu des bibliothèques de traitement d'image rétablir le code optimisé SIMD dans une version antérieure en code non SIMD dans une version ultérieure, entraînant de graves régressions de performances.
(La réponse du vendeur est qu'il était nécessaire d'éviter les bogues du compilateur.)
- Dans certains cas, j'ai même vu des bibliothèques de traitement d'image rétablir le code optimisé SIMD dans une version antérieure en code non SIMD dans une version ultérieure, entraînant de graves régressions de performances.
- Les bibliothèques open source semblent tièdes pour SIMD.
La question de ce programmeur: le code à faible latence doit-il parfois être "moche"? est lié, et j'ai déjà écrit une réponse à cette question pour expliquer mes points de vue il y a quelques années.
Cependant, cette réponse est à peu près "l'apaisement" au point de vue de "l'optimisation prématurée", c'est-à-dire au point de vue que:
- Toutes les optimisations sont prématurées par définition (ou, à court terme par nature ), et
- La seule optimisation qui présente des avantages à long terme est la simplicité.
Mais de tels points de vue sont contestés dans cet article d'ACM .
Tout cela m'amène à me demander: le
code SIMD est différent du code d'application général, et je voudrais savoir s'il existe un consensus similaire dans l'industrie concernant la valeur d'un code propre et simple pour le code SIMD.