วิธีใดเป็นวิธีที่ดีที่สุดในการตั้งค่ารีจิสเตอร์เป็นศูนย์ในแอสเซมบลี x86: xor, mov หรือและ?


120

คำแนะนำต่อไปนี้ทั้งหมดทำสิ่งเดียวกัน: ตั้งค่า%eaxเป็นศูนย์ วิธีใดเหมาะสมที่สุด (ต้องใช้รอบเครื่องน้อยที่สุด)

xorl   %eax, %eax
mov    $0, %eax
andl   $0, %eax

6
คุณอาจต้องการอ่านบทความนี้
Michael Petch

คำตอบ:


222

TL; DR สรุป : xor same, sameเป็นทางเลือกที่ดีที่สุดสำหรับซีพียูทั้งหมด ไม่มีวิธีอื่นใดที่มีข้อได้เปรียบเหนือกว่าวิธีนี้และอย่างน้อยก็มีข้อได้เปรียบเหนือวิธีอื่นใด แนะนำอย่างเป็นทางการโดย Intel และ AMD และสิ่งที่คอมไพเลอร์ทำ ในโหมด 64 บิตยังคงใช้xor r32, r32เพราะการเขียนแบบ 32 บิตศูนย์ reg บน 32 xor r64, r64เป็นการเสียไบต์เพราะต้องการคำนำหน้า REX

ที่แย่ไปกว่านั้น Silvermont ยอมรับxor r32,r32ว่าเป็นการทำลายล้างเท่านั้นไม่ใช่ขนาดตัวถูกดำเนินการ 64 บิต ดังนั้นแม้ว่าจะต้องใช้คำนำหน้า REX เนื่องจากคุณเป็นศูนย์ r8..r15 ให้ใช้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

Zeroing pxor xmm, xmmลงทะเบียนเวกเตอร์ที่ดีที่สุดมักจะทำด้วย โดยทั่วไปแล้วนั่นคือสิ่งที่ gcc ทำ (ก่อนใช้ด้วยคำแนะนำ FP)

xorps xmm, xmmสามารถทำให้รู้สึก สั้นกว่าหนึ่งไบต์pxorแต่xorpsต้องการพอร์ตการดำเนินการ 5 บน Intel Nehalem ในขณะที่pxorสามารถทำงานบนพอร์ตใดก็ได้ (0/1/5) (โดยปกติแล้วเวลาในการตอบสนองการหน่วงเวลาบายพาส 2c ของ Nehalem ระหว่างจำนวนเต็มและ FP จะไม่เกี่ยวข้องเนื่องจากการดำเนินการที่ไม่อยู่ในใบสั่งมักจะซ่อนไว้ที่จุดเริ่มต้นของห่วงโซ่การพึ่งพาใหม่)

สำหรับสถาปัตยกรรมขนาดเล็กตระกูล SnB ทั้งรสชาติของ xor-zeroing ไม่จำเป็นต้องมีพอร์ตการประมวลผล บน AMD และ pre-Nehalem P6 / Core2 Intel xorpsและpxorได้รับการจัดการในลักษณะเดียวกัน (ตามคำแนะนำเวกเตอร์จำนวนเต็ม)

การใช้คำสั่งเวกเตอร์ 128b เวอร์ชัน AVX จะเป็นศูนย์ที่ส่วนบนของ reg เช่นกันดังนั้นจึงvpxor xmm, xmm, xmmเป็นทางเลือกที่ดีสำหรับการทำให้ YMM เป็นศูนย์ (AVX1 / AVX2) หรือ ZMM (AVX512) หรือส่วนขยายเวกเตอร์ในอนาคต vpxor ymm, ymm, ymmไม่ใช้ไบต์พิเศษใด ๆ ในการเข้ารหัสแม้ว่าจะทำงานแบบเดียวกันบน Intel แต่ช้ากว่าบน AMD ก่อน Zen2 (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.

ดูvxorps-zeroing บน AMD Jaguar / Bulldozer / Zen เร็วกว่าด้วยการลงทะเบียน xmm มากกว่า ymm หรือไม่ และ
วิธีใดที่มีประสิทธิภาพที่สุดในการล้างการลงทะเบียน ZMM เพียงตัวเดียวหรือสองสามตัวบน Knights Landing

กึ่งเกี่ยวข้อง: วิธีที่เร็วที่สุดในการตั้งค่า __m256 ให้เป็นหนึ่งบิต
ทั้งหมดและตั้งค่าบิตทั้งหมดในทะเบียน CPU เป็น 1 อย่างมีประสิทธิภาพยังครอบคลุมการk0..7ลงทะเบียนมาสก์AVX512 ด้วย SSE / AVX vpcmpeqdกำลังทำลายล้างในหลาย ๆ เรื่อง (แม้ว่าจะยังต้องการ uop ในการเขียน 1s) แต่ AVX512 vpternlogdสำหรับ ZMM regs ก็ไม่ได้ทำลาย ภายในลูปให้พิจารณาคัดลอกจากรีจิสเตอร์อื่นแทนที่จะสร้างใหม่ด้วย ALU uop โดยเฉพาะอย่างยิ่งกับ AVX512

แต่การเป็นศูนย์มีราคาถูก: การ xor-zeroing xmm reg ภายในลูปมักจะดีพอ ๆ กับการคัดลอกยกเว้นซีพียู AMD บางรุ่น (Bulldozer และ Zen) ซึ่งมีการกำจัดการเคลื่อนย้ายสำหรับ vector regs แต่ยังต้องใช้ ALU uop เพื่อเขียนเลขศูนย์สำหรับ xor -zeroing


มีอะไรพิเศษเกี่ยวกับการเป็นศูนย์สำนวนเช่น xor ใน uarches ต่างๆ

ซีพียูบางคนรับรู้sub same,sameว่าเป็นสำนวน zeroing เหมือนxorแต่ซีพียูทั้งหมดที่รู้จักสำนวน zeroing xorใดรู้จัก เพียงแค่ใช้xorเพื่อให้คุณไม่ต้องกังวลว่า CPU ตัวใดจะจำสำนวนที่เป็นศูนย์ได้

xor(เป็นสำนวนการเป็นศูนย์ที่ได้รับการยอมรับซึ่งแตกต่างจากmov reg, 0) มีข้อดีบางประการที่ชัดเจนและลึกซึ้ง (รายการสรุปแล้วฉันจะขยายในสิ่งเหล่านั้น):

  • โค้ดขนาดเล็กกว่าmov reg,0. (ซีพียูทั้งหมด)
  • หลีกเลี่ยงบทลงโทษการลงทะเบียนบางส่วนสำหรับรหัสในภายหลัง (ตระกูล Intel P6 และตระกูล SnB)
  • ไม่ใช้หน่วยปฏิบัติการประหยัดพลังงานและทำให้ทรัพยากรในการดำเนินการว่างเปล่า (ตระกูล Intel SnB)
  • uop ที่เล็กกว่า (ไม่มีข้อมูลทันที) ออกจากที่ว่างใน uop cache-line สำหรับคำแนะนำในบริเวณใกล้เคียงที่จะยืมหากจำเป็น (ตระกูล Intel SnB)
  • ไม่ได้ใช้รายการในแฟ้มทะเบียนทางกายภาพ (อย่างน้อย Intel SnB-family (และ P4) อาจเป็น AMD เช่นกันเนื่องจากพวกเขาใช้การออกแบบ PRF ที่คล้ายกันแทนที่จะรักษาสถานะการลงทะเบียนใน ROB เช่น microarchitectures ตระกูล Intel P6)

ขนาดรหัสเครื่องที่เล็กกว่า (2 ไบต์แทนที่จะเป็น 5) เป็นข้อดีเสมอ: ความหนาแน่นของโค้ดที่สูงขึ้นทำให้พลาดแคชคำสั่งน้อยลงและดึงคำสั่งได้ดีขึ้นและอาจถอดรหัสแบนด์วิดท์ได้


ประโยชน์ของการไม่ใช้หน่วยประมวลผลสำหรับ xor บน microarchitectures ตระกูล Intel SnB นั้นมีน้อย แต่ช่วยประหยัดพลังงาน มีแนวโน้มที่จะมีความสำคัญกับ SnB หรือ IvB ซึ่งมีพอร์ตการดำเนินการ ALU เพียง 3 พอร์ต Haswell และใหม่กว่ามีพอร์ตการดำเนินการ 4 พอร์ตที่สามารถจัดการคำสั่ง ALU จำนวนเต็มซึ่งรวมถึงmov r32, imm32ด้วยการตัดสินใจที่สมบูรณ์แบบโดยตัวกำหนดตารางเวลา (ซึ่งไม่ได้เกิดขึ้นในทางปฏิบัติเสมอไป) HSW ยังคงรักษาได้ 4 uops ต่อนาฬิกาแม้ว่าพวกเขาทั้งหมดจะต้องการ ALU ก็ตาม พอร์ตการดำเนินการ

ดูคำตอบของฉันสำหรับคำถามอื่นเกี่ยวกับการลงทะเบียนเป็นศูนย์สำหรับรายละเอียดเพิ่มเติม

บล็อกโพสต์ของ Bruce Dawsonที่ Michael Petch เชื่อมโยง (ในความคิดเห็นเกี่ยวกับคำถาม) ชี้ให้เห็นว่าxorได้รับการจัดการในขั้นตอนการเปลี่ยนชื่อโดยไม่ต้องใช้หน่วยดำเนินการ (ศูนย์ uops ในโดเมนที่ไม่ได้ใช้) แต่พลาดความจริงที่ว่ามันยังคงเป็นหนึ่งใน uop ในโดเมนที่หลอมรวม ซีพียู Intel สมัยใหม่สามารถออกและเลิกใช้งาน fused-domain ได้ 4 ครั้งต่อนาฬิกา นั่นคือจุดที่ 4 ศูนย์ต่อนาฬิกา จำกัด มาจาก ความซับซ้อนที่เพิ่มขึ้นของฮาร์ดแวร์การเปลี่ยนชื่อการลงทะเบียนเป็นเพียงหนึ่งในสาเหตุของการจำกัดความกว้างของการออกแบบเป็น 4 (บรูซเขียนบล็อกโพสต์ที่ยอดเยี่ยมมากเช่นซีรี่ส์ของเขาเกี่ยวกับคณิตศาสตร์ FP และ x87 / SSE / ปัญหาการปัดเศษซึ่งฉันทำ ขอเเนะนำ).


เกี่ยวกับเอเอ็มดีซีพียู Bulldozer ครอบครัว , mov immediateวิ่งบนเดียวกัน EX0 / EX1 xorพอร์ตการดำเนินการเป็นจำนวนเต็ม mov reg,regยังสามารถรันบน AGU0 / 1 ได้ แต่สำหรับการลงทะเบียนคัดลอกเท่านั้นไม่ใช่สำหรับการตั้งค่าจากทันที ดังนั้น AFAIK ในเอเอ็มดีได้เปรียบเพียง แต่จะxorมากกว่าmovคือการเข้ารหัสสั้น นอกจากนี้ยังอาจช่วยประหยัดทรัพยากรการลงทะเบียนทางกายภาพ แต่ฉันไม่เห็นการทดสอบใด ๆ


สำนวนการเป็นศูนย์ที่ได้รับการยอมรับจะหลีกเลี่ยงบทลงโทษการลงทะเบียนบางส่วนในซีพียู Intel ซึ่งเปลี่ยนชื่อการลงทะเบียนบางส่วนแยกจากการลงทะเบียนแบบเต็ม (ตระกูล P6 และ SnB)

xorจะแท็กรีจิสเตอร์ว่ามีส่วนบนเป็นศูนย์ดังนั้นxor eax, eax/ inc al/ inc eaxหลีกเลี่ยงบทลงโทษการลงทะเบียนบางส่วนตามปกติที่ซีพียู pre-IvB มี แม้ว่าจะไม่มีxorIvB ก็ต้องการการผสานรวมเมื่อมีการแก้ไข 8 บิตสูง ( AH) จากนั้นจึงอ่านรีจิสเตอร์ทั้งหมดและแฮสก็ลบออกด้วยซ้ำ

จากคู่มือ microarch ของ Agner Fog หน้า 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จะไม่ได้รับการยอมรับเป็นสำนวน zeroing อย่างน้อยในช่วงต้น P6 ออกแบบเช่น PIII หรือ PM ฉันจะแปลกใจมากถ้าพวกเขาใช้ทรานซิสเตอร์ในการตรวจจับมันในซีพียูรุ่นหลัง ๆ


xorตั้งค่าสถานะซึ่งหมายความว่าคุณต้องระมัดระวังในการทดสอบเงื่อนไข เนื่องจากsetccน่าเสียดายที่มีให้เฉพาะกับปลายทาง 8 บิตคุณจึงต้องระมัดระวังเพื่อหลีกเลี่ยงบทลงโทษในการลงทะเบียนบางส่วน

คงจะดีถ้า x86-64 เปลี่ยนหนึ่งใน opcodes ที่ถูกลบ (เช่น AAM) สำหรับ 16/32/64 บิตsetcc r/mโดยมีการเข้ารหัสเพรดิเคตในฟิลด์ 3 บิตที่ลงทะเบียนต้นทางของฟิลด์ r / m (ทาง คำแนะนำตัวถูกดำเนินการเดี่ยวอื่น ๆ ใช้เป็นบิต opcode) แต่พวกเขาไม่ได้ทำอย่างนั้นและนั่นก็ไม่ช่วยสำหรับ 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

สิ่งนี้มีประสิทธิภาพที่ดีที่สุดสำหรับซีพียูทั้งหมด (ไม่มีการหยุดชะงักการรวม uops หรือการอ้างอิงเท็จ)

สิ่งที่มีความซับซ้อนมากขึ้นเมื่อคุณไม่ต้องการที่จะ xor ก่อนการเรียนการสอนธงการตั้งค่า เช่นคุณต้องการแยกเงื่อนไขหนึ่งแล้ว setcc กับเงื่อนไขอื่นจากแฟล็กเดียวกัน เช่นcmp/jle, seteและคุณอย่างใดอย่างหนึ่งไม่ได้มีการลงทะเบียนอะไหล่หรือคุณต้องการที่จะให้xorออกจากเส้นทางรหัสไม่ได้นำมารวมกัน

ไม่มีสำนวนการเป็นศูนย์ที่เป็นที่รู้จักซึ่งไม่มีผลต่อแฟล็กดังนั้นทางเลือกที่ดีที่สุดจึงขึ้นอยู่กับสถาปัตยกรรมขนาดเล็กเป้าหมาย บน Core2 การใส่ uop แบบผสานอาจทำให้เกิดวงจร 2 หรือ 3 รอบ ดูเหมือนว่าจะถูกกว่าใน SnB แต่ฉันไม่ได้ใช้เวลาในการวัดมากนัก การใช้mov reg, 0/ setccจะมีโทษอย่างมีนัยสำคัญสำหรับซีพียู Intel รุ่นเก่าและยังค่อนข้างแย่กว่า Intel รุ่นใหม่

การใช้setcc/ movzx r32, r8อาจเป็นทางเลือกที่ดีที่สุดสำหรับตระกูล Intel P6 และ SnB หากคุณไม่สามารถ xor-zero ก่อนหน้าคำสั่งการตั้งค่าสถานะ ควรจะดีกว่าการทดสอบซ้ำหลังจาก xor-zeroing (อย่าพิจารณาsahf/ lahfหรือpushf/ popf) IvB สามารถกำจัดได้movzx r32, r8(เช่นจัดการด้วยการเปลี่ยนชื่อการลงทะเบียนโดยไม่มีหน่วยประมวลผลหรือเวลาแฝงเช่น xor-zeroing) Haswell และใหม่กว่ากำจัดเฉพาะmovคำสั่งปกติดังนั้นจึงmovzxใช้หน่วยประมวลผลและมีเวลาแฝงที่ไม่ใช่ศูนย์ทำให้การทดสอบ / setcc/ movzxแย่กว่าxor/ ทดสอบ / setccแต่อย่างน้อยก็ยังดีพอ ๆ กับการทดสอบ / mov r,0/ setcc(และดีกว่ามากสำหรับซีพียูรุ่นเก่า)

การใช้setcc/ movzxโดยไม่มีศูนย์ก่อนนั้นไม่ดีใน AMD / P4 / Silvermont เนื่องจากไม่ได้ติดตาม deps แยกต่างหากสำหรับการลงทะเบียนย่อย จะมีการลบค่าเก่าของทะเบียนเป็นเท็จ การใช้mov reg, 0/ setccสำหรับการเป็นศูนย์ / การทำลายการพึ่งพาอาจเป็นทางเลือกที่ดีที่สุดเมื่อxor/ ทดสอบ / setccไม่ใช่ตัวเลือก

แน่นอนว่าถ้าคุณไม่ต้องการsetccให้เอาต์พุตกว้างเกิน 8 บิตคุณก็ไม่จำเป็นต้องศูนย์อะไรเลย อย่างไรก็ตามโปรดระวังการอ้างอิงที่ผิดพลาดบน CPU นอกเหนือจาก P6 / SnB หากคุณเลือกรีจิสเตอร์ที่เพิ่งเป็นส่วนหนึ่งของห่วงโซ่การพึ่งพาที่ยาวนาน (และระวังการก่อให้เกิด reg Stall บางส่วนหรือ uop พิเศษหากคุณเรียกใช้ฟังก์ชันที่อาจบันทึก / กู้คืนรีจิสเตอร์ที่คุณใช้เป็นส่วนหนึ่ง)


andการมีศูนย์ในทันทีไม่ได้ถูกระบุไว้เป็นพิเศษเนื่องจากไม่ขึ้นกับค่าเก่าของซีพียูใด ๆ ที่ฉันรู้จักดังนั้นจึงไม่ทำลายห่วงโซ่การพึ่งพา มันไม่มีข้อดีกว่าxorและข้อเสียมากมาย

มีประโยชน์สำหรับการเขียน microbenchmarks เมื่อคุณต้องการการอ้างอิงเป็นส่วนหนึ่งของการทดสอบเวลาในการตอบสนอง แต่ต้องการสร้างค่าที่ทราบโดยการกำหนดค่าเป็นศูนย์และเพิ่ม


โปรดดูhttp://agner.org/optimize/สำหรับรายละเอียด microarchซึ่งรวมถึงสำนวนการเป็นศูนย์ที่ได้รับการยอมรับว่าเป็นการทำลายการพึ่งพา (เช่นใช้sub same,sameกับ CPU บางตัว แต่ไม่ใช่ทั้งหมดในขณะที่ระบบxor same,sameรู้จักทั้งหมด) movจะทำลายห่วงโซ่การพึ่งพาของค่าเดิม ของรีจิสเตอร์ (ไม่ว่าค่าต้นทางจะเป็นศูนย์หรือไม่ก็ตามเพราะนั่นคือวิธีการmovทำงาน) xorเพียง แต่แบ่งโซ่พึ่งพาในกรณีพิเศษที่ src และปลายทางมีการลงทะเบียนเดียวกันซึ่งเป็นเหตุผลที่movถูกปล่อยออกมาจากรายการพิเศษได้รับการยอมรับการพึ่งพาเบรกเกอร์ (นอกจากนี้เนื่องจากไม่ได้รับการยอมรับว่าเป็นสำนวนที่เป็นศูนย์รวมทั้งประโยชน์อื่น ๆ ที่มี)

ที่น่าสนใจคือการออกแบบ P6 ที่เก่าแก่ที่สุด (PPro ถึง Pentium III) ไม่รู้จักxor-zeroing เป็นตัวตัดการพึ่งพา แต่เป็นสำนวนที่เป็นศูนย์เพื่อวัตถุประสงค์ในการหลีกเลี่ยงแผงขายของที่ลงทะเบียนบางส่วนดังนั้นในบางกรณีจึงควรใช้ทั้งสองอย่าง movแล้วxor-zeroing ในลำดับนั้นเพื่อทำลาย dep แล้วศูนย์อีกครั้ง + ตั้งค่าบิตแท็กภายในที่บิตสูงเป็นศูนย์ดังนั้น EAX = AX = AL

ดูตัวอย่างของ Agner Fog 6.17 ใน microarch pdf ของเขา เขาบอกว่าสิ่งนี้ใช้ได้กับ P2, P3 และแม้กระทั่ง (ช่วงต้น?) PM ความคิดเห็นในบล็อกโพสต์ที่เชื่อมโยงบอกว่าเป็น PPro เท่านั้นที่มีการกำกับดูแลนี้ แต่ฉันได้ทดสอบ Katmai PIII และ @Fanael ทดสอบกับ Pentium M และเราทั้งคู่พบว่ามันไม่ได้ทำลายการพึ่งพาสำหรับเวลาแฝง - imulโซ่ขา นี่เป็นการยืนยันผลลัพธ์ของ Agner Fog อย่างน่าเสียดาย


TL: DR:

ถ้ามันทำให้โค้ดของคุณดีขึ้นจริง ๆ หรือบันทึกคำแนะนำให้แน่ใจว่าให้ศูนย์movเพื่อหลีกเลี่ยงการสัมผัสแฟล็กตราบใดที่คุณไม่แนะนำปัญหาด้านประสิทธิภาพนอกเหนือจากขนาดโค้ด การหลีกเลี่ยงแฟล็ก clobbering เป็นเหตุผลเดียวที่สมเหตุสมผลสำหรับการไม่ใช้xorแต่บางครั้งคุณสามารถ xor-zero ก่อนสิ่งที่กำหนดแฟล็กได้หากคุณมีทะเบียนสำรอง

mov-zero ข้างหน้าsetccจะดีกว่าสำหรับเวลาแฝงmovzx reg32, reg8(ยกเว้นใน Intel เมื่อคุณสามารถเลือกรีจิสเตอร์ที่แตกต่างกันได้) แต่ขนาดโค้ดแย่ลง


7
คำแนะนำทางคณิตศาสตร์ส่วนใหญ่ OP R, S ถูกบังคับโดย CPU ที่ไม่ได้รับคำสั่งเพื่อรอให้เนื้อหาของ register R ถูกเติมเต็มตามคำสั่งก่อนหน้าโดยมี register R เป็นเป้าหมาย นี่คือการพึ่งพาข้อมูล ประเด็นสำคัญคือชิป Intel / AMD มีฮาร์ดแวร์พิเศษที่จะทำลายต้องรอสำหรับการพึ่งพาข้อมูลในรีจิสเตอร์ R เมื่อพบ XOR R, R และไม่จำเป็นต้องทำเช่นนั้นสำหรับคำแนะนำการรีจิสเตอร์อื่น ๆ ซึ่งหมายความว่าคำสั่ง XOR สามารถกำหนดเวลาให้ดำเนินการได้ทันทีและนี่คือเหตุผลที่ Intel / AMD แนะนำให้ใช้
Ira Baxter

3
@IraBaxter: ใช่และเพื่อหลีกเลี่ยงความสับสนใด ๆ (เพราะฉันได้เห็นความเข้าใจผิดเกี่ยวกับ SO นี้) mov reg, srcยังแบ่งโซ่การใช้งานสำหรับซีพียู OO (ไม่ว่า src จะเป็น imm32 [mem]หรือรีจิสเตอร์อื่นก็ตาม) การทำลายการพึ่งพานี้ไม่ได้รับการกล่าวถึงในคู่มือการเพิ่มประสิทธิภาพเนื่องจากไม่ใช่กรณีพิเศษที่เกิดขึ้นเฉพาะเมื่อ src และ dest เป็นรีจิสเตอร์เดียวกัน มันเกิดขึ้นเสมอสำหรับคำแนะนำที่ไม่ขึ้นอยู่กับปลายทางของพวกเขา (ยกเว้นการนำไปใช้ของ Intel ในการกำหนดpopcnt/lzcnt/tzcntเป้าหมายที่ผิดพลาด)
Peter Cordes

2
@Zboson: "เวลาในการตอบสนอง" ของคำสั่งที่ไม่มีการอ้างอิงจะมีความสำคัญก็ต่อเมื่อมีฟองสบู่ในท่อ เป็นสิ่งที่ดีสำหรับการกำจัดการเคลื่อนไหว แต่สำหรับคำแนะนำที่เป็นศูนย์ผลประโยชน์ที่แฝงเป็นศูนย์จะเข้ามามีบทบาทหลังจากมีบางอย่างเช่นการคาดเดาผิดสาขาหรือ I $ miss โดยที่การดำเนินการกำลังรอคำสั่งที่ถอดรหัสแทนที่จะให้ข้อมูลพร้อม แต่ใช่การกำจัดการเคลื่อนไหวไม่ได้ทำให้movฟรีมีเวลาแฝงเป็นศูนย์เท่านั้น ส่วน "ไม่ใช้พอร์ตการดำเนินการ" มักจะไม่สำคัญ ปริมาณงาน Fused-domain อาจเป็นปัญหาคอขวดได้อย่างง่ายดาย กับโหลดหรือร้านค้าในการผสมผสาน
Peter Cordes

2
ตาม Agner KNL ไม่รู้จักความเป็นอิสระของการลงทะเบียน 64 บิต ดังนั้นxor r64, r64ไม่เพียงแค่เสียไบต์ อย่างที่คุณบอกว่าxor r32, r32เป็นทางเลือกที่ดีที่สุดโดยเฉพาะกับ KNL ดูส่วนที่ 15.7 "กรณีพิเศษเพื่อความเป็นอิสระ" ในคู่มือไมโครชั้นนี้หากคุณต้องการอ่านเพิ่มเติม
Z boson

3
อ่าMIPSเก่า ๆ ดีตรงไหนมี "zero register"เมื่อคุณต้องการ
hayalci
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.