SIMD Intrinsics ne sont pas si effrayants, mais devrions-nous les utiliser?

La programmation de bas niveau est-elle un péché ou une vertu? Ça dépend.

Lors de la programmation pour l'utilisation du traitement vectoriel sur un processeur moderne, idéalement, j'écrirais du code dans mon langage préféré et il fonctionnerait aussi vite que possible «automatiquement par magie».

À moins que vous ne commenciez à programmer la semaine dernière, je suppose que vous savez que ce n'est pas ainsi que le monde fonctionne. Des performances optimales ne viennent qu'avec l'effort. D'où ma question: jusqu'où faut-il aller?

Opérations vectorielles définies

Une opération «vectorielle» est une opération mathématique qui effectue plus d'une opération. Un ajout vectoriel peut ajouter huit paires de nombres au lieu de l'addition régulière, qui n'ajoute qu'une seule paire de nombres. Pensez à demander à l'ordinateur d'ajouter deux nombres ensemble. Nous pouvons le faire avec une instruction d'ajout régulière. Pensez à demander à l'ordinateur d'ajouter huit paires de nombres les uns aux autres (calculez C1 = A1 + B1, C2 = A2 + B2,… C8 = A8 + B8). Nous pouvons le faire avec une instruction d'ajout de vecteur .

Les instructions vectorielles incluent l'addition, la soustraction, la multiplication et d'autres opérations.

 SIMD: parallélisme des vecteurs

Les informaticiens ont un nom sophistiqué pour les instructions vectorielles: SIMD ou «Single Instruction Multiple Data». Si nous considérons une instruction d'ajout régulière comme un SISD (Single Instruction Single Data) où single signifie une seule paire d'entrées de données, alors un vecteur add est un SIMD où multiple pourrait signifier huit paires d'entrées de données.

J'aime appeler SIMD «l'autre parallélisme matériel», car le «parallélisme» dans les ordinateurs est si souvent considéré comme venant d'avoir plusieurs cœurs. Le nombre de cœurs a régulièrement augmenté. Un nombre de cœurs de quatre est courant, 20 ou plus sont courants dans les processeurs pour serveurs et le nombre de cœurs le plus élevé d'Intel est aujourd'hui de 72 cœurs dans un seul processeur Intel® Xeon Phi ™.

La taille des instructions vectorielles a également augmenté. Les premières instructions vectorielles, telles que SSE, exécutaient jusqu'à quatre opérations à la fois. La largeur de vecteur supérieure d'Intel aujourd'hui, dans l'AVX-512, effectue jusqu'à 16 opérations à la fois.

 Jusqu'où devrions-nous aller?

Avec autant de performances en jeu, combien de travail devons-nous faire pour exploiter ces performances?

 La réponse est beaucoup, et voici pourquoi: quatre cœurs peuvent nous permettre d'accélérer au maximum 4X. AVX (la moitié de la taille de l'AVX-512, mais beaucoup plus commun) peut nous permettre d'accélérer jusqu'à 8X au maximum. Combinés, ils peuvent atteindre jusqu'à 32X. Faire les deux a beaucoup de sens.

Voici ma liste simple de la façon d'essayer d'exploiter les instructions vectorielles (dans l'ordre où nous devrions essayer de les appliquer):

 1.     Premièrement, appelez une bibliothèque qui fait le travail (le summum de la vectorisation implicite). Un exemple d'une telle bibliothèque est la bibliothèque Intel® Math Kernel (Intel® MKL). Tout le travail pour utiliser les instructions vectorielles a été effectué par quelqu'un d'autre. Les limites sont évidentes: nous devons trouver une bibliothèque qui fait ce dont nous avons besoin.

2.     Deuxièmement, utilisez la vectorisation implicite. Restez abstrait et écrivez-le vous-même en utilisant des modèles ou des compilateurs pour vous aider. De nombreux compilateurs ont des options et des commutateurs de vectorisation. Les compilateurs sont probablement la solution la plus portable et la plus stable. Il existe de nombreux modèles pour la vectorisation, mais aucun n'a été suffisamment utilisé au fil du temps pour être un gagnant clair (une entrée récente est Intel® SIMD Data Layout Templates [Intel® SDLT]).

3.     Troisièmement, utilisez la vectorisation explicite. Ceci est devenu très populaire ces dernières années et tente de résoudre le problème de rester abstrait mais en forçant le compilateur à utiliser des instructions vectorielles alors qu'il ne les utiliserait pas autrement. La prise en charge de SIMD dans OpenMP est l'exemple clé ici, où les requêtes de vectorisation pour le compilateur sont données très explicitement. Des extensions non standard existent dans de nombreux compilateurs, souvent sous la forme d'options ou de «pragmas». Si vous prenez cette voie, OpenMP est la voie à suivre si vous êtes en C, C ++ ou Fortran.

4.     Enfin, soyez bas et sale. Utilisez les intrinsèques SIMD. C'est comme le langage d'assemblage, mais écrit dans votre programme C / C ++. Les intrinsèques SIMD ressemblent en fait à un appel de fonction, mais produisent généralement une seule instruction (une instruction d'opération vectorielle, également appelée instruction SIMD).

Les intrinsèques SIMD ne sont pas mauvais; cependant, ils sont un dernier recours. Les trois premiers choix sont toujours plus faciles à maintenir pour l'avenir lorsqu'ils fonctionnent. Cependant, lorsque les trois premiers ne répondent pas à nos besoins, nous devons absolument essayer d'utiliser les intrinsèques SIMD.

 Si vous voulez commencer à utiliser les intrinsèques SIMD, vous aurez une longueur d'avance si vous êtes habitué à la programmation en langage assembleur. Ceci est principalement dû au fait que vous aurez plus de facilité à lire la documentation qui explique les opérations, y compris l'excellent «Guide intrinsèque» d'Intel en ligne. Si vous êtes complètement nouveau dans ce domaine, je suis tombé sur un blog récent ("SSE: attention à l'écart!") Qui a une main douce dans l'introduction des intrinsèques. J'aime aussi «Crunching Numbers with AVX and AVX2».

 Si une bibliothèque ou un compilateur peut faire ce dont vous avez besoin, les intrinsèques SIMD ne sont pas le meilleur choix. Cependant, ils ont leur place et ils ne sont pas difficiles à utiliser une fois que vous vous y êtes habitué. Donnez-leur un essai. Les avantages en termes de performances peuvent être incroyables. J'ai vu des intrinsèques SIMD utilisés par des programmeurs intelligents pour du code qu'aucun compilateur n'est susceptible de produire.

Même si nous essayons les intrinsèques SIMD, et que nous laissons finalement une bibliothèque ou un compilateur faire le travail, ce que nous apprenons peut être inestimable pour comprendre la meilleure utilisation d'une bibliothèque ou d'un compilateur pour la vectorisation. Et c'est peut-être la meilleure raison d'essayer les intrinsèques SIMD la prochaine fois que nous aurons besoin de quelque chose pour utiliser des instructions vectorielles.

Cliquez ici pour télécharger votre essai gratuit de 30 jours d'Intel Parallel Studio XE