Domanda

Ho bisogno di una funzione di una variabile __M128i che ha un periodo 2^128. Non ha bisogno di aumentare monotonicamente (come un contatore), ma visitare ogni valore una volta.

L'esempio più semplice che mi viene in mente è in realtà un contatore a 128 bit, ma ho trovato difficile da implementare in SSE. Ci sono soluzioni più semplici/più veloci?

È stato utile?

Soluzione

Ecco un contatore monotonico. Non sono sicuro se puoi chiamarlo semplice però.

Assumendo entrambi ONE e ZERO sono sempre nei registri, quindi questo dovrebbe compilare a 5 istruzioni. (7 o 8 se il codifica vex non viene utilizzato)

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

Codice test (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;
}

Produzione:

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

Versione leggermente migliore suggerita da @Norbert P. Salva 1 istruzione sulla mia soluzione 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;
}

Altri suggerimenti

Non dimenticare mai il principio del bacio.

Incollare questo (i numeri interi non firmati sono tenuti a avvolgere dallo standard C, quindi visitando ogni valore solo una volta):

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

in questo rese (per x64):

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

Facile/abbastanza veloce? Se lo in linea, probabilmente sarai in grado di cavartela solo con il addq/adcq (il movqsabbia ret sono richiesti da X64 ABI: se inlinea la funzione, non sono richiesti)


Per affrontare il commento di Voo sulla risucosità di MSVC, puoi usare quanto segue:

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

Non ho un'installazione MSVC nelle vicinanze, quindi non posso testarlo, ma questo dovrebbe cedere qualcosa di simile a quello che ho pubblicato sopra. Quindi, se tu veramente Hai bisogno di un __M128i, dovresti essere in grado di farlo lancio le due metà.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top