Pregunta

Necesito una función de una variable __m128i que tenga el período 2^128. No necesita aumentar monotónicamente (como un mostrador), pero visite cada valor una vez.

El ejemplo más simple que se me ocurre es, de hecho, un mostrador de 128 bits, pero me pareció difícil de implementar en SSE. ¿Hay soluciones más simples/más rápidas?

¿Fue útil?

Solución

Aquí hay un mostrador monotónico. Sin embargo, no estoy seguro de si puedes llamarlo simple.

Asumiendo ambos ONE y ZERO están siempre en registros, entonces esto debería compilar a 5 instrucciones. (7 u 8 si no se usa la codificación VEX)

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;
}

Código de prueba (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;
}

Producción:

18446744073709551610  1
18446744073709551611  1
18446744073709551612  1
18446744073709551613  1
18446744073709551614  1
18446744073709551615  1
0  2
1  2
2  2
3  2

Versión ligeramente mejor sugerida por @norbert P. Guarda 1 instrucción sobre mi solución original.

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;
}

Otros consejos

Nunca olvides el principio de beso.

Pastar esto (se requieren enteros sin firmar para envolver por el estándar C, por lo tanto, visitar cada valor solo una vez):

__uint128_t inc(__uint128_t x) {
  return x+1;
}

dentro este rendimientos (para x64):

    addq    $1, %rdi
    adcq    $0, %rsi
    movq    %rdi, %rax
    movq    %rsi, %rdx
    ret

fácil/rápido? Si está en línea, probablemente podrá salirse con la suya solo con el addq/adcq (la movqarena ret son requeridos por el X64 ABI: si está en línea la función, no se requiere)


Para abordar el comentario de Voo sobre la succión de MSVC, puede usar lo siguiente:

inline void inc(unsigned long long *x, unsigned long long *y) {
  if (!++*x) ++*y; // yay for obfuscation!
}

No tengo una instalación de MSVC cerca, así que no puedo probarlo, pero debería Reduce algo similar a lo que publiqué anteriormente. Entonces, si tu De Verdad Necesita un __m128i, debería poder emitir las dos mitades.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top