Aşağıdaki tüm talimatlar aynı şeyi yapar: %eax
sıfıra ayarlayın . En uygun yol hangisidir (en az makine döngüsü gerektirir)?
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
Aşağıdaki tüm talimatlar aynı şeyi yapar: %eax
sıfıra ayarlayın . En uygun yol hangisidir (en az makine döngüsü gerektirir)?
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
Yanıtlar:
TL; DR özeti : tüm CPU'lar içinxor same, same
en iyi seçimdir . Başka hiçbir yöntemin ona göre avantajı yoktur ve en azından diğer yöntemlere göre bir avantajı vardır. Resmi olarak Intel ve AMD tarafından tavsiye edilir ve derleyiciler ne yapar. 64 bit modunda, hala kullanmak xor r32, r32
çünkü 32 bit reg sıfır üst 32 yazma . xor r64, r64
bayt israfıdır çünkü REX önekine ihtiyaç duyar.
Bundan daha da kötüsü, Silvermont xor r32,r32
64 bit işlenen boyutunu değil, yalnızca ayırma olarak kabul eder. Bu nedenle , r8..r15'i sıfırladığınız için bir REX öneki hala gerekli olsa bile, kullanın xor r10d,r10d
, değilxor r10,r10
.
GP tamsayı örnekleri:
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
Bir vektör yazmacının sıfırlanması genellikle en iyi şekilde yapılır pxor xmm, xmm
. Bu genellikle gcc'nin yaptığı şeydir (FP talimatlarıyla kullanılmadan önce bile).
xorps xmm, xmm
mantıklı olabilir. Bir bayt daha kısadır pxor
, ancak xorps
Intel Nehalem'de yürütme bağlantı noktası 5'e ihtiyaç duyarken pxor
herhangi bir bağlantı noktasında (0/1/5) çalışabilir. (Nehalem'in tamsayı ile FP arasındaki 2c baypas gecikme gecikmesi genellikle alakalı değildir, çünkü sıra dışı yürütme tipik olarak yeni bir bağımlılık zincirinin başlangıcında bunu gizleyebilir).
SnB ailesi mikro mimarilerinde, xor-sıfırlamanın hiçbir çeşidi bir yürütme portuna bile ihtiyaç duymaz. AMD günü ve P6 / Core2 Intel, Nehalem-öncesi xorps
ve pxor
(vektör-tamsayı talimatları gibi) aynı şekilde ele alınır.
128b vektör talimatının AVX sürümünü kullanmak, reg'in üst kısmını da sıfırlar, bu nedenle vpxor xmm, xmm, xmm
YMM (AVX1 / AVX2) veya ZMM (AVX512) veya gelecekteki herhangi bir vektör uzantısını sıfırlamak için iyi bir seçimdir. vpxor ymm, ymm, ymm
kodlamak için fazladan bayt gerektirmez ve Intel'de aynı şekilde çalışır, ancak Zen2'den önce AMD'de daha yavaş çalışır (2 uops). AVX512 ZMM sıfırlama fazladan bayt gerektirir (EVEX öneki için), bu nedenle XMM veya YMM sıfırlama tercih edilmelidir.
XMM / YMM / ZMM örnekleri
# 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.
Bkz Is-vxorps sıfırlama hızlı YMM daha xmm kayıtları ile AMD Jaguar / Bulldozer / Zen üzerinde? ve
Knights Landing'de bir veya birkaç ZMM kaydını silmenin en etkili yolu nedir?
Yarı related: hızlı yolu tüm BİR bite __m256 değerini ayarlamak için ve
1 CPU kayıt Seti tüm bit verimli da AVX512 kapsayan k0..7
maske kayıtlarını. SSE / AVX vpcmpeqd
birçoğunda yıkılıyor (1'leri yazmak için hala bir uop'a ihtiyaç duysa da), ancak vpternlogd
ZMM regs için AVX512 bozulmuyor bile. Bir döngü içinde, özellikle AVX512 ile ALU uop ile olanları yeniden oluşturmak yerine başka bir kayıttan kopyalamayı düşünün.
Ancak sıfırlama ucuzdur: Bir döngü içinde bir xmm regini xor-sıfırlamak, vektör regs için mov-eliminasyonu olan ancak yine de xor için sıfır yazmak için bir ALU uop'a ihtiyaç duyan bazı AMD CPU'ları (Buldozer ve Zen) hariç, kopyalama kadar iyidir. -zeroing.
Bazı CPU'lar sub same,same
sıfırlama deyimi gibi algılar xor
, ancak sıfırlama deyimlerini tanıyan tüm CPU'lar tanırxor
. Sadece kullanmak xor
Eğer CPU deyim sıfırlanması hangi tanır hangi endişe zorunda kalmamak.
xor
(aksine, tanınan bir sıfırlama deyimi olmanın mov reg, 0
) bazı bariz ve bazı ince avantajları vardır (özet listesi, sonra bunları genişleteceğim):
mov reg,0
. (Tüm CPU'lar)Daha küçük makine kodu boyutu (5 yerine 2 bayt) her zaman bir avantajdır: Daha yüksek kod yoğunluğu, daha az talimat önbelleği eksikliğine ve daha iyi komut getirme ve potansiyel olarak bant genişliğini çözme olasılığına yol açar.
Intel SnB ailesi mikro mimarilerinde xor için bir yürütme birimi kullanmamanın yararı küçüktür, ancak güç tasarrufu sağlar. Yalnızca 3 ALU yürütme portuna sahip olan SnB veya IvB'de önemli olma olasılığı daha yüksektir. Haswell ve daha sonra, tamsayı ALU komutlarını işleyebilen 4 yürütme bağlantı noktasına sahiptir mov r32, imm32
; bu nedenle, zamanlayıcı tarafından mükemmel karar verme ile (bu, pratikte her zaman gerçekleşmez), HSW, tümü ALU'ya ihtiyaç duysa bile saat başına 4 uops sürdürebilir. yürütme bağlantı noktaları.
Daha fazla ayrıntı için kayıtları sıfırlamakla ilgili başka bir soruya cevabıma bakın.
Bruce Dawson'ın Michael Petch'in bağlantılı olduğu blog yazısı (soruya ilişkin bir yorumda) xor
, bunun bir yürütme birimine ihtiyaç duymadan kayıt yeniden adlandırma aşamasında ele alındığına işaret ediyor (kaynaşmayan etki alanında sıfır uop), ancak hala bir uop olduğu gerçeğini gözden kaçırdı kaynaşmış alanda. Modern Intel CPU'lar saat başına 4 birleşik etki alanı üretebilir ve kullanımdan kaldırabilir. Saat sınırı başına 4 sıfır buradan geliyor. Kayıt yeniden adlandırma donanım Artan karmaşıklığı sadece 4. tasarımın genişliğini sınırlayan nedenlerinden biri (Bruce üzerine seri gibi bazı çok mükemmel blog postaları yazmıştır olan FP matematik ve x87 / SSE / yuvarlama konularda yapmam, tavsiye ederim).
AMD Bulldozer ailesi CPU'larda , mov immediate
aynı EX0 / EX1 tamsayı yürütme bağlantı noktalarında çalışır xor
. mov reg,reg
AGU0 / 1 üzerinde de çalışabilir, ancak bu sadece kayıt kopyalama içindir, hemen ayar yapmak için değildir. AMD Yani AFAIK, tek avantajı xor
üzerinde mov
kısa kodlamasıdır. Fiziksel kayıt kaynaklarını da kurtarabilir, ancak herhangi bir test görmedim.
Tanınan sıfırlama deyimleri , kısmi kayıtları tam kayıtlardan (P6 ve SnB aileleri) ayrı olarak yeniden adlandıran Intel CPU'larda kısmi kayıt cezalarını önler.
xor
olacaktır üst kısımları sıfırlanmasını sahip olarak kayıt etiketi , böylece xor eax, eax
/ inc al
/ inc eax
ön IVB CPU'lar olduğunu zamanki kısmi kayıt ceza engeller. xor
Olmasa bile , IvB yalnızca yüksek 8 bitler ( AH
) değiştirildiğinde ve ardından tüm kayıt okunduğunda bir birleştirme işlemine ihtiyaç duyar ve Haswell bunu bile kaldırır.
Agner Fog'un microarch kılavuzundan, s. 98 (Pentium M bölümü, SnB dahil olmak üzere sonraki bölümlerde referans alınmıştır):
İşlemci, bir kütüğün XOR'unu kendisi ile onu sıfıra ayarlıyor olarak tanır. Kayıttaki özel bir etiket, EAX = AL olacak şekilde yazmacın yüksek kısmının sıfır olduğunu hatırlar. Bu etiket bir döngüde bile hatırlanır:
; 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'den): İşlemci, bir kesme, yanlış tahmin veya başka bir serileştirme olayı almadığınız sürece EAX'in üst 24 bitinin sıfır olduğunu hatırlar.
Ayrıca kılavuz onaylar ait pg82 mov reg, 0
edilir değil erken P6 üzerinde en azından bir sıfırlama deyim olarak kabul PIII veya PM gibi tasarlar. Transistörleri daha sonraki CPU'larda algılamak için harcadılarsa çok şaşırırdım.
xor
bayrakları ayarlar , bu da koşulları test ederken dikkatli olmanız gerektiği anlamına gelir. Yana setcc
bir 8bit hedefiyle maalesef yalnızca , genellikle kısmi kayıt ceza almamak için dikkat çekmek gerekir.
X86-64 setcc r/m
, 16/32/64 bit için kaldırılan işlem kodlarından birini (AAM gibi) , r / m alanının kaynak-kayıt 3-bit alanında kodlanmış (yol diğer bazı tek işlenen talimatlar bunları işlem kodu bitleri olarak kullanır). Ama bunu yapmadılar ve bu x86-32 için yardımcı olmazdı.
İdeal olarak, xor
/ bayrakları ayarlamanız / setcc
/ tam kaydı okumalısınız:
...
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
Bu, tüm CPU'larda optimum performansa sahiptir (takılma, birleşme veya yanlış bağımlılıklar yok).
Bir bayrak belirleme talimatından önce xveya yapmak istemediğinizde işler daha karmaşık hale gelir . örneğin, bir koşulda dallanmak ve ardından aynı bayraklardan başka bir koşula setcc yapmak istiyorsunuz. örneğin cmp/jle
, sete
ya yedek bir kaydınız yok ya da xor
alınmayan kod yolunu tamamen uzak tutmak istiyorsunuz .
Bayrakları etkilemeyen tanınan sıfırlama deyimleri yoktur, bu nedenle en iyi seçim hedef mikro mimariye bağlıdır. Core2'de, bir birleştirme uopunun takılması 2 veya 3 döngüde durmaya neden olabilir. SnB'de daha ucuz görünüyor, ancak ölçmek için fazla zaman harcamadım. mov reg, 0
/ Kullanmak setcc
eski Intel CPU'larda önemli bir cezaya neden olur ve daha yeni Intel'de hala biraz daha kötüdür.
Kullanılması setcc
/ movzx r32, r8
muhtemelen Intel P6 ve SNB aileler için en iyi alternatiftir eğer işaret-ayarlama öğretim değil xor sıfır önde can. Bu, x veya sıfırlamadan sonra testi tekrar etmekten daha iyi olmalıdır. ( sahf
/ lahf
Veya pushf
/'yi düşünmeyin bile popf
). IvB ortadan kaldırabilir movzx r32, r8
(yani, xor-sıfırlama gibi yürütme birimi veya gecikme olmaksızın kayıt yeniden adlandırma ile başa çıkabilir ). Haswell ve daha sonra sadece düzenli ortadan kaldırmak mov
talimatları, bu nedenle movzx
bir yürütme birimi alır ve sıfır olmayan gecikme, yapım testi vardır / setcc
/ movzx
daha kötü xor
/ test / setcc
, ama yine de en azından olarak test / mal olarak mov r,0
/ setcc
(ve çok daha iyi eski CPU'lar üzerine).
Öncelikle sıfırlama olmadan setcc
/ kullanmak movzx
, AMD / P4 / Silvermont'ta kötüdür çünkü alt kayıtlar için ayrı ayrı dep'leri takip etmezler. Yazıcının eski değerine dair yanlış bir açıklama olacaktır. Kullanılması mov reg, 0
/ setcc
/ bağımlılık-kırılmasını sıfırlanması için muhtemelen en iyi alternatiftir xor
/ test / setcc
bir seçenek değildir.
Tabii ki, setcc
çıktısının 8 bitten daha geniş olmasına ihtiyacınız yoksa, hiçbir şeyi sıfırlamanıza gerek yoktur. Ancak, yakın zamanda uzun bir bağımlılık zincirinin parçası olan bir kayıt seçerseniz, P6 / SnB dışındaki CPU'lara yanlış bağımlılıklara dikkat edin. (Ayrıca, kullanmakta olduğunuz kaydı kaydedebilecek / geri yükleyebilecek bir işlev çağırırsanız, kısmi bir kayıt durmasına veya fazladan uop'a neden olmaktan kaçının.)
and
hemen sıfır olması, bildiğim herhangi bir CPU'daki eski değerden bağımsız olarak özel bir kasaya sahip değildir, bu nedenle bağımlılık zincirlerini kırmaz. Hiçbir avantajı xor
ve birçok dezavantajı yoktur.
Eğer zaman sadece microbenchmarks yazmak için yararlıdır istediğiniz bir gecikme testin parçası olarak bir bağımlılık ama sıfırlama ve ekleyerek bilinen bir değer yaratmak istiyoruz.
Bkz http://agner.org/optimize/ microarch detayları için sıfırlama deyimler bağımlılık kırma olarak kabul edildiği de dahil olmak üzere (örneğin sub same,same
ederken, bazı tümünü değil CPU'lar üzerinde xor same,same
tümü üzerinde kabul edilmektedir.) mov
Eski değerine bağımlılık zincirini kırmak yok kaydın (kaynak değerine bakılmaksızın, sıfır olsun ya da olmasın, çünkü böyle mov
çalışır). xor
yalnızca src ve dest'in aynı yazmaç olduğu özel durumda bağımlılık zincirlerini kırar, bu nedenle özel olarak tanınan bağımlılık kesiciler mov
listesinin dışında bırakılır . (Ayrıca, sıfırlama deyimi olarak tanınmadığı için, diğer faydaları da beraberinde gelir.)
İlginç bir şekilde, en eski P6 tasarımı (Pentium III aracılığıyla PPRO) vermedi tanımak xor
sadece kısmi-kayıt tezgahlarda engelleme amacıyla bir sıfırlama deyim olarak, bir bağımlılık-kesici olarak -zeroing kullanarak bu nedenle bazı durumlarda değdi, hem mov
sonra ve xor
Dep'i kırmak için bu sırayla sıfırlama ve ardından tekrar sıfırlama + dahili etiket bitini yüksek bitlerin sıfır olacağı şekilde ayarlayın, böylece EAX = AX = AL.
Agner Fog Örneği 6.17'ye bakın. microarch pdf'inde. Bunun P2, P3 ve hatta (erken mi?) PM için de geçerli olduğunu söylüyor. Bağlantılı blog gönderisine yapılan bir yorum, bu denetime sahip olanın yalnızca PPro olduğunu söylüyor, ancak Katmai PIII ve @Fanael'i bir Pentium M üzerinde test ettim ve ikimiz de gecikme için bir bağımlılığı bozmadığını gördük. bağlı imul
zincir. Bu, ne yazık ki Agner Fog'un sonuçlarını doğruluyor.
Kodunuzu gerçekten daha güzel hale getiriyorsa veya talimatları kaydediyorsa, mov
kod boyutundan başka bir performans sorunu oluşturmadığınız sürece, bayraklara dokunmaktan kaçınmak için sıfırlayın . İşaretleri patlatmaktan kaçınmak, kullanmamanın tek mantıklı nedenidir xor
, ancak bazen yedek bir kaydınız varsa bayrakları ayarlayan şeyin önünde x veya sıfır yapabilirsiniz.
mov
-zero önde, setcc
gecikme için movzx reg32, reg8
sonrasına göre daha iyidir (farklı yazmaçları seçebileceğiniz Intel'de hariç), ancak daha kötü kod boyutu.
mov reg, src
OO CPU'ları için dep zincirlerini de kırıyor (src'nin imm32 [mem]
veya başka bir kayıt olmasına bakılmaksızın ). Optimizasyon kılavuzlarında bu bağımlılıktan söz edilmez çünkü bu sadece src ve dest aynı register olduğunda meydana gelen özel bir durum değildir. Her zaman hedeflerine bağlı olmayan talimatlar için olur. (Intel'in popcnt/lzcnt/tzcnt
hedefe sahte bir depoya sahip olma uygulaması hariç )
mov
bedava değil , sadece sıfır gecikme. "Bir yürütme portu almama" kısmı genellikle önemli değildir. Kaynaştırılmış alan verimi, özellikle darboğaz olabilir. karışımda yükler veya depolar olan.
xor r64, r64
bir bayt israf etmiyor. Dediğiniz gibi xor r32, r32
özellikle KNL ile en iyi seçim. Daha fazlasını okumak istiyorsanız, bu mikro işlem kılavuzunun 15.7 "Özel bağımsızlık durumları" bölümüne bakın.