次のすべての命令は同じこと%eax
を行います。ゼロに設定します。最適な方法はどれですか(必要なマシンサイクルが最も少ない)。
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
次のすべての命令は同じこと%eax
を行います。ゼロに設定します。最適な方法はどれですか(必要なマシンサイクルが最も少ない)。
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
回答:
TL; DRの概要は:xor same, same
あるすべてのCPUに最適。他の方法にはそれ以上の利点はなく、他の方法に比べて少なくともいくつかの利点があります。これはIntelとAMDによって公式に推奨されており、コンパイラーが何を行うかを示しています。64ビットモードでも、32ビットのレジスタを書き込むと上位32がゼロになるxor r32, r32
ため、引き続きを使用します。 REXプレフィックスが必要なため、バイトの無駄です。xor r64, r64
さらに悪いことに、Silvermont xor r32,r32
は64ビットのオペランドサイズではなく、dep-breakingとしてのみ認識します。したがって、r8..r15をゼロにしているためにREXプレフィックスが必要な場合でもxor r10d,r10d
、ではなくを使用しますxor r10,r10
。
GP整数の例:
xor eax, eax ; RAX = 0. Including AL=0 etc.
xor r10d, r10d ; R10 = 0
xor edx, edx ; RDX = 0
; small code-size alternative: cdq ; zero RDX if EAX is already zero
; SUB-OPTIMAL
xor rax,rax ; waste of a REX prefix, and extra slow on Silvermont
xor r10,r10 ; bad on Silvermont (not dep breaking), same as r10d everywhere else because a REX prefix is still needed for r10d or r10.
mov eax, 0 ; doesn't touch FLAGS, but not faster and takes more bytes
and eax, 0 ; false dependency. (Microbenchmark experiments might want this)
sub eax, eax ; same as xor on most but not all CPUs; bad on Silvermont for example.
xor al, al ; false dep on some CPUs, not a zeroing idiom. Use xor eax,eax
mov al, 0 ; only 2 bytes, and probably better than xor al,al *if* you need to leave the rest of EAX/RAX unmodified
ベクトルレジスタのゼロ化は通常、を使用して行うのが最適pxor xmm, xmm
です。これは通常、gccが行うことです(FP命令で使用する前でも)。
xorps xmm, xmm
理にかなっています。これは1バイトより短いですがpxor
、xorps
Intel Nehalemの実行ポート5 が必要ですpxor
が、任意のポート(0/1/5)で実行できます。(Nehalemの整数とFP間の2cバイパス遅延レイテンシは通常、関係ありません。アウトオブオーダー実行は通常、新しい依存関係チェーンの開始時にそれを隠すことができるためです)。
SnBファミリのマイクロアーキテクチャでは、xor-zeroingのどちらのフレーバーも実行ポートを必要としません。AMD、プリNehalemのP6 / Core2のインテルに、xorps
及びpxor
(ベクトル整数命令など)と同じ方法で処理されます。
128bベクトル命令のAVXバージョンを使用すると、regの上部もゼロになるのでvpxor xmm, xmm, xmm
、YMM(AVX1 / AVX2)またはZMM(AVX512)、または将来のベクトル拡張をゼロにするのに適しています。 vpxor ymm, ymm, ymm
ただし、エンコードに余分なバイトを必要とせず、Intelでは同じように実行されますが、Zen2より前のAMDでは遅くなります(2 uops)。AVX512 ZMMゼロ化には追加のバイト(EVEXプレフィックス用)が必要になるため、XMMまたはYMMゼロ化をお勧めします。
XMM / YMM / ZMMの例
# Good:
xorps xmm0, xmm0 ; smallest code size (for non-AVX)
pxor xmm0, xmm0 ; costs an extra byte, runs on any port on Nehalem.
xorps xmm15, xmm15 ; Needs a REX prefix but that's unavoidable if you need to use high registers without AVX. Code-size is the only penalty.
# Good with AVX:
vpxor xmm0, xmm0, xmm0 ; zeros X/Y/ZMM0
vpxor xmm15, xmm0, xmm0 ; zeros X/Y/ZMM15, still only 2-byte VEX prefix
#sub-optimal AVX
vpxor xmm15, xmm15, xmm15 ; 3-byte VEX prefix because of high source reg
vpxor ymm0, ymm0, ymm0 ; decodes to 2 uops on AMD before Zen2
# Good with AVX512
vpxor xmm15, xmm0, xmm0 ; zero ZMM15 using an AVX1-encoded instruction (2-byte VEX prefix).
vpxord xmm30, xmm30, xmm30 ; EVEX is unavoidable when zeroing zmm16..31, but still prefer XMM or YMM for fewer uops on probable future AMD. May be worth using only high regs to avoid needing vzeroupper in short functions.
# Good with AVX512 *without* AVX512VL (e.g. KNL / Xeon Phi)
vpxord zmm30, zmm30, zmm30 ; Without AVX512VL you have to use a 512-bit instruction.
# sub-optimal with AVX512 (even without AVX512VL)
vpxord zmm0, zmm0, zmm0 ; EVEX prefix (4 bytes), and a 512-bit uop. Use AVX1 vpxor xmm0, xmm0, xmm0 even on KNL to save code size.
AMD Jaguar /ブルドーザー/ Zenのvxorps-zeroingは、ymmよりもxmmレジスタの方が高速ですか?を参照してください。そして
騎士ランディングに単一または少数ZMMレジスタをクリアするための最も効率的な方法は何ですか?
準関連:__m256値をすべてのONEビットに
設定し、CPUレジスタのすべてのビットを1に効率的に設定する最速の方法は、AVX512 k0..7
マスクレジスタも効率的にカバーします。SSE / AVX vpcmpeqd
は多くの点vpternlogd
で重大な問題を解決しています(ただし、1を書き込むためにuop がまだ必要です)が、ZMM regのAVX512 は重大な問題を解決していません。ループ内では、ALV uopを使用して、特にAVX512でレジスタを再作成する代わりに、別のレジスタからコピーすることを検討してください。
しかし、ゼロ化は安価です:ループ内のxmmレジスターのxorゼロ化は、ベクトルregのmov-eliminationを備えているが、xorのゼロを書き込むためにALU uopを必要とする一部のAMD CPU(BulldozerおよびZen)を除いて、通常はコピーと同じです。 -ゼロ化。
いくつかのCPUは認識sub same,same
のようなゼロイディオムとしてxor
、しかし、任意のゼロイディオムを認識し、すべてのCPUが認識しますxor
。xor
どのCPUがどのゼロ化イディオムを認識するかを気にする必要がないように使用してください。
xor
(認識されたゼロ化イディオムであることは、とは異なりますmov reg, 0
)いくつかの明白で微妙な利点があります(要約リスト、次にそれらを拡張します):
mov reg,0
。(すべてのCPU)マシンコードサイズを小さくする(5バイトではなく2バイトにする)ことは常に利点です。コード密度が高いほど、命令キャッシュミスが少なくなり、命令フェッチが改善され、帯域幅がデコードされる可能性があります。
Intel SnBファミリマイクロアーキテクチャでxorの実行ユニットを使用しない利点はわずかですが、電力を節約できます。3つのALU実行ポートしかないSnBまたはIvBで問題になる可能性が高くなります。Haswellには、を含む整数ALU命令を処理できる4つの実行ポートがありmov r32, imm32
、スケジューラによる完全な意思決定(実際には常に発生するわけではありません)により、HSWはすべてがALUを必要とする場合でも、1クロックあたり4 uopsを維持できます。実行ポート。
詳細については、レジスタのゼロ化に関する別の質問に対する私の回答を参照してください。
Michael xor
Petchが(質問へのコメントで)リンクしたBruce Dawsonのブログ投稿は、実行ユニット(融合されていないドメインでのuops)を必要とせずに、register-renameステージで処理されることを指摘していますが、それはまだ1つのuopであるという事実を見落としました融合ドメイン内。最新のIntel CPUは、クロックごとに4つの融合ドメインuopsを発行および廃止できます。これが、クロックごとの4つのゼロ制限の由来です。レジスタの名前を変更するハードウェアの複雑さが増したことは、デザインの幅を4に制限する理由の1つにすぎません(ブルースは、FP演算とx87 / SSE /丸めの問題に関する彼のシリーズなど、非常に優れたブログ投稿をいくつか書いています。強くお勧めします)。
AMD BulldozerファミリCPUでは、mov immediate
と同じEX0 / EX1整数実行ポートで実行されますxor
。 mov reg,reg
AGU0 / 1でも実行できますが、これはレジスターのコピー専用であり、イミディエートからの設定用ではありません。AMD上の唯一の利点、私の知る限りではそれほどxor
オーバーがmov
短いエンコーディングです。物理的なレジスタリソースを節約することもできますが、テストは確認していません。
認識されたゼロ化イディオムは、完全なレジスター(P6およびSnBファミリー)とは別に部分的なレジスターの名前を変更するIntel CPUでの部分的なレジスターのペナルティを回避します。
xor
う上部がゼロ有するとしてレジスタにタグを付けるように、xor eax, eax
/ inc al
/ inc eax
プレIVB CPUを持っていることを、通常の部分レジスタペナルティを回避します。がなくてもxor
、IvBは、上位8ビット(AH
)が変更されてからレジスタ全体が読み取られ、Haswellがそれを削除したときにのみ、マージするuopを必要とします。
Agner Fogのmicroarchガイド、pg 98(Pentium Mセクション、SnBを含む後のセクションで参照)から:
プロセッサは、レジスタ自体のXORを、それをゼロに設定するものとして認識します。レジスター内の特別なタグは、レジスターの上位部分がゼロであるため、EAX = ALであることを記憶しています。このタグはループでも記憶されます:
; Example 7.9. Partial register problem avoided in loop xor eax, eax mov ecx, 100 LL: mov al, [esi] mov [edi], eax ; No extra uop inc esi add edi, 4 dec ecx jnz LL
(pg82から):割り込み、予測ミス、またはその他のシリアル化イベントが発生しない限り、プロセッサはEAXの上位24ビットがゼロであることを記憶しています。
そのガイドのpg82もそれmov reg, 0
が確認されています少なくともPIIIやPMなどの初期のP6設計では、ゼロ化の慣用句として認識されていないいます。彼らが後のCPUでそれを検出するためにトランジスタを費やしたとしたら、私は非常に驚きます。
xor
フラグを設定します。これは、条件をテストするときに注意する必要があることを意味します。setcc
は残念ながら8ビットの宛先でのみ使用できるので、通常、レジスタの部分的なペナルティを回避するように注意する必要があります。
x / 86-64が削除されたオペコードの1つ(AAMなど)を16/32/64ビットsetcc r/m
に転用し、述語がr / mフィールドのソースレジスタの3ビットフィールドにエンコードされていれば、すばらしいでしょう(方法他のいくつかの単一オペランド命令は、それらをオペコードビットとして使用します)。しかし、彼らはそれをしませんでした、そしてそれはとにかくx86-32のために助けにはなりません。
理想的には、xor
/フラグを設定する/ / setcc
レジスタ全体を読み取る:
...
call some_func
xor ecx,ecx ; zero *before* the test
test eax,eax
setnz cl ; cl = (some_func() != 0)
add ebx, ecx ; no partial-register penalty here
これにより、すべてのCPUで最適なパフォーマンスが得られます(ストール、uopsのマージ、または誤った依存関係はありません)。
フラグ設定命令の前にxorを実行したくない場合、状況はさらに複雑になります。たとえば、ある条件で分岐し、同じフラグから別の条件でsetccを実行したい場合などです。例:cmp/jle
、sete
そして、予備のレジスタがないか、またはxor
されていないコードパスを完全に除外し。
フラグに影響を与えない認識されたゼロ化イディオムはないため、最適な選択はターゲットのマイクロアーキテクチャーに依存します。Core2では、マージしているuopを挿入すると、2または3サイクルのストールが発生する可能性があります。SnBの方が安いようですが、測定に多くの時間を費やしませんでした。mov reg, 0
/ setcc
を使用すると、古いIntel CPUでは重大なペナルティが発生しますが、新しいIntelではやや劣ります。
フラグ設定命令の前にxor-zeroを実行できない場合は、setcc
/ を使用することmovzx r32, r8
がおそらくIntel P6&SnBファミリの最良の代替案です。これは、xorゼロ化の後にテストを繰り返すよりも優れているはずです。(sahf
/ lahf
またはpushf
/ も考慮しないでくださいpopf
)。IvBは排除できますmovzx r32, r8
(つまり、xor-zeroingのように、実行ユニットやレイテンシのないレジスタ名変更で処理します)。Haswell以降は通常のmov
命令のみを削除するためmovzx
、実行ユニットを取得し、レイテンシはゼロではないため、test / setcc
/ movzx
はxor
/ test / よりも劣りますsetcc
が、少なくともtest / mov r,0
/と同等ですsetcc
と同じくらい優れています(以前のCPUでははるかに優れています)。
AMD / P4 / Silvermontでは、最初にゼロ化なしでsetcc
/ movzx
を使用するのは好ましくありません。サブレジスタのdepを個別に追跡しないためです。レジスターの古い値に誤ったdepがあります。ゼロ化に/ を使用するmov reg, 0
/ setcc
依存関係を破壊することは、xor
/ test /setcc
がオプションではないの選択肢です。
もちろん、setcc
出力を8ビットより広くする必要がない場合は、何もゼロにする必要はありません。ただし、最近長い依存関係チェーンの一部となったレジスタを選択する場合は、P6 / SnB以外のCPUへの誤った依存関係に注意してください。(また、一部を使用しているレジスターを保存/復元する可能性のある関数を呼び出す場合、部分的なregストールや余分なuopを引き起こすことに注意してください。)
and
即時ゼロの場合は、私が認識しているCPUの古い値とは無関係であるため、特別なケースではないため、依存関係のチェーンが壊れることはありません。それ以上の利点はありませんxor
、多くの欠点があります。
レイテンシテストの一部として依存関係が必要であるが、ゼロ化および追加によって既知の値を作成する必要がある場合に、マイクロベンチマークを記述する場合にのみ役立ちます。
microarchの詳細については、http://agner.org/optimize/を参照してください。どのゼロ化イディオムが依存関係の破壊として認識されるsub same,same
か(たとえば、一部のCPUではすべてではないが、すべてのCPUではxor same,same
認識される)mov
は、古い値の依存性チェーンを破壊します。 レジスタの(ソース値に関係なく、ゼロかどうか、それがどのようにmov
機能するか)。 xor
srcとdestが同じレジスタである特別な場合にのみ依存チェーンを切断します。そのためmov
、特別に認識された依存関係ブレーカーのリストから除外されます。(また、ゼロ化のイディオムとして認識されないため、他の利点もあります。)
興味深いことに、最も古いP6デザイン(PPro からPentium III)は、xor
-zeroingを依存関係のブレーカーとして認識せず、部分的なレジスターストールを回避するためのゼロ化イディオムとしてのみ認識したため、場合によっては両方 mov
を使用する価値がありましたxor
+ depを解除するためにゼロ化してから再びゼロ化します。+内部タグビットを設定して、上位ビットがゼロになるようにし、EAX = AX = ALにします。
Agner Fogの例6.17を参照してください。彼のmicroarch pdfで。これはP2、P3、さらには(初期の?)PMにも当てはまると言います。 リンクされたブログ投稿へのコメントは、この見落としがあったのはPProだけだったと述べていますが、私はKatmai PIIIでテストし、@ FanaelはPentium Mでテストしました。バインドされたimul
チェーン。残念ながら、これはAgner Fogの結果を裏付けるものです。
コードがより良いものになるか、命令が保存mov
される場合は、コードサイズ以外のパフォーマンスの問題を引き起こさない限り、フラグに触れないようにゼロを設定してください。フラグを壊さないようにすることはxor
、を使用しない唯一の賢明な理由ですが、予備のレジスタがある場合は、フラグを設定する前にxor-zeroを実行できます。
mov
-zero before setcc
はレイテンシがmovzx reg32, reg8
後よりも良い(異なるレジスタを選択できるIntelを除く)が、コードサイズはより悪い。
mov reg, src
OO CPUのdepチェーンも壊れます(srcがimm32 、、[mem]
または別のレジスターであるかどうかに関係なく)。この依存関係の破れは、srcとdestが同じレジスタである場合にのみ発生する特別なケースではないため、最適化マニュアルでは言及されていません。それは彼らのdestに依存しない命令に対して常に起こります。(popcnt/lzcnt/tzcnt
destに誤ったdepがあるというIntelの実装を除く)
mov
無料ではなく、待ち時間はゼロです。通常、「実行ポートを使用しない」という部分は重要ではありません。特に、融合ドメインのスループットはボトルネックになりがちです。ミックス内のロードまたはストアで。
xor r64, r64
、バイトを無駄にするだけではありません。あなたが言うようxor r32, r32
に、特にKNLでは最良の選択です。詳細については、このmicrarchマニュアルのセクション15.7「独立の特殊なケース」を参照してください。