Comment optimiser un cycle?
-
10-10-2019 - |
Question
J'ai la fonction de goulot d'étranglement suivant.
typedef unsigned char byte;
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3)
{
const byte b1 = 128-30;
const byte b2 = 128+30;
for (const byte * p1 = p1Start; p1 != p1End; ++p1, ++p2, ++p3) {
*p3 = (*p1 < *p2 ) ? b1 : b2;
}
}
Je veux remplacer le code de C++
avec SSE2 fonctions intinsic. Je l'ai essayé _mm_cmpgt_epi8
mais utilisé signé comparer. Je besoin non signés comparer.
Y at-il astuce (SSE, SSE2, SSSE3) pour résoudre mon problème?
Remarque: Je ne veux pas utiliser dans ce cas multi-threading.
La solution
Au lieu de compenser vos valeurs signées pour les rendre non signé, d'une manière un peu plus efficace serait de faire ce qui suit:
- utilisation
_mm_min_epu8
pour obtenir le min non signé de p1, p2 - comparer cette min pour l'égalité avec p2 en utilisant
_mm_cmpeq_epi8
- le masque résultant sera désormais 0x00 pour les éléments p1
= p2 - vous pouvez maintenant utiliser ce masque avec
_mm_or_si128
et_mm_andc_si128
pour sélectionner les valeurs b1 / b2 appropriée
Notez que ceci est 4 instructions au total, par rapport à 5 en utilisant la méthode de comparaison offset + signée.
Autres conseils
Vous pouvez soustraire 127 de vos numéros, puis utilisez _mm_cmpgt_epi8
Oui, cela peut être fait en SIMD, mais il faudra quelques pas pour faire le masque.
Ruslik il a raison, je pense. Vous voulez XOR chaque composant avec 0x80 pour retourner le sens de la comparaison signé et non signé. _mm_xor_si128 (PXOR
) vous permet de vous que - vous aurez besoin pour créer le masque comme un tableau char statique quelque part avant de le charger dans un registre SIMD. Ensuite _mm_cmpgt_epi8 vous obtient un masque et vous pouvez utiliser Bitwise ET (par exemple _mm_and_si128
) pour effectuer un mouvement masqué.
Oui, SSE ne fonctionnera pas ici. Vous pouvez améliorer cette performance de code sur ordinateur multi-core en utilisant OpenMP:
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3) { const byte b1 = 128-30; const byte b2 = 128+30; int n = p1End - p1Start; #pragma omp parallel for for (int i = 0; i < n; ++p1, ++i) { p3[i] = (p1[i] < p2[i]) ? b1 : b2; } }
Malheureusement, la plupart des réponses ci-dessus sont incorrectes. Supposons un mot de 3 bits:
unsigned: 4 5 6 7 0 1 2 3 == signée: -4 -3 -2 -1 0 1 2 3 (bits: 101 110 111 100 000 001 010 011)
La méthode de Paul R est incorrect. Supposons que nous voulons savoir si 3> 2. min (3,2) == 2, ce qui suggère oui, la méthode fonctionne ici. Supposons maintenant que nous voulons savoir si, si 7> 2. La valeur 7 est -1 en représentation signée, donc min (-1,2) == -1, ce qui suggère à tort que 7 ne soit pas supérieure à 2 non signé.
La méthode par Andrey est incorrect. Supposons que nous voulons savoir si 7> 2, ou a = 7, et b = 2. La valeur 7 est -1 dans la représentation signée, de sorte que le premier terme (a> b) échoue, et la méthode suggère que 7 n'est pas plus à 2.
Cependant, le procédé par BJobnh, tel que corrigé par Alexey, est correcte. Il suffit de soustraire 2 ^ (n-1) à partir des valeurs, où n est le nombre de bits. Dans ce cas, nous soustraire 4 pour obtenir de nouvelles valeurs correspondant:
vieux signée: -4 -3 -2 -1 0 1 2 3 => new signée:. 0 1 2 3 -4 -3 -2 -1 == nouveau unsigned 0 1 2 3 4 5 6 7
En d'autres termes, unsigned_greater_than (a, b) est équivalent à signed_greater_than (a - 2 ^ (n-1), b - 2 ^ (n-1)).
utilisation pcmpeqb et la puissance avec vous.