2017-08-30 7 views
2

_mm256_blendv_pd()は、位置63,127,191,255のビットを調べます。uint8_tの4つの下位ビットをAVXレジスタのこれらの位置に分散させる効率的な方法はありますか?4ビットのAVXレジスタ用マスクの作成方法は?

また、_mm256_cmp_pd()の結果のように、AVXレジスタの対応する64ビットコンポーネントで繰り返されるように、これらのビットをブロードキャストする効率的な方法はありますか?

命令セットはAVX2(他の機能が必要な場合はRyzen CPU)です。

+0

63,127,191,255は2の累乗ではないため、ビット位置を示すマスクにすることはできません。ビットベクトルへのインデックスであれば、少なくとも255ビットの対処が必要です。 'unit8_t'には8ビット(したがって '8')が含まれているので、255ビットを8ビットで表現できるかどうか尋ねていますか?それはそう思わない。あなたは意味のある答えを得る前に質問を修正する必要があります。 –

+0

オフ、バイナリ、64,128、および256 AREの2のべき乗なので、ビットマスクかもしれませんが、192はパターンに適合しません(それは64 + 128ですが、それは2ビットです)。 –

+1

@DaleWilson、これは256ビットのベクトルを処理するAVX(2)テクノロジに関する質問です。最初は 'uint8_t'に4ビットがあります。私は256ビットのAVXレジスタに対して、指定した位置(63,127,191,255はマスクではなく0ベースのビット位置)を指定位置に移動したいと考えています。 –

答えて

2

最も効率的な方法は、uint-8によってインデックスされた16個の256ビットエントリを含むルックアップベクトルを使用することです。

+0

これは良い解決策ですが、16 * 32 = 512バイトのキャッシュが必要です。 –

+1

I.多くのプロセッサ上に2つのキャッシュラインがあります。これらのキャッシュラインは読み込み専用になっていますので、ビットを256ビットに分配するのに必要なシフト、マスク、ORをコンパイルするまでには賭けていますベクタルックアップは時折キャッシュロードがかかる場合でも高速に実行されます。しかしもちろん、このタイプの質問では、「より速い」という唯一の真の答えはプロファイリングです。しかし、私が記述したアプローチは、コードの明快さと保守性について明確な勝者です。 –

+0

キャッシュラインは通常x86_64では64バイトなので、512バイトは8キャッシュラインです。 –

2

明白な解決策:これらの4ビットをルックアップテーブルのインデックスとして使用します。あなたはすでにそれを知っていたので、もう一度試してみましょう。

可変シフトベースのアプローチ:バイトをすべてのqwordにブロードキャストし、次にそれを{63,62,61,60}だけシフトして、msbの右のビットを一列に並べます。テストされていない、このようなもの:

_mm256_sllv_epi64(_mm256_set1_epi64x(mask), _mm256_set_epi64x(63, 62, 61, 60)) 

ボーナスとして、負荷はマスクに依存しないので、ループから持ち上げることができます。

これは、Ryzenの素晴らしいアイデアではありません。メモリからの256ビットのロードは、vpsllvq(それはRyzenのほとんどの256b演算のように2μopsです)よりも高いスループットを持ちますが、ここでもvmovq(そのバイトがベクタ・レジスタから来ない場合)、および幅が広いvpbroadcastq(2μops)

コンテキストによっては、行うこともできないこともあります。場合によります。

2

汎用レジスタにuint8_tが存在すると仮定すると、アプローチがある:

  1. 使用PDEP YMMの低い部分に4バイト(最上位ビット)
  2. 転送32ビットGPRから4バイトに4ビットを変換する
  3. レジスタ(代わりに値を入れビット63、127、191、255)

だから私は2つのバージョンを思い付いた - メモリとせずに、他の1と1:メモリと

アプローチ:

.data 
    ; Always use the highest bytes of a QWORD as target/128 means 'set ZERO' 
    ddqValuesDistribution: .byte 3,128,128,128,128,128,128,128, 2,128,128,128,128,128,128,128, 1,128,128,128,128,128,128,128, 0,128,128,128,128,128,128,128 
.code 
    ; Input value in lower 4 bits of EAX 
    mov  edx, 0b10000000100000001000000010000000 
    pdep eax, eax, edx 
    vmovd xmm0, eax 
    vpshufb ymm0, ymm0, ymmword ptr [ddqValuesDistribution] 

これはHaswellとSkylakeで5μOpsで出ます。メモリ変数なし


アプローチ(@Peterコルドへの改善されたおかげで):(!)

mov edx, 0b10000000100000001000000010000000 
    pdep eax, eax, edx 
    vmovd xmm0, eax 
    vpmovsxbq ymm0, xmm0 

この1つはハスウェルとSkylakeマイクロアーキテクチャ上の4つのuopで出てくると、さらに移動することによって改善することができます変数へのEDXのマスク。
出力は最初のバージョンとは異なります(すべてのビットが最上位ビットと比較されます)。

+0

@ zx485:Rydeのpdepは6ユーロです。したがって、これらのuopカウントはIntel CPUにのみ適用されます。 –

+0

'vpmovsxbq'を使って各バイトの符号ビットを各qwordの上位56ビットにコピーしてみてください。 –

+0

@PeterCordes:ありがとう。本当に素晴らしい提案。 PDEPがRyzenに悪いことをするのは残念です。 – zx485

関連する問題