Question

Y at-il une différence entre intrinsics SSE logiques pour différents types? Par exemple, si nous prenons l'opération OU, il y a trois: intrinsics _mm_or_ps, _mm_or_pd et _mm_or_si128 tous qui font la même chose: calculer bitwise OU de leurs opérandes. Mes questions:

  1. Quelle est la différence entre l'utilisation de l'une ou l'autre intrinsèque (avec coulée de type approprié). Ne il y avoir des coûts cachés comme l'exécution plus dans une situation spécifique?

  2. Ces cartes de trois à des instructions intrinsèques x86 différentes (por, orps, ORPD). Est-ce que quelqu'un a des idées pourquoi Intel gaspille l'espace précieux opcode pour plusieurs instructions qui font la même chose?

Était-ce utile?

La solution

Je pense que tous les trois sont effectivement les mêmes, à savoir 128 bits opérations au niveau du bit. La raison pour laquelle il existe différentes formes est probablement historique, mais je ne suis pas certain. Je suppose qu'il est possible qu'il peut y avoir un comportement supplémentaire dans les versions à virgule flottante, par exemple quand il y a NaN, mais cela est pure conjecture. Pour les entrées normales les instructions semblent être interchangeables, par exemple.

#include <stdio.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <xmmintrin.h>

int main(void)
{
    __m128i a = _mm_set1_epi32(1);
    __m128i b = _mm_set1_epi32(2);
    __m128i c = _mm_or_si128(a, b);

    __m128 x = _mm_set1_ps(1.25f);
    __m128 y = _mm_set1_ps(1.5f);
    __m128 z = _mm_or_ps(x, y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    c = (__m128i)_mm_or_ps((__m128)a, (__m128)b);
    z = (__m128)_mm_or_si128((__m128i)x, (__m128i)y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    return 0;
}

$ gcc -Wall -msse3 por.c -o por

$ ./por

a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000
a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000

Autres conseils

  
      
  1. Quelle est la différence entre l'utilisation de l'une ou l'autre intrinsèque (avec coulée de type approprié). Ne il y avoir des coûts cachés comme l'exécution plus dans une situation spécifique?
  2.   

Oui, il peut y avoir des raisons de performances à choisir l'un par rapport à l'autre.

1 Parfois, il y a un cycle ou deux de latence (délai d'envoi) si la sortie d'une unité d'exécution de nombre entier doit être acheminé à l'entrée d'une unité d'exécution FP, ou vice versa . Il faut beaucoup de fils pour se déplacer 128b de données à l'une des nombreuses destinations possibles, donc les concepteurs de CPU doivent faire des compromis, comme ayant seulement une voie directe de chaque sortie FP à chaque entrée FP, pas toutes les entrées possibles.

Voir cette réponse, ou microarchitecture doc de de Agner Fog pour by-pass retards. Recherche de « retards de contournement des données sur Nehalem » dans la doc de Agner; il a quelques bons exemples et des discussions pratiques. Il a une section pour chaque microarch qu'il a analysé.

  

Cependant, les retards pour transmettre des données entre la   différents domaines ou différents types de registres sont plus petits sur la   Sandy Bridge et Ivy Bridge que sur le Nehalem, et souvent nul. -   arc micro de Agner Fog doc

Rappelez-vous que la latence n'a pas d'importance si elle est pas sur le chemin critique de votre code. L'utilisation pshufd au lieu de movaps + shufps peut être une victoire si le débit de UOP est votre goulot d'étranglement, plutôt que la latence de votre chemin critique.

2 La version ...ps prend 1 octet de moins de code que les deux autres. Ceci alignera différemment les instructions suivantes, qui peuvent la matière pour les décodeurs et / ou des lignes de cache de UOP.

3:. récents processeurs Intel ne peut exécuter les versions FP sur port5

  • Merom (Core2) et Penryn: orps peut fonctionner sur P0 / P1 / P5, mais entier domaine uniquement. On peut supposer que les 3 versions décodées dans la même UOP exacte. Ainsi, le délai de transmission inter-domaines qui se passe. (AMD CPUs faire aussi: instructions FP binaires exécutent dans le domaine de IVEC.)

  • Nehalem / Sandybridge / IVB / Haswell / Broadwell: por peut fonctionner sur p0 / p1 / p5, mais orps peut fonctionner uniquement sur port5. p5 est également nécessaire par les remaniements, mais le FMA, FP ajouter, et les unités FP mul sont sur les ports 0/1.

  • Skylake: por et orps les deux ont 3 par cycle débit . Informations sur les délais d'expédition ne sont pas encore disponibles.

Notez que le SNB / IVB (AVX mais pas AVX2), ne doit p5 pour gérer 256B opérations logiques, comme vpor ymm, ymm exige AVX2. Ce fut sans doute pas la raison de ce changement, étant donné que Nehalem fait cela.

Comment choisir judicieusement :

Si le débit op logique sur port5 pourrait être un goulot d'étranglement, puis utilisez les versions entières, même sur des données FP. Cela est particulièrement vrai si vous voulez utiliser des remaniements entiers ou d'autres instructions de transfert de données.

processeurs AMD toujours utiliser le domaine entier pour Logicals, donc si vous avez plusieurs choses domaine entier à faire, les faire à la fois pour réduire au minimum allers-retours entre les domaines. Shorter latences vont faire avancer les choses débarrassées de la mémoire tampon de réapprovisionnement plus rapide, même si une chaîne est dep pas le goulot d'étranglement pour votre code.

Si vous voulez juste activer / désactiver l'/ retourner un peu dans des vecteurs FP entre FP ajouter et instructions MUL, utilisez les Logicals de ...ps, même sur des données à double précision, parce que simple et double FP sont le même domaine sur chaque CPU existence, et les versions ...ps sont un octet plus courte.

Il y a des raisons de facteur humain / pratiques pour l'utilisation des versions ...pd, cependant, qui sera souvent outweigh sauvegarde 1 octet de code. Lisibilité de votre code par d'autres humains est un facteur: Ils se demandent pourquoi vous traitez vos données en tant que célibataires quand il est double en fait. Esp. avec C / C ++ intrinsics, jonchant votre code avec des moulages entre __mm256 et __mm256d est pas la peine. Si l'accord sur le niveau des questions d'alignement de insn, écrire en asm directement, pas intrinsics! (Avoir l'instruction un octet plus pourrait mieux aligner les choses pour la densité des lignes de cache de UOP et / ou décodeurs).

Pour les données entières, utilisez les versions entières. Enregistrement d'un octet d'instruction ne vaut pas le by-pass retard, et le code entier conserve souvent port5 entièrement occupé par les remaniements. Pour Haswell, beaucoup de lecture aléatoire / insert / extrait / paquet / instructions Déballez se sont p5 seulement, au lieu de p1 / p5 SNB / IVB.

  
      
  1. Ces cartes de trois à des instructions intrinsèques x86 différentes (por, orps,   orpd). Est-ce que quelqu'un a des idées pourquoi Intel gaspille opcode précieux   l'espace pour plusieurs instructions qui font la même chose?
  2.   

Si vous regardez l'histoire de ces jeux d'instructions, vous pouvez voir genre de la façon dont nous sommes arrivés ici.

por  (MMX):     0F EB /r
orps (SSE):     0F 56 /r
orpd (SSE2): 66 0F 56 /r
por  (SSE2): 66 0F EB /r

MMX existait avant SSE, il ressemble à opcodes pour les instructions SSE (de ...ps) ont été choisis sur le même espace 0F xx. Ensuite, SSE2, la version ...pd a ajouté un préfixe opérande taille 66 à l'opcode ...ps, et la version entière a ajouté un préfixe 66 à la version MMX.

Ils pourrait ont laissés orpd et / ou por, mais ils ne l'ont pas. Peut-être qu'ils pensaient que la conception des futurs processeurs pourraient avoir des chemins plus longs entre la transmission des domaines différents, et ainsi en utilisant l'instruction correspondant à vos données seraient une plus grande affaire. Même si il y a des opcodes séparés, AMD et Intel au début les traités tout de même, comme int vecteur.

Selon les directives d'optimisation Intel et AMD mélange des types op avec des types de données produit une performance due en tant que CPU balises interne 64 moitiés de bits du registre pour un type de données particulier. Cela semble affecter principalement tuyau doublure comme l'instruction est décodé et les UOP sont prévus. Fonctionnellement ils produisent le même résultat. Les versions les plus récentes pour les types de données entier ont un codage plus grand et prennent plus de place dans le segment de code. Donc, si la taille du code est un problème, utilisez les anciennes opérations que celles-ci ont un codage plus petit.

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