2017-11-14 6 views
3

Intelの組み込み関数を使用して、複数の単精度演算を並列に実行するアルゴリズムを作成しました。私のアルゴリズムの各繰り返しの結果は、単一の256ビットベクトル(__m256)の非ゼロエントリの数です。例えば__mm256ベクトルの非ゼロエントリの数を数える最速の方法は何ですか?

反復の結果は、4

ベクトル内の数の非ゼロのエントリをカウントするための最速の方法は何である

00000000 FFFFFFFF 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF 

float results[8]; 
_mm256_storeu_ps(results, result_vector); 

int count = 0; 
for (uint32_t idx = 0; idx < 8; ++idx) 
{ 
    if (results[idx] != 0) 
    {    
     ++count; 
    } 
} 

このアプローチはうまく動作しますが、おそらく、店を伴わないもの、それを行うには、より効率的な方法があれば、私は疑問に思う:

は現在、私はこのような何かをやっています。

+0

ゼロ以外のエントリは「0xFFFFFFFF」であることが保証されていますか?もしそうなら、1つのアイデアは、各32ビットセクションの最下位ビットを分離するためにマスクをANDにしてから、絶対差の和を適用することです。 – njuffa

+3

または単にゼロ( '_mm256_cmp_ps')と比較し、ビットマスク(' _mm256_movemask_ps')を抽出し、 'popcnt'を使ってビットを数えますか? 3つの指示。 –

+2

すでに0/0xFFF ...(比較結果)の場合は、 'cmpps'のステップをスキップしてmovemask/popcntだけを実行することができます。 –

答えて

6

ハードウェアpopcnt命令がここに最善の賭けです。これは速く、vmovmskpsは整数ビットマスクとして各要素の上位ビットを与えるのに非常に効率的です。 (compare/movemaskは、ベクトル比較結果を分岐する標準的な方法です。index a lookup table of shuffle masksに使用してください)。

movemask/popcnt when left-packingは、保存先の要素数(シャッフル後)で宛先ポインタをインクリメントするのに便利です。

#include <immintrin.h> 

// use only with compare-results. 
// or to count elements with their sign-bit set 
unsigned count_true(__m256 v) { 
    unsigned mask = _mm256_movemask_ps(v); 
    return _mm_popcnt_u32(mask); 
} 

popcnt AVXとは別の機能ビットを持っているので、理論的にはCPU(または仮想マシン)があるかもしれませんAVXではなく、ハードウェアpopcntが、実際に私はそれについて心配しないでしょう。あなたが何かのためのベクトルレジスタに結果をしたい場合でも、vmovmskps/POPCNT/MOVDおそらく水平に追加するよりも良い配列(popcntはSSE4.2に導入され、AVXは、SSE4.2を意味した)


0/-1整数を含む要素が追加されます。それは3つのシャッフル/ステップを追加して8つの要素を1に減らし、負の合計を持つことになります。

比較結果を整数として扱うのは、0/-1が有用な場合があるため、ほとんどの場合これを言及します。例えばカウンタのベクトルを条件付きでインクリメントするには、cmpps/psubdがそのトリックを行います。 (0 + x = xなので、偽の要素は変更されません)

関連する問題