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?

Était-ce utile?

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 movqs 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.

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