2017-02-18 14 views
4

PTESTはキャリーフラグには影響しませんが、(やや厄介な)ZFだけが設定されます。もCFとZFの両方に影響します。xmm/ymmレジスタがゼロであるかどうかをテストする高速な方法?

多くの値をテストするために次のシーケンスを考え出しましたが、実行時間が悪いと不満です。

   Latency/rThoughput 
setup: 
    xor eax,eax  ; na 
    vpxor xmm0,xmm0 ; na  ;mask to use for the nand operation of ptest 
work: 
    vptest xmm4,xmm0 ; 3 1 ;is xmm4 alive? 
    adc eax,eax  ; 1 1 ;move first bit into eax 
    vptest xmm5,xmm0 ; 3 1 ;is N alive? 
    adc eax,eax  ; 1 1 ;move consecutive bits into eax 

私は(明らかに、私は複数のレジスタに複数のビットマップを組み合わせることができます)eaxにすべての非ゼロレジスタのビットマップを持っていると思います。

したがって、すべてのテストのレイテンシは3 + 1 = 4サイクルです。
このうちいくつかは、eax,ecxなどの間で交互に実行できます。
しかし、それでもまだかなり遅いです。
これはもっと速い方法ですか?

8 xmm/ymmレジスタを一列に並べてテストする必要があります。 1バイトのビットマップでレジスタごとに1ビット。

+1

希望の出力は何ですか?あなたは値が0でないビットマップを持っていますか?または、レジスタが0でないかどうかを知ることで十分ですか? – fuz

+0

もちろん、多くは周囲のコードに依存しています - ループ内でゼロを比較するレジスタの値の数と、その結果で何をしたいですか?出力形式に制限がなければ、レジスタがゼロかどうかをチェックする "最速の"方法は、 'PCMPEQ'命令の1つを使用して0のレジスタと比較することですが、結果をあなたが望む形式で残すことはできません。 .. ** Added:** fuzが分かりました:) – BeeOnRope

+0

このような行でいくつのレジスタをテストしますか? – BeeOnRope

答えて

6

"かなり遅い"というのではなく、あなたの既存のアプローチは実際には合理的です。

確かに個々のテストは、あなたが一般的な目的での結果は、登録したい場合は、通常movmskb、例えば(とにかくその移動のための3サイクルのレイテンシを支払うとしている4サイクルのレイテンシーを持っていますが、それ3のレイテンシもある)。いずれにしても、8つのレジスタをテストする必要があります。それぞれのレジスタがほとんど独立しているため、単純にレイテンシを追加するわけではありません。したがって、uopのカウントとポートの使用は、他の作業と重複します。

