Frage

Ich brauche eine Funktion einer __m128i -Variablen mit Periode 2^128. Es muss sich nicht monoton erhöhen (wie ein Zähler), sondern jeden Wert einmal.

Das einfachste Beispiel, das ich mir vorstellen kann, ist tatsächlich ein 128-Bit-Zähler, aber ich fand, dass das in SSE schwierig zu implementieren war. Gibt es einfachere/schnellere Lösungen?

War es hilfreich?

Lösung

Hier ist eine monotonische Theke. Ich bin mir nicht sicher, ob Sie es einfach nennen können.

Annahme beides ONE und ZERO sind immer in Registern, dann sollte dies zu 5 Anweisungen kompiliert werden. (7 oder 8, wenn nicht VEX-Codierung verwendet wird)

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

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

Ausgabe:

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

Eine etwas bessere Version von @norbert P., die 1 Anweisung über meine ursprüngliche Lösung spart.

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

Andere Tipps

Vergiss niemals das Kussprinzip.

Das Einfügen dies (unsignierte ganze Zahlen müssen nach dem C -Standard umwickeln, wodurch jeder Wert nur einmal besucht wird):

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

hinein Dies ergibt (für x64):

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

Einfach/schnell genug? Wenn Sie das einleiten, können Sie wahrscheinlich mit nur dem davonkommen addq/adcq (das movqs und ret sind vom X64 ABI erforderlich: Wenn Sie die Funktion inline investieren, sind sie nicht erforderlich).


Um den Kommentar von Voo über die Saugheit von MSVC anzusprechen, können Sie Folgendes verwenden:

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

Ich habe keine MSVC in der Nähe, also kann ich es nicht testen, aber es sollte Ergeben Sie etwas Ähnliches wie oben, was ich oben gepostet habe. Dann, wenn du Ja wirklich Benötigen Sie ein __m128i, Sie sollten in der Lage sein gießen die beiden Hälften.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top