2012-02-19 17 views
7

期間2^128の変数__m128iが必要です。単調増加(カウンターのように)する必要はありませんが、各値を一度訪れてください。128ビットSSEカウンタ?

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

+2

なぜ2^128値を訪問する必要がありますか?地球上のコンピュータはそれを行うことはできません。あなたは64ビットintを使用できませんか? – usr

+0

ギガヘルツオーダーのクロック速度を持つプロセッサでは、64ビットカウンタが使い尽くされるまでに約584年間、1サイクルごとに1つの数値を消費することができます。 – Damon

答えて

5

単調カウンタです。私はあなたがそれを簡単に呼び出せるかどうか分からない。

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

ありがとうございます、あなたのコードは私よりもはるかにクリーンです。私はカウンター以外の解決策があるかどうかを少し調べるつもりです。 – jk4736

+0

これは、SSEを使用していないソリューションよりも実際に速い場合に問題になります。私は、2 64bit unsignedと1 branchの構造体を使用する明らかな解決策が、SSEオーバーヘッドを回避し、分岐が非常に予測可能であることを意味します。 – Voo

+0

@Vooそれはおそらく、値がどんな形式で必要とされるかに依存するでしょう。汎用レジスタやメモリに必要な場合は、 'add + adc 'が最速になります。 SSEレジスタに必要な場合は、5種類のSSE-Int命令が抽出/挿入のどの種類よりも高速になる可能性があります。構造体/共用体を介してメモリに渡すと、別のワードサイズの同じメモリにアクセスしているため、ロード/ストアバッファが停止する可能性があります。 – Mysticial

4

KISSの原理を忘れないでください。

貼り付け、この(符号なし整数が故に、一度だけ、各値を訪問し、C標準で一巡するのに必要とされる):

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

簡単:(x64の場合)this利回りに

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

/十分に速い?あなたがいることをインライン化した場合、あなたはおそらくちょうどaddq/adcqで逃げることができるようになります(movq sおよびretはx64のABIによって必要とされている:あなたは関数をインライン場合、それらが必要とされていない)


それはに似 すべき降伏何か私はMSVCので、私はそれをテストすることはできません近くのインストールがありません

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

、しかし:あなたは以下を使用することができ、MSVCのsuckinessについてVOOさんのコメントに対処するために

私は上記の投稿。次に、が本当にに__m128iが必要な場合は、castを2つの半分にすることができます。

+0

このソリューションの問題点は、SSEレジスタからのロードとSSEレジスタへの格納に多くの時間がかかることです。 – jk4736

+0

そのコードが有効であるかどうかにかかわらず、コンパイラに大きく依存します。 MSVCの下にないことは間違いありません - __m128は小さなプリミティブの集合体の単なる構造体です。 – Voo

+0

@ jk4736私のスニペットのすべてのレジスタは、非SSEの64ビット幅のものです... – CAFxX

関連する問題