インテルのハードウェアで少し速いアプローチは、連続したPCMPEQ命令を使用して複数のベクタをテストし、結果をまとめてフォールディングすることです(たとえば、PCMPEQQを使用すると効果的に4クワッドワードの結果が得られますそれらを1に折り畳む。 PCMPEQの前後に折り畳むことができますが、結果がより良くなる方法や場所についてもっと知ることができます。ここでは、xmm1-8xmm0と仮定された8つのレジスタのテストされていないスケッチがあり、xmm14pblendvbのマスクで、最後の命令で使用された代替バイトを選択します。

# test the 2 qwords in each vector against zero 
vpcmpeqq xmm11, xmm1, xmm0 
vpcmpeqq xmm12, xmm3, xmm0 
vpcmpeqq xmm13, xmm5, xmm0 
vpcmpeqq xmm14, xmm7, xmm0 

# blend the results down into xmm10 word origin 
vpblendw xmm10, xmm11, xmm12, 0xAA # 3131 3131 
vpblendw xmm13, xmm13, xmm14, 0xAA # 7575 7575 
vpblendw xmm10, xmm10, xmm13, 0xCC # 7531 7531 

# test the 2 qwords in each vector against zero 
vpcmpeqq xmm11, xmm2, xmm0 
vpcmpeqq xmm12, xmm4, xmm0 
vpcmpeqq xmm13, xmm6, xmm0 
vpcmpeqq xmm14, xmm8, xmm0 

# blend the results down into xmm11 word origin 
vpblendw xmm11, xmm11, xmm12, 0xAA # 4242 4242 
vpblendw xmm13, xmm13, xmm14, 0xAA # 8686 8686 
vpblendw xmm11, xmm11, xmm13, 0xCC # 8642 8642 

# blend xmm10 and xmm11 together int xmm100, byte-wise 
#   origin bytes 
# xmm10 77553311 77553311 
# xmm11 88664422 88664422 
# res 87654321 87654321 
vpblendvb xmm10, xmm10, xmm11, xmm15 

# move the mask bits into eax 
vpmovmskb eax, xmm10 
and al, ah 

直感を使用すると、8つのレジスタのための16件の結果を与え、ゼロに対する各xmmQWORDをテストすることで、あなたは(持つために、バイトごとに結果で終わるxmm10に一緒に結果をブレンドすべての低QWORD結果の前にすべての高QWORD結果)。次に、これらの16バイトのマスクを16ビットとしてmovmskbeaxに移動し、最後にの各レジスタの上位ビットと下位ビットを組み合わせます。

これは、合計8個のレジスタで16 uopsのように見えるので、レジスタあたり約2個です。大部分が「減少」タイプの並列ツリーであるため、総レイテンシは妥当である。制限要因は、すべて最新のインテルのポート5のみになる6 vpblendw操作です。それらのうちの4つをVPBLENDDに置き換えてください。VPBLENDDは、p015のいずれかになる1つの「祝福された」ブレンドです。それは簡単であるはずです。

すべての操作は簡単で高速です。最後のand al, ahは部分的なレジスタ書込みですが、movの場合はeaxに入り、おそらく罰金はありません。もしもそれが問題であれば、最後の行にはいくつかの異なる方法があります...

このアプローチもymmレジスタに自然に縮尺が変わり、最後にeaxに若干異なる折り畳みがあります。

EDITわずかに速い2つの高価な命令を避けるために、シフトを詰め用途終了

A:

;combine bytes of xmm10 and xmm11 together into xmm10, byte wise 
; xmm10 77553311 77553311 
; xmm11 88664422 88664422 before shift 
; xmm10 07050301 07050301 
; xmm11 80604020 80604020 after shift 
;result 87654321 87654321 combined 
vpsrlw xmm10,xmm10,8 
vpsllw xmm11,xmm11,8 
vpor xmm10,xmm10,xmm11 

;combine the low and high dqword to make sure both are zero. 
vpsrldq xmm12,xmm10,64 
vpand xmm10,xmm12 
vpmovmskb eax,xmm10 

これは、2サイクルvpblendvbor al,ahの部分書き込みペナルティを回避することにより、2サイクルを節約し、それその命令の結果を直ちに使用する必要がない場合は遅い依存関係を修正します(vpmovmskb)。


実際にはそれが2であるように思わことを、私はまた、あなたがrcl eax, 1のためにリストされた1サイクルのレイテンシについてはよく分からない前PTESTは、3サイクルのレイテンシーを持っていることだけSkylakeマイクロアーキテクチャ上のようですAgnerによると、最新のインテルでは、3μsと2サイクルのレイテンシ/レシピスループットのようです。

+0

ああ、私はAMDのタイミングを見ていた、私は 'adc eax、eax'はより良い選択だと思う。 – Johan

+1

'pblendvb xmm10、xmm10、xmm11、xmm14'は' vpblendvb xmm0、xmm10、xmm11、xmm14'でなければなりませんか? –

+1

はい、はい!実際、 'movmskb'は、' xmm0'ではなく 'xmm10'を使うことを意図していました。 – BeeOnRope

関連する問題