J'ai beaucoup lu sur les avantages de l'organisation des données en `` structures de tableaux '' (SoA) au lieu du `` tableau de structures '' (AoS) typique pour obtenir un meilleur débit lors de l'utilisation d' instructions SIMD . Bien que le `` pourquoi '' ait un sens total pour moi, je ne sais pas combien faire pour travailler avec des choses comme des vecteurs.
Les vecteurs eux-mêmes peuvent être considérés comme une structure d'un tableau de données (de taille fixe), vous pouvez donc convertir un tableau de ces derniers en une structure de tableaux X, Y et Z. Grâce à cela, vous pouvez travailler sur 4 vecteurs à la fois, par opposition à un à la fois.
Maintenant, pour la raison spécifique que je poste sur GameDev:
Est-ce que cela a du sens pour travailler avec des vecteurs sur le SPU? Plus précisément, cela a-t-il un sens pour les tableaux DMA multiples juste pour un seul vecteur? Ou serait-il préférable de s'en tenir à DMAing le tableau de vecteurs et de les dérouler dans les différents composants avec lesquels travailler?
Je pouvais voir l'avantage de couper le déroulement (si vous l'avez fait `` AoS ''), mais il semble que vous pourriez rapidement manquer de canaux DMA si vous preniez cette route et travailliez avec plusieurs ensembles de vecteurs à la fois.
(Remarque: aucune expérience professionnelle avec Cell pour le moment, mais nous avons joué dans OtherOS pendant un certain temps)
la source
Les SPU sont en fait un cas spécial intéressant en ce qui concerne la vectorisation du code. Les instructions sont divisées en familles «arithmétique» et «chargement / stockage», et les deux familles s'exécutent sur des pipelines distincts. Le SPU peut émettre un de chaque type par cycle.
Le code mathématique est évidemment fortement lié par des instructions mathématiques - donc généralement les boucles mathy sur SPU auront beaucoup, beaucoup de cycles ouverts sur le tube de chargement / stockage. Étant donné que les brassages se produisent sur le tuyau de chargement / stockage, vous avez souvent suffisamment d'instructions de chargement / stockage gratuites pour passer du formulaire xyzxyzxyzxyz au format xxxxyyyyzzzz sans aucun frais généraux.
Cette technique est utilisée au moins chez Naughty Dog - voir leurs présentations d'assemblage SPU ( partie 1 et partie 2 ) pour plus de détails.
Malheureusement, le compilateur n'est souvent pas assez intelligent pour le faire automatiquement - si vous décidez de suivre cette voie, vous devrez soit écrire l'assemblage vous-même, soit dérouler vos boucles à l'aide d'intrinsèques et vérifier l'assembleur pour vous assurer que c'est ce que vous voulez. Donc, si vous cherchez à écrire du code multi-plateforme général qui fonctionne bien sur SPU, vous voudrez peut-être utiliser SoA ou AoSoA (comme le suggère jpaver).
la source
Comme pour toute optimisation, profil! La lisibilité vient en premier et ne doit être sacrifiée que lorsque le profilage identifie un goulot d'étranglement particulier et que vous avez épuisé toutes vos options pour régler l'algorithme de haut niveau (le moyen le plus rapide de faire le travail est de ne pas avoir à faire le travail!) Vous devez toujours reprofiler en suivant toute optimisation de bas niveau pour confirmer que vous avez vraiment rendu les choses plus rapides que l'inverse, en particulier avec des pipelines aussi excentriques que ceux de la cellule.
Les techniques que vous utiliserez alors dépendront des détails du goulot d'étranglement. En général, lorsque vous travaillez avec des types vectoriels, un composant vectoriel que vous ignorez dans un résultat représente un gaspillage de travail. La commutation SoA / AoS n'a de sens que si elle vous permet de faire un travail plus utile en remplissant ces composants inutilisés (par exemple, un produit scalaire sur le PPU de la PS3 vs quatre produits scalaires en parallèle dans le même laps de temps). Pour répondre à votre question, passer du temps à mélanger les composants juste pour effectuer une opération sur un seul vecteur me semble être une pessimisation!
Le revers des SPU est que la majeure partie du coût des petits transferts DMA est en cours de configuration; rien de moins de 128 octets prendra le même nombre de cycles à transférer, et rien de moins d'environ un kilo-octet seulement quelques cycles de plus. Ne vous inquiétez donc pas du fait que DMA utilise plus de données que vous n'en avez strictement besoin; la réduction du nombre de transferts DMA séquentiels déclenchés et l'exécution de travaux pendant les transferts DMA - et donc le déploiement de prologues et d'épilogues de boucle pour former des pipelines logiciels - est la clé des bonnes performances du SPU, et il est plus facile de traiter les cas d'angle en récupérant des données supplémentaires / rejeter les résultats partiellement calculés plutôt que de sauter à travers des cerceaux pour essayer de déterminer la quantité exacte de données qui doivent être lues et traitées.
la source
Non, cela n'aurait pas beaucoup de sens en général car la plupart des opcodes vectoriels opèrent sur un vecteur dans son ensemble et non sur des composants séparés. Vous pouvez donc déjà multiplier un vecteur en 1 instruction, alors qu'avec la séparation des composants séparés, vous dépenseriez 4 instructions dessus. Donc, comme vous effectuez essentiellement de nombreuses opérations en général sur une partie d'une structure, vous êtes préférable de les regrouper dans un tableau, mais vous ne faites presque jamais des choses uniquement sur un composant d'un vecteur, ou très différentes sur chaque composant, donc les casser out ne fonctionnerait pas.
Bien sûr, si vous trouvez une situation où vous devez faire quelque chose uniquement avec (disons) les composants x des vecteurs, cela pourrait fonctionner, mais la pénalité de tout renvoyer lorsque vous avez besoin du vecteur réel ne serait pas bon marché, donc vous pourriez Je me demande si vous ne devriez pas utiliser de vecteurs pour commencer, mais juste un tableau de flottants qui permettent aux opcodes vectoriels de faire leurs calculs spécifiques.
la source