Question

Est-ce une bonne idée de vectoriser le code? Quelles sont les bonnes pratiques en termes de moment pour le faire? Que se passe-t-il en dessous?

Était-ce utile?

La solution

La vectorisation signifie que le compilateur détecte que vos instructions indépendantes peuvent être exécutées comme une Simd instruction. L'exemple habituel est que si vous faites quelque chose comme

for(i=0; i<N; i++){
  a[i] = a[i] + b[i];
}

Il sera vectorisé comme (en utilisant la notation vectorielle)

for (i=0; i<(N-N%VF); i+=VF){
  a[i:i+VF] = a[i:i+VF] + b[i:i+VF];
}

Fondamentalement, le compilateur choisit une opération qui peut être effectuée sur des éléments VF du tableau en même temps et fait ce n / vf fois au lieu de faire le fonctionnement unique n fois.

Il augmente les performances, mais met plus d'exigences sur l'architecture.

Autres conseils

Comme mentionné ci-dessus, la vectorisation est utilisée pour utiliser les instructions SIMD, qui peuvent effectuer des opérations identiques de différentes données emballées dans de grands registres.

Une directive générique pour permettre à un compilateur de s'autoVERVECTRIZE une boucle consiste à s'assurer qu'il n'y a pas d'éléments de données B / W Flow et Anti-Dépendances dans différentes itérations d'une boucle.

http://en.wikipedia.org/wiki/data_dependency

Certains compilateurs comme les compilateurs Intel C ++ / FORTRAN sont capables de code automatiquement. Dans le cas où il n'aurait pas été en mesure de vectoriser une boucle, le compilateur Intel est capable de expliquer pourquoi il ne pouvait pas le faire. Il peut être utilisé pour modifier le code de telle sorte qu'il devient vectoriel (en supposant que c'est possible)

Les dépendances sont couvertes en profondeur dans le livre «Optimiser les compilateurs pour les architectures modernes: une approche basée sur la dépendance»

C'est la génération de code SSE.

Vous avez une boucle avec du code de matrice flottante dans IT Matrix1 [i] [j] + matrix2 [i] [j] et le compilateur génère du code SSE.

La vectorisation n'a pas besoin d'être limitée au registre unique qui peut contenir de grandes données. Comme utiliser le registre «128» pour contenir les données de 4 x 32 pouces. Cela dépend des limitations architecturales. Certaines architectures ont des unités d'exécution différentes qui ont leurs propres registres. Dans ce cas, une partie des données peut être alimentée à cette unité d'exécution et le résultat peut être tiré d'un registre correspondant à cette unité d'exécution.

Par exemple, considérez le cas ci-dessous.

pour (i = 0; i <n; i ++)
{
a [i] = a [i] + b [i];
}



Si je travaille sur une architecture qui a deux unités d'exécution, alors la taille de mon vecteur est définie comme deux. La boucle mentionnée ci-dessus sera recadrée comme

pour (i = 0; i <(n / 2); i + = 2)
{
a [i] = a [i] + b [i];


a [i + 1] = a [i + 1] + b [i + 1];
}

Remarque: Le 2 à l'intérieur de l'instruction FOR est dérivé de la taille du vecteur.

Comme j'ai deux unités d'exécution, les deux instructions à l'intérieur de la boucle seront introduites dans les deux unités d'exécution. La somme sera accumulée dans les unités d'exécution séparément. Enfin, la somme des valeurs accumulées (à partir de deux unités d'exécution) sera effectuée.

Les bonnes pratiques sont
1. Les contraintes telles que la dépendance (entre différentes itérations de la boucle) doivent être vérifiées avant de vectoriser la boucle.
2. Les appels de fonction doivent être évités.
3. L'accès au pointeur peut créer un aliasage et il faut l'éviter.

Peut-être aussi jeter un œil à libsimdx86 (code source).

Un bel exemple bien expliqué est:

Choisir pour éviter les branches: un petit exemple d'Altivec

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top