"かなり遅い"というのではなく、あなたの既存のアプローチは実際には合理的です。
確かに個々のテストは、あなたが一般的な目的での結果は、登録したい場合は、通常movmskb
、例えば(とにかくその移動のための3サイクルのレイテンシを支払うとしている4サイクルのレイテンシーを持っていますが、それ3のレイテンシもある)。いずれにしても、8つのレジスタをテストする必要があります。それぞれのレジスタがほとんど独立しているため、単純にレイテンシを追加するわけではありません。したがって、uopのカウントとポートの使用は、他の作業と重複します。
インテルのハードウェアで少し速いアプローチは、連続したPCMPEQ
命令を使用して複数のベクタをテストし、結果をまとめてフォールディングすることです(たとえば、PCMPEQQを使用すると効果的に4クワッドワードの結果が得られますそれらを1に折り畳む。 PCMPEQ
の前後に折り畳むことができますが、結果がより良くなる方法や場所についてもっと知ることができます。ここでは、xmm1-8
がxmm0
と仮定された8つのレジスタのテストされていないスケッチがあり、xmm14
はpblendvb
のマスクで、最後の命令で使用された代替バイトを選択します。
# 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件の結果を与え、ゼロに対する各xmm
各QWORD
をテストすることで、あなたは(持つために、バイトごとに結果で終わるxmm10
に一緒に結果をブレンドすべての低QWORD結果の前にすべての高QWORD結果)。次に、これらの16バイトのマスクを16ビットとしてmovmskb
でeax
に移動し、最後にの各レジスタの上位ビットと下位ビットを組み合わせます。
これは、合計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サイクルvpblendvb
とor al,ah
の部分書き込みペナルティを回避することにより、2サイクルを節約し、それその命令の結果を直ちに使用する必要がない場合は遅い依存関係を修正します(vpmovmskb
)。
実際にはそれが2であるように思わことを、私はまた、あなたがrcl eax, 1
のためにリストされた1サイクルのレイテンシについてはよく分からない前PTEST
は、3サイクルのレイテンシーを持っていることだけSkylakeマイクロアーキテクチャ上のようですAgnerによると、最新のインテルでは、3μsと2サイクルのレイテンシ/レシピスループットのようです。
希望の出力は何ですか?あなたは値が0でないビットマップを持っていますか?または、レジスタが0でないかどうかを知ることで十分ですか? – fuz
もちろん、多くは周囲のコードに依存しています - ループ内でゼロを比較するレジスタの値の数と、その結果で何をしたいですか?出力形式に制限がなければ、レジスタがゼロかどうかをチェックする "最速の"方法は、 'PCMPEQ'命令の1つを使用して0のレジスタと比較することですが、結果をあなたが望む形式で残すことはできません。 .. ** Added:** fuzが分かりました:) – BeeOnRope
このような行でいくつのレジスタをテストしますか? – BeeOnRope