การใช้ xor reg, reg ให้ข้อได้เปรียบเหนือ mov reg, 0 หรือไม่ [ทำซ้ำ]

มีสองวิธีที่รู้จักกันดีในการตั้งค่าการลงทะเบียนจำนวนเต็มให้เป็นค่าศูนย์บน x86

ทั้ง

mov reg, 0

or

xor reg, reg

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

อย่างไรก็ตาม มีการพึ่งพาคำสั่งระหว่างคำสั่งอย่างเป็นทางการระหว่างคำสั่ง xor และคำสั่งก่อนหน้าใดก็ตามที่เปลี่ยนรีจิสเตอร์เดียวกัน เนื่องจากมีการพึ่งพา คำสั่งหลังจึงต้องรอจนกว่าคำสั่งแรกจะเสร็จสมบูรณ์ และอาจลดภาระของหน่วยประมวลผลและทำให้ประสิทธิภาพลดลง

add reg, 17
;do something else with reg here
xor reg, reg

เห็นได้ชัดว่าผลลัพธ์ของ xor จะเหมือนกันทุกประการโดยไม่คำนึงถึงค่ารีจิสเตอร์เริ่มต้น แต่โปรเซสเซอร์สามารถรับรู้สิ่งนี้ได้หรือไม่?

ฉันลองทดสอบต่อไปนี้ใน VC++7:

const int Count = 10 * 1000 * 1000 * 1000;
int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    DWORD start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            xor eax, eax
        };
    }
    DWORD diff = GetTickCount() - start;
    start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            mov eax, 0
        };
    }
    diff = GetTickCount() - start;
    return 0;
}

ด้วยการเพิ่มประสิทธิภาพของทั้งสองลูปจะใช้เวลาเท่ากันทุกประการ นี่เป็นการพิสูจน์อย่างสมเหตุสมผลหรือไม่ว่าโปรเซสเซอร์รับรู้ว่าไม่มีการพึ่งพาคำสั่ง xor reg, reg กับคำสั่ง mov eax, 0 ก่อนหน้าหรือไม่ อะไรจะเป็นการทดสอบที่ดีกว่าในการตรวจสอบสิ่งนี้


person sharptooth    schedule 16.07.2009    source แหล่งที่มา
comment
ฉันคิดว่านี่คือเหตุผลที่เราใช้ภาษาระดับสูง หากคุณต้องการทราบจริงๆ เพียงแค่เปลี่ยนขั้นตอนของโค้ดเจนเพื่อทำอย่างใดอย่างหนึ่ง เกณฑ์มาตรฐาน เลือกสิ่งที่ดีที่สุด   -  person jrockway    schedule 16.07.2009
comment
อ่า เคล็ดลับ xor reg, reg แบบเก่า - สมัยก่อนที่ดี :)   -  person Nick Dandoulakis    schedule 16.07.2009
comment
ฉันคิดว่าสถาปัตยกรรม x86 กำหนด XOR reg อย่างชัดเจนว่าทำลายการพึ่งพา reg ดูคู่มือสถาปัตยกรรม Intel ฉันคาดหวังว่า MOV reg,... จะทำสิ่งเดียวกันเพียงเพราะมันเป็น MOV ดังนั้นตัวเลือกที่แท้จริงของคุณคืออันไหนใช้พื้นที่น้อยกว่า (ฉันเดาว่าเวลาดำเนินการจะเท่ากัน) หากคุณไม่สนใจบิตสถานะ (XOR ทำลายพวกมันทั้งหมด)   -  person Ira Baxter    schedule 21.07.2009
comment
ตัวแปร Count ของคุณล้น ดังนั้นลูปจะทำงานน้อยกว่าที่คุณคาดไว้มาก   -  person phuclv    schedule 06.12.2013
comment
สำหรับสถาปัตยกรรมไมโครล่าสุด xor reg,reg ไม่จำเป็นต้องมีหน่วยดำเนินการ (จัดการด้วยการถอดรหัส?) มันทำลายการพึ่งพาใน reg และแผงลอยอัปเดตสถานะบางส่วน และมีการเข้ารหัสที่เล็กกว่า ไม่มีเหตุผลที่ดีสำหรับแนวทาง mov ใน x86-64 ล่าสุด เว้นแต่ว่าคุณจะต้องรักษาสถานะ [e] ไว้   -  person Brett Hale    schedule 10.02.2014
comment
มีข้อดีเล็กๆ น้อยๆ หลายประการนอกเหนือจากขนาดโค้ดในการใช้สำนวนที่เป็นศูนย์ที่ได้รับการยอมรับ เช่น xor เมื่อเปรียบเทียบกับ mov ฉันเขียนคำตอบสำหรับคำถามล่าสุดก่อนที่จะเห็นคำถามนี้: stackoverflow.com/questions/33666617/ . ฉันคิดว่ามันเป็นคำตอบที่ดีและสมบูรณ์มากกว่าคำตอบเหล่านี้ ตามหลักการแล้วควรทำเครื่องหมายว่าซ้ำกัน   -  person Peter Cordes    schedule 19.01.2016


คำตอบ (6)


คำตอบที่แท้จริงสำหรับคุณ:

คู่มืออ้างอิงการเพิ่มประสิทธิภาพสถาปัตยกรรม Intel 64 และ IA-32

ส่วนที่ 3.5.1.8 คือส่วนที่คุณต้องการดู

ในระยะสั้นมีสถานการณ์ที่อาจต้องการ xor หรือ mov ประเด็นนี้เน้นที่ห่วงโซ่การพึ่งพาและการเก็บรักษารหัสเงื่อนไข

person Mark    schedule 16.07.2009
comment
ดูเหมือนว่าข้อความที่ยกมาไม่แนะนำให้ใช้ MOV ในทุกสถานการณ์ - person mwfearnley; 07.05.2016
comment
@mwfearnley น่าเสียดายที่ Addison ตัดสินใจแก้ไขคำตอบของฉันและเชอร์รี่เลือกชุดย่อยของเนื้อหา มันไม่ชัดเจนว่าทำไมจึงทำเช่นนี้ คุณควรอ่านเอกสารฉบับเต็มซึ่งครอบคลุมสถานการณ์ที่ต้องการ mov - person Mark; 09.05.2016
comment
ขอบคุณสำหรับการชี้แจง ฉันเดาว่ามันเป็นความพยายามที่จะหลีกเลี่ยงปัญหาในการเคลื่อนย้าย / เปลี่ยนแปลงเอกสาร แต่น่าเสียดายที่ใบเสนอราคาไม่มีประเด็นทั้งหมดที่จำเป็น .. ตอนนี้ฉันเห็นได้จากส่วนนั้น มันบอกว่าให้ใช้ MOV เมื่อคุณต้องการหลีกเลี่ยง การตั้งรหัสเงื่อนไข - person mwfearnley; 09.05.2016
comment
@mwfearnley: เป็นเรื่องยากที่คุณไม่สามารถ xor-zero ก่อนตั้งค่าสถานะได้ ดู คำตอบของฉันในคำถาม xor ล่าสุดสำหรับคำแนะนำบางประการเกี่ยวกับวิธีหลีกเลี่ยง mov reg, 0 เพื่อเตรียมพร้อมสำหรับ setcc (และสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับข้อดีทั้งหมดของ xor-zeroing) mov reg,0 / setcc นั้นแย่มากสำหรับ Intel CPU รุ่นเก่า โดยที่การอ่าน reg แบบเต็มจะทำให้การลงทะเบียนบางส่วนหยุดชะงักซึ่ง xor จะหลีกเลี่ยงได้ - person Peter Cordes; 09.05.2016

สำหรับ CPU สมัยใหม่ แนะนำให้ใช้รูปแบบ XOR มันเล็กลงและเร็วขึ้น

ที่จริงแล้วขนาดที่เล็กกว่านั้นมีความสำคัญเพราะบนเวิร์คโหลดจริงหลายๆ ปัจจัยหลักที่จำกัดประสิทธิภาพก็คือการขาดแคชของ i-cache สิ่งนี้จะไม่ถูกบันทึกไว้ในเกณฑ์มาตรฐานขนาดเล็กเมื่อเปรียบเทียบทั้งสองตัวเลือก แต่ในโลกแห่งความเป็นจริง มันจะทำให้โค้ดทำงานเร็วขึ้นเล็กน้อย

และหากละเว้นการพลาด i-cache ที่ลดลง XOR บน CPU ใด ๆ ในช่วงหลายปีที่ผ่านมาจะมีความเร็วเท่ากันหรือเร็วกว่า MOV อะไรจะเร็วไปกว่าการรันคำสั่ง MOV? ไม่ได้ดำเนินการตามคำสั่งใดๆ เลย! ในโปรเซสเซอร์ Intel ล่าสุด ลอจิกการจัดส่ง/เปลี่ยนชื่อจะจดจำรูปแบบ XOR โดย 'ตระหนัก' ว่าผลลัพธ์จะเป็นศูนย์ และเพียงชี้การลงทะเบียนไปที่การลงทะเบียนทางกายภาพเป็นศูนย์ จากนั้นจะโยนคำสั่งทิ้งไปเพราะไม่จำเป็นต้องดำเนินการ

ผลลัพธ์สุทธิคือรูปแบบ XOR ใช้ทรัพยากรการดำเนินการเป็นศูนย์ และสามารถ 'ดำเนินการ' สี่คำสั่งต่อรอบบน CPU Intel ล่าสุดได้ MOV มีคำสั่งสูงสุดสามคำสั่งต่อรอบ

สำหรับรายละเอียดโปรดดูโพสต์บล็อกนี้ที่ฉันเขียน:

https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

โปรแกรมเมอร์ส่วนใหญ่ไม่ควรกังวลเกี่ยวกับเรื่องนี้ แต่ผู้เขียนคอมไพเลอร์ก็ต้องกังวล และเป็นการดีที่จะเข้าใจโค้ดที่กำลังถูกสร้างขึ้น และมันก็เจ๋งมาก!

person Bruce Dawson    schedule 19.03.2015
comment
การเขียนที่ยอดเยี่ยม! ฉันสงสัยว่ามีรูปแบบเดียวกันบน Thumb หรือไม่ - person Asti; 16.01.2021
comment
มีแนวโน้มว่าจะมีการเพิ่มประสิทธิภาพแบบเดียวกันนี้บน Thumb การเพิ่มประสิทธิภาพนี้สามารถใช้ได้กับโปรเซสเซอร์ที่ไม่อยู่ในลำดับใดๆ และควรประหยัดพลังงานและบางครั้งอาจปรับปรุงประสิทธิภาพได้ แต่ฉันไม่รู้ - person Bruce Dawson; 17.01.2021

x86 มีคำสั่งที่มีความยาวผันแปรได้ MOV EAX, 0 ต้องการหนึ่งหรือสองไบต์ในพื้นที่โค้ดมากกว่า XOR EAX, EAX

person ajs410    schedule 15.10.2009
comment
mov eax, 0 คือ 5 ไบต์: หนึ่งไบต์สำหรับ mov eax, imm32 opcode และ 4 สำหรับ 4B ของข้อมูลทันที xor eax, eax คือ 2 ไบต์: หนึ่ง xor r32, r/m32 opcode หนึ่งอันสำหรับตัวถูกดำเนินการ - person Peter Cordes; 12.12.2015

ฉันหยุดซ่อมรถของตัวเองไม่ได้หลังจากที่ขายรถสเตชั่นแวกอน HR ปี 1966 ไป ฉันอยู่ในการแก้ไขที่คล้ายกันกับ CPU สมัยใหม่ :-)

มันจะขึ้นอยู่กับไมโครโค้ดหรือวงจรพื้นฐานจริงๆ ค่อนข้างเป็นไปได้ที่ CPU สามารถจดจำ "XOR Rn,Rn" และทำให้บิตทั้งหมดเป็นศูนย์โดยไม่ต้องกังวลเกี่ยวกับเนื้อหา แต่แน่นอนว่า มันอาจทำสิ่งเดียวกันกับ "MOV Rn, 0" คอมไพเลอร์ที่ดีจะเลือกตัวเลือกที่ดีที่สุดสำหรับแพลตฟอร์มเป้าหมาย ดังนั้นโดยปกติแล้วปัญหานี้จะเกิดขึ้นเฉพาะเมื่อคุณเขียนโค้ดในแอสเซมเบลอร์

หาก CPU ฉลาดเพียงพอ การพึ่งพา XOR ของคุณจะหายไปเนื่องจาก รู้ ค่านั้นไม่เกี่ยวข้องและจะตั้งค่าให้เป็นศูนย์ต่อไป (ขึ้นอยู่กับ CPU จริงที่ใช้งานอยู่อีกครั้ง)

อย่างไรก็ตาม ฉันไม่สนใจไบต์หรือรอบสัญญาณนาฬิกาสองสามรอบในโค้ดของฉันมานานแล้ว - ดูเหมือนว่าการเพิ่มประสิทธิภาพแบบไมโครจะบ้าไปแล้ว

person paxdiablo    schedule 16.07.2009
comment
ไม่ว่าจะเป็นการเพิ่มประสิทธิภาพมากเกินไปสำหรับการใช้งานจริงหรือไม่ก็ตาม อาจมีคุณค่าที่จะเข้าใจว่าคำสั่งที่คล้ายคลึงกันไม่ได้ถูกสร้างขึ้นเท่ากันทั้งหมด ;) - person jerryjvl; 16.07.2009
comment
@jerryjvl - นอกจากนี้ยังมีประโยชน์ที่จะตระหนักว่าซีพียูเดสก์ท็อป x86 สมัยใหม่ไม่ได้ใช้รหัสเครื่อง x86 - พวกเขาถอดรหัส x86 เป็น RISC เช่นคำสั่งภายในเพื่อดำเนินการ ด้วยเหตุนี้ จึงสามารถจดจำลำดับโค้ดทั่วไปได้ (เช่น xor eax, eax) และแปลเป็นคำสั่งที่ง่ายกว่า เช่น อาจมีคำสั่ง reg ที่ชัดเจนแทน xor จริงอาจไม่เกิดขึ้นในกรณีนี้ - person Michael; 16.07.2009
comment
การเพิ่มประสิทธิภาพแบบไมโครอาจต้องทำให้คุณคลั่งไคล้เมื่อคุณเขียน MBR =) - person brianmearns; 25.03.2013
comment
@ sh1ftst0rm : ทุกวันนี้มีแต่คนไม่ฉลาดเท่านั้นที่ทำเรื่องแบบนี้ - person Daniel Kamil Kozar; 06.05.2014

ฉันคิดว่าในสถาปัตยกรรมรุ่นก่อนๆ คำสั่ง mov eax, 0 เคยใช้เวลานานกว่า xor eax, eax เล็กน้อยเช่นกัน... จำไม่ได้ว่าทำไม เว้นแต่คุณจะมี movs อีกมาก แต่ฉันจินตนาการว่าคุณไม่น่าจะทำให้แคชพลาดเนื่องจากมีหนึ่งตัวอักษรที่เก็บไว้ในโค้ด

โปรดทราบว่าจากหน่วยความจำสถานะของแฟล็กระหว่างวิธีการเหล่านี้ไม่เหมือนกัน แต่ฉันอาจจำสิ่งนี้ผิด

person jerryjvl    schedule 16.07.2009

คุณกำลังเขียนคอมไพเลอร์หรือไม่?

และประการที่สอง การเปรียบเทียบของคุณอาจจะไม่ได้ผล เนื่องจากคุณมีสาขาในนั้นที่อาจต้องใช้เวลาตลอดเวลาอยู่แล้ว (เว้นแต่ว่าคอมไพเลอร์ของคุณจะคลายการวนซ้ำให้คุณ)

อีกสาเหตุหนึ่งที่คุณไม่สามารถเปรียบเทียบคำสั่งเดียวในลูปได้ก็คือโค้ดทั้งหมดของคุณจะถูกแคช (ไม่เหมือนกับโค้ดจริง) ดังนั้นคุณจึงนำขนาดที่แตกต่างกันมากระหว่าง mov eax,0 และ xor eax,eax ออกจากรูปภาพโดยเก็บไว้ใน L1-cached ตลอดเวลา

ฉันเดาว่าความแตกต่างด้านประสิทธิภาพที่วัดได้ในโลกแห่งความเป็นจริงจะเกิดจากความแตกต่างของขนาดที่กัดกินแคช ไม่ใช่เพราะเวลาดำเนินการของทั้งสองตัวเลือก

person Thomas    schedule 16.07.2009
comment
เว็บไซต์ทั้งหมดนี้มีคนที่ใส่ใจคุณภาพต่อส่วนอื่นๆ ของโลก ฉันไม่คิดว่านั่นจะเป็นคำตอบที่ดี - person Roman Starkov; 21.01.2011
comment
ดูเหมือนว่าคุณและคนอื่นๆ กำลังมุ่งความสนใจไปที่สิ่งที่ฉันเดาว่าคุณรู้สึกว่าน่ารังเกียจ ฉันได้ลบส่วนนั้นออกไปแล้วเนื่องจากฉันคิดว่าคุณและคนอื่น ๆ ไม่เคยอ่านเรื่องนั้นเลยและเพิ่งลงคะแนน - person Thomas; 09.07.2019
comment
สำหรับ Sandybridge / Ivybridge คุณสามารถสร้างลูปที่ทำงานที่ 1 การวนซ้ำต่อนาฬิกาด้วย nop หรือ xor same,same ได้อย่างง่ายดาย แต่เกิดปัญหาคอขวดในทรูพุตของหน่วยประมวลผล ALU ด้วย mov reg,0 Intel CPU รุ่นต่อมามีหน่วยประมวลผล ALU 4 หน่วย ดังนั้นตัวอย่างที่ชัดเจนของการกำจัด xor-zeroing ที่สร้างความแตกต่างที่วัดได้นอกเหนือจากขนาดโค้ดจึงสร้างได้ง่ายกว่ามาก (xorps การทำให้ค่าศูนย์ของ xmm/ymm regs ยังคงเป็นเรื่องง่าย เนื่องจากมีพอร์ต vector ALU น้อยกว่าความกว้างส่วนหน้า) และ CPU ของ AMD ไม่ได้กำจัด uop ของแบ็กเอนด์ ดังนั้นข้อดีจึงเป็นเพียงขนาดโค้ดเท่านั้น - person Peter Cordes; 10.07.2019
comment
โค้ดส่วนใหญ่ได้รับแคช L1i เกือบตลอดเวลา แคช L1i พลาดเกิดขึ้น แต่คำสั่ง ส่วนใหญ่ ที่ดำเนินการตลอดหลักสูตรนั้นมาจากแคช L1i หรือแม้แต่แคช uop ที่เล็กกว่า/เร็วกว่า โปรแกรมส่วนใหญ่ใช้เวลาส่วนใหญ่ในการวนซ้ำขนาดเล็กถึงขนาดกลาง แคชทำงาน - person Peter Cordes; 10.07.2019
comment
คุณพูดถูกที่ความพยายามของ OP ในการเปรียบเทียบนั้นไม่น่าจะได้ผล แต่อาจอยู่ที่ Sandybridge หากโอเวอร์เฮดของลูปคือ ALU พิเศษ 2 uops ทำให้มี uops ส่วนหน้าทั้งหมด 4 รายการ หากหนึ่งในนั้นคือ xor-zeroing ที่สามารถกำจัดได้ แบ็กเอนด์ก็สามารถจัดการได้ - person Peter Cordes; 10.07.2019
comment
ฉันเห็นด้วยกับทุกสิ่งที่คุณพูด ฉันไม่ได้เร่งความเร็วในการมีอยู่ของ AGI แผงลอยหรือเทียบเท่าในซีพียู Intel สมัยใหม่นอกเหนือจากปัญหาคอขวดของ ALU แต่ประเด็นยังคงเหมือนเดิม: คุณไม่สามารถเปรียบเทียบคำสั่งในลักษณะที่ op ทำ มันขึ้นอยู่กับโค้ดทั้งหมดที่อยู่รอบๆ และสาขาเป็นเพียงส่วนหนึ่งเท่านั้น ประเด็นของฉันว่าวิธีการและทำไม? ยังคงเหมือนเดิมและเสริมแต่สิ่งที่คุณเพิ่มเข้าไปเท่านั้น - person Thomas; 11.07.2019