質問

期間2^128を持つ__m128i変数の関数が必要です。単調に(カウンターのように)増加する必要はありませんが、各値に1回アクセスしてください。

私が考えることができる最も単純な例は、実際には128ビットカウンターですが、SSEで実装するのは難しいことがわかりました。よりシンプル/高速なソリューションはありますか?

役に立ちましたか?

解決

これが単調なカウンターです。ただし、シンプルと呼ぶことができるかどうかはわかりません。

両方を仮定します ONEZERO 常にレジスタになり、5つの命令にコンパイルする必要があります。 (VEXエンコードが使用されていない場合は7または8)

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

テストコード(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;
}

出力:

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

@norbert Pによって提案されたやや優れたバージョンは、元のソリューションよりも1つの命令を節約します。

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

他のヒント

キスの原則を決して忘れないでください。

これを貼り付ける(署名されていない整数は、C標準で包むために必要であるため、各値に1回だけアクセスします):

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

の中へ これ 利回り(x64の場合):

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

簡単/速いですか?あなたがそれをインラインであるならば、あなたはおそらくだけで逃げることができるでしょう addq/adcqmovqret X64 ABIで必要です:関数にインラインである場合、それらは必要ありません)


MSVCの吸盤についてのVooのコメントに対処するには、以下を使用できます。

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

近くにMSVCインストールがないので、テストできませんが したほうがいい 私が上に投稿したものに似たものを生み出します。それから、あなたがいれば 本当 __m128iが必要です。できるはずです キャスト 2つの半分。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top