128 bits contre SSE?
-
27-10-2019 - |
Question
besoin d'une fonction d'une variable __m128i qui a période de 2 ^ 128. Il n'a pas besoin d'augmentation de façon monotone (comme un compteur), mais visiter chaque valeur une fois.
L'exemple le plus simple que je pouvais penser est en fait un compteur 128 bits, mais je trouve que difficile à mettre en œuvre dans l'ESS. Y at-il des solutions plus simples / rapides?
La solution
Voici un compteur monotones. Je ne sais pas si vous pouvez appeler simple cependant.
Si l'on suppose à la fois ONE
et ZERO
sont toujours dans les registres, alors cela devrait compiler 5 instructions. (7 ou 8 si VEX-codage est pas utilisé)
inline __m128i nextc(__m128i x){
const __m128i ONE = _mm_setr_epi32(1,0,0,0);
const __m128i ZERO = _mm_setzero_si128();
x = _mm_add_epi64(x,ONE);
__m128i t = _mm_cmpeq_epi64(x,ZERO);
t = _mm_and_si128(t,ONE);
t = _mm_unpacklo_epi64(ZERO,t);
x = _mm_add_epi64(x,t);
return x;
}
Test Code (MSVC):
int main() {
__m128i x = _mm_setr_epi32(0xfffffffa,0xffffffff,1,0);
int c = 0;
while (c++ < 10){
cout << x.m128i_u64[0] << " " << x.m128i_u64[1] << endl;
x = nextc(x);
}
return 0;
}
Sortie:
18446744073709551610 1
18446744073709551611 1
18446744073709551612 1
18446744073709551613 1
18446744073709551614 1
18446744073709551615 1
0 2
1 2
2 2
3 2
Version légèrement mieux suggérée par @Norbert P. Il permet d'économiser 1 instruction sur ma solution originale.
inline __m128i nextc(__m128i x){
const __m128i ONE = _mm_setr_epi32(1,0,0,0);
const __m128i ZERO = _mm_setzero_si128();
x = _mm_add_epi64(x,ONE);
__m128i t = _mm_cmpeq_epi64(x,ZERO);
t = _mm_unpacklo_epi64(ZERO,t);
x = _mm_sub_epi64(x,t);
return x;
}
Autres conseils
Ne jamais oublier le principe KISS.
ce collage (entiers non signés sont nécessaires pour envelopper par la norme C, rendant donc chaque valeur une seule fois):
__uint128_t inc(__uint128_t x) {
return x+1;
}
ce rendement (pour x64):
addq $1, %rdi
adcq $0, %rsi
movq %rdi, %rax
movq %rsi, %rdx
ret
facile / assez rapide? Si vous en ligne, vous serez probablement en mesure de sortir avec juste la addq
/ adcq
(les movq
s et ret
sont requis par l'ABI x64: si vous en ligne la fonction, ils ne sont pas nécessaires)
Pour répondre à la remarque de Voo sur le suckiness de MSVC, vous pouvez utiliser les éléments suivants:
inline void inc(unsigned long long *x, unsigned long long *y) {
if (!++*x) ++*y; // yay for obfuscation!
}
Je ne pas de MSVC installer à proximité, donc je ne peux pas tester, mais devrait rendement quelque chose de semblable à ce que je posté ci-dessus. Ensuite, si vous vraiment ont besoin d'un __m128i, vous devriez être en mesure de Casting les deux moitiés.