เหตุใดการแนะนำคำแนะนำ MOV ที่ไร้ประโยชน์จะทำให้วงวนแน่นใน x86_64 ชุดประกอบ


222

พื้นหลัง:

ในขณะที่เพิ่มประสิทธิภาพรหัสภาษาปาสคาลด้วยภาษาแอสเซมบลีที่ฝังตัวฉันสังเกตเห็นMOVคำสั่งที่ไม่จำเป็นและลบมันออก

ที่แปลกใจของฉันเอาคำสั่งยกเลิกการจำเป็นที่เกิดจากโปรแกรมของฉันที่จะชะลอตัวลง

ฉันพบว่าการเพิ่มMOVคำแนะนำแบบไม่มีประโยชน์เพิ่มประสิทธิภาพให้ดียิ่งขึ้น

ผลที่ได้คือเอาแน่เอานอนและการเปลี่ยนแปลงตามลำดับการดำเนินการ: คำแนะนำขยะเดียวกันขนย้ายขึ้นหรือลงโดยบรรทัดเดียวผลิตชะลอตัว

ฉันเข้าใจว่าซีพียูใช้การปรับแต่งและการทำให้เพรียวลมทุกชนิด แต่ดูเหมือนว่าจะเป็นเวทย์มนตร์ดำ

ข้อมูล:

รหัสของฉันในเวอร์ชันรวบรวมเงื่อนไขการทำงานของขยะสามครั้งในช่วงกลางของลูปที่รัน2**20==1048576ครั้ง (โปรแกรมที่อยู่รอบ ๆ เพียงแค่คำนวณแฮชSHA-256 )

ผลลัพธ์บนเครื่องที่ค่อนข้างเก่า (Intel (R) Core (TM) 2 CPU 6400 @ 2.13 GHz):

avg time (ms) with -dJUNKOPS: 1822.84 ms
avg time (ms) without:        1836.44 ms

โปรแกรมถูกเรียกใช้ 25 ครั้งในการวนลูปโดยที่ลำดับการรันจะเปลี่ยนแบบสุ่มในแต่ละครั้ง

ข้อความที่ตัดตอนมา:

{$asmmode intel}
procedure example_junkop_in_sha256;
  var s1, t2 : uint32;
  begin
    // Here are parts of the SHA-256 algorithm, in Pascal:
    // s0 {r10d} := ror(a, 2) xor ror(a, 13) xor ror(a, 22)
    // s1 {r11d} := ror(e, 6) xor ror(e, 11) xor ror(e, 25)
    // Here is how I translated them (side by side to show symmetry):
  asm
    MOV r8d, a                 ; MOV r9d, e
    ROR r8d, 2                 ; ROR r9d, 6
    MOV r10d, r8d              ; MOV r11d, r9d
    ROR r8d, 11    {13 total}  ; ROR r9d, 5     {11 total}
    XOR r10d, r8d              ; XOR r11d, r9d
    ROR r8d, 9     {22 total}  ; ROR r9d, 14    {25 total}
    XOR r10d, r8d              ; XOR r11d, r9d

    // Here is the extraneous operation that I removed, causing a speedup
    // s1 is the uint32 variable declared at the start of the Pascal code.
    //
    // I had cleaned up the code, so I no longer needed this variable, and 
    // could just leave the value sitting in the r11d register until I needed
    // it again later.
    //
    // Since copying to RAM seemed like a waste, I removed the instruction, 
    // only to discover that the code ran slower without it.
    {$IFDEF JUNKOPS}
    MOV s1,  r11d
    {$ENDIF}

    // The next part of the code just moves on to another part of SHA-256,
    // maj { r12d } := (a and b) xor (a and c) xor (b and c)
    mov r8d,  a
    mov r9d,  b
    mov r13d, r9d // Set aside a copy of b
    and r9d,  r8d

    mov r12d, c
    and r8d, r12d  { a and c }
    xor r9d, r8d

    and r12d, r13d { c and b }
    xor r12d, r9d

    // Copying the calculated value to the same s1 variable is another speedup.
    // As far as I can tell, it doesn't actually matter what register is copied,
    // but moving this line up or down makes a huge difference.
    {$IFDEF JUNKOPS}
    MOV s1,  r9d // after mov r12d, c
    {$ENDIF}

    // And here is where the two calculated values above are actually used:
    // T2 {r12d} := S0 {r10d} + Maj {r12d};
    ADD r12d, r10d
    MOV T2, r12d

  end
end;

ลองด้วยตัวคุณเอง:

รหัสออนไลน์ที่ GitHubหากคุณต้องการลองด้วยตัวเอง

คำถามของฉัน:

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

7
มีทุกประเภทของคำสั่ง 'ไร้ประโยชน์' ที่จริงสามารถทำหน้าที่ในการทำลายโซ่พึ่งพามาร์คลงทะเบียนทางกายภาพเช่นเกษียณ ฯลฯ การใช้ประโยชน์จากการดำเนินงานเหล่านี้ต้องมีความรู้บางmicroarchitecture คำถามของคุณควรให้ลำดับขั้นตอนสั้น ๆ เป็นตัวอย่างที่ง่ายที่สุดแทนที่จะบอกให้ผู้อื่นมาที่ GitHub
Brett Hale

1
@BrettHale จุดดีขอบคุณ ฉันเพิ่มข้อความที่ตัดตอนมาพร้อมความเห็นบางส่วน การคัดลอกค่าของการลงทะเบียนเพื่อ ram ทำเครื่องหมายการลงทะเบียนเป็นออกแม้ว่าจะใช้ในภายหลังหรือไม่
tangentstorm

9
คุณช่วยเบี่ยงเบนมาตรฐานของค่าเฉลี่ยเหล่านั้นได้ไหม? ไม่มีการบ่งชี้ที่แท้จริงในโพสต์นี้ว่ามีความแตกต่างที่แท้จริง
starwed

2
คุณสามารถลองจับเวลาคำแนะนำโดยใช้คำสั่ง rdtscp และตรวจสอบวงจรนาฬิกาของทั้งสองรุ่นได้หรือไม่?
jakobbotsch

2
อาจเป็นเพราะการจัดตำแหน่งหน่วยความจำได้หรือไม่ ฉันไม่ได้ทำคณิตศาสตร์ด้วยตัวเอง (สันหลังยาว: P) แต่การเพิ่มคำแนะนำหลอกๆอาจทำให้โค้ดของคุณสอดคล้องกับหน่วยความจำ ...
Lorenzo Dematté

คำตอบ:


144

สาเหตุที่เป็นไปได้มากที่สุดของการปรับปรุงความเร็วคือ:

  • การใส่ MOV จะเลื่อนขั้นตอนถัดไปไปยังหน่วยความจำที่แตกต่างกัน
  • หนึ่งในคำแนะนำที่ย้ายเหล่านี้เป็นสาขาที่มีเงื่อนไขสำคัญ
  • สาขานั้นถูกคาดการณ์ไม่ถูกต้องเนื่องจากมีนามแฝงในตารางการทำนายสาขา
  • การย้ายสาขากำจัดนามแฝงและอนุญาตให้สาขาสามารถทำนายได้อย่างถูกต้อง

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

บทช่วยสอนการพยากรณ์สาขาเล็ก ๆ นี้แสดงให้เห็นว่าบัฟเฟอร์การทำนายสาขาทำงานอย่างไร แคชบัฟเฟอร์ถูกทำดัชนีโดยส่วนล่างของที่อยู่ของคำสั่งสาขา วิธีนี้ใช้ได้ดีเว้นแต่สาขาที่ไม่เกี่ยวข้องสองสาขาที่สำคัญจะแชร์บิตที่ต่ำกว่ากัน ในกรณีดังกล่าวคุณจะต้องจบด้วยนามแฝงซึ่งทำให้เกิดสาขาที่ผิดจำนวนมาก (ซึ่งจะไปป์ไลน์คำสั่งและทำให้โปรแกรมของคุณช้าลง)

หากคุณต้องการเข้าใจว่าการคาดคะเนความผิดพลาดของสาขาส่งผลต่อประสิทธิภาพอย่างไรให้ดูที่คำตอบที่ยอดเยี่ยมนี้: https://stackoverflow.com/a/11227902/1001643

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


2
อืมมม ฟังดูมีแนวโน้ม สาขาที่มีเงื่อนไขเท่านั้นในการใช้งาน sha256 นี้คือการตรวจสอบการสิ้นสุดของลูป FOR ในเวลานั้นฉันติดแท็กการแก้ไขนี้เป็นความผิดปกติในคอมไพล์และเพิ่มประสิทธิภาพอย่างต่อเนื่อง หนึ่งในขั้นตอนต่อไปของฉันคือการเขียน Pascal FOR วนซ้ำตัวเองในการชุมนุมซึ่ง ณ จุดนี้คำแนะนำพิเศษเหล่านี้จะไม่มีผลในเชิงบวกอีกต่อไป บางทีรหัสที่สร้างขึ้นจากปาสกาลฟรีนั้นยากกว่าสำหรับตัวประมวลผลที่จะคาดเดาได้ง่ายกว่าตัวนับอย่างง่ายที่ฉันแทนที่ด้วย
tangentstorm

1
@tangentstorm ฟังดูเหมือนบทสรุปที่ดี ตารางการคาดคะเนสาขาไม่ใหญ่มากดังนั้นรายการตารางหนึ่งอาจอ้างถึงสาขามากกว่าหนึ่ง สิ่งนี้สามารถทำให้การคาดการณ์บางอย่างไร้ประโยชน์ ปัญหาจะได้รับการแก้ไขได้อย่างง่ายดายหากหนึ่งในกิ่งที่ขัดแย้งกันเคลื่อนไปยังส่วนอื่นของตาราง การเปลี่ยนแปลงเล็ก ๆ น้อย ๆ สามารถทำให้สิ่งนี้เกิดขึ้นได้ :-)
Raymond Hettinger

1
ฉันคิดว่านี่เป็นคำอธิบายที่สมเหตุสมผลที่สุดของพฤติกรรมเฉพาะที่ฉันสังเกตเห็นดังนั้นฉันจะทำเครื่องหมายสิ่งนี้เป็นคำตอบ ขอบคุณ :)
tangentstorm

3
มีการพูดคุยกันอย่างยอดเยี่ยมเกี่ยวกับปัญหาที่คล้ายกันซึ่งหนึ่งในผู้ติดต่อไปยัง Bochs พบคุณอาจต้องการเพิ่มคำตอบของคุณ: emulators.com/docs/nx25_nostradamus.htm
leander

3
การจัดแนวอินน์มีความสำคัญมากกว่าเป้าหมายสาขา คอขวดถอดรหัสเป็นปัญหาใหญ่สำหรับ Core2 และ Nehalem: มันมักจะมีช่วงเวลาที่ยากลำบากในการทำให้หน่วยประมวลผลไม่ว่าง การแนะนำแคช uop ของ Sandybridge เพิ่มปริมาณงานจำนวนมาก การจัดแนวเป้าหมายสาขาจะทำเนื่องจากปัญหานี้ แต่จะมีผลกับรหัสทั้งหมด
Peter Cordes

80

คุณอาจต้องการอ่านhttp://research.google.com/pubs/pub37077.html

TL; DR: การแทรกคำสั่ง nop แบบสุ่มในโปรแกรมสามารถเพิ่มประสิทธิภาพได้อย่างง่ายดาย 5% หรือมากกว่าและไม่คอมไพเลอร์ไม่สามารถใช้ประโยชน์ได้อย่างง่ายดาย มันมักจะเป็นการรวมกันของการพยากรณ์สาขาและพฤติกรรมแคช แต่มันก็สามารถเป็นได้เช่นแผงสถานีสำรอง (แม้ในกรณีที่ไม่มีเครือข่ายการพึ่งพาที่ทำลายหรือทรัพยากรที่ชัดเจนเกินการสมัครสมาชิกใด ๆ )


1
น่าสนใจ แต่โปรเซสเซอร์ (หรือ FPC) ฉลาดพอที่จะเห็นว่าการเขียน ram เป็น NOP ในกรณีนี้หรือไม่
tangentstorm

8
แอสเซมเบลอร์ไม่ได้รับการปรับปรุง
Marco van de Voort

5
คอมไพเลอร์สามารถใช้ประโยชน์จากมันโดยการเพิ่มประสิทธิภาพที่มีราคาแพงอย่างไม่น่าเชื่อเช่นการสร้างและการทำโปรไฟล์ซ้ำ ๆ กันและจากนั้นปรับเอาท์พุทคอมไพเลอร์ด้วยการจำลองการอบหรืออัลกอริทึมทางพันธุกรรม ฉันได้อ่านเกี่ยวกับงานในพื้นที่นั้น แต่เรากำลังพูดถึงซีพียู 100% อย่างน้อย 5-10 นาทีในการรวบรวมและการเพิ่มประสิทธิภาพที่เกิดขึ้นอาจเป็นรูปแบบหลักของ CPU และแม้กระทั่ง core หรือ microcode revision ที่เฉพาะเจาะจง
AdamIerymenko

ฉันจะไม่เรียกมันว่า NOP แบบสุ่มพวกเขาอธิบายว่าทำไม NOP จึงมีผลดีต่อประสิทธิภาพ (tl; dr: stackoverflow.com/a/5901856/357198 ) และการแทรกแบบสุ่มของ NOP ทำให้ประสิทธิภาพลดลง สิ่งที่น่าสนใจของบทความนี้ก็คือการกำจัด 'ยุทธศาสตร์' NOP โดย GCC ไม่มีผลต่อประสิทธิภาพโดยรวม!
PuercoPop

15

ฉันเชื่อในคำสั่งแอสเซมบลีของ CPU ที่ทันสมัยในขณะที่เป็นเลเยอร์ล่าสุดที่มองเห็นได้สำหรับโปรแกรมเมอร์ที่ให้คำแนะนำในการดำเนินการกับ CPU จริง ๆ แล้วเป็นเลเยอร์หลายชั้นจากการดำเนินการจริงโดย CPU

ซีพียูรุ่นใหม่คือลูกผสมRISC / CISCที่แปลคำสั่ง CISC x86 เป็นคำสั่งภายในที่มี RISC มากกว่าในการทำงาน นอกจากนี้ยังมีตัววิเคราะห์การดำเนินการที่ไม่เป็นไปตามคำทำนายตัวพยากรณ์สาขา "ไมโครฟิวชั่น" ของ Intel ที่พยายามจัดกลุ่มคำสั่งให้มีขนาดใหญ่ขึ้นพร้อมกันในการทำงานพร้อมกัน (เช่นVLIW / Itanium titanic) มีขอบเขตแคชที่สามารถทำให้โค้ดทำงานได้เร็วขึ้นสำหรับ god-Know- ทำไมถ้ามันใหญ่กว่า (อาจจะเป็นตัวควบคุมแคชสล็อตจะชาญฉลาดกว่าหรือเก็บไว้นานกว่า)

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

ผลของคำแนะนำเพิ่มเติมอาจขึ้นอยู่กับรุ่นของ CPU / รุ่น / ผู้ผลิตและไม่น่าจะคาดเดาได้ การเพิ่มประสิทธิภาพภาษาแอสเซมบลีด้วยวิธีนี้จะต้องดำเนินการกับสถาปัตยกรรมซีพียูหลายรุ่นอาจใช้พา ธ การประมวลผลเฉพาะ CPU และเป็นที่พึงปรารถนาสำหรับส่วนของรหัสที่สำคัญจริงๆแม้ว่าคุณจะทำแอสเซมบลีอยู่ก็ตาม


6
คำตอบของคุณค่อนข้างสับสน ในหลาย ๆ สถานที่ดูเหมือนว่าคุณกำลังคาดเดาแม้ว่าสิ่งที่คุณพูดส่วนใหญ่จะถูกต้องก็ตาม
alcuadrado

2
บางทีฉันควรชี้แจง สิ่งที่ฉันสับสนคือการขาดความมั่นใจ
alcuadrado

3
การคาดเดาที่สมเหตุสมผลและการโต้แย้งที่ดีนั้นถูกต้องสมบูรณ์
jturolla

7
ไม่มีใครรู้แน่ชัดว่าทำไม OP ถึงเฝ้าสังเกตพฤติกรรมที่แปลกประหลาดนี้เว้นแต่เป็นวิศวกรของ Intel ที่เข้าถึงอุปกรณ์การวินิจฉัยพิเศษ ดังนั้นคนอื่น ๆ ทุกคนสามารถเดาได้ นั่นไม่ใช่ความผิดของ @ cowarldlydragon
Alex D

2
downvote; ไม่มีสิ่งที่คุณพูดอธิบายพฤติกรรมที่ OP เห็น คำตอบของคุณไม่มีประโยชน์
fuz

0

กำลังเตรียมแคช

การดำเนินการย้ายไปยังหน่วยความจำสามารถเตรียมแคชและทำให้การดำเนินการย้ายครั้งต่อไปเร็วขึ้น CPU มักจะมีสองหน่วยโหลดและหนึ่งหน่วยเก็บ หน่วยโหลดสามารถอ่านจากหน่วยความจำไปยังรีจิสเตอร์ (หนึ่งครั้งต่อรอบ) ซึ่งเป็นหน่วยจัดเก็บจากรีจิสเตอร์ไปยังหน่วยความจำ นอกจากนี้ยังมีหน่วยงานอื่น ๆ ที่ดำเนินการระหว่างการลงทะเบียน หน่วยงานทั้งหมดทำงานในแบบคู่ขนาน ดังนั้นในแต่ละรอบเราอาจทำการดำเนินการหลายอย่างพร้อมกัน แต่ไม่เกินสองโหลดหนึ่งร้านค้าและการดำเนินการลงทะเบียนหลาย โดยปกติแล้วจะมีการดำเนินการอย่างง่ายมากถึง 4 การดำเนินการด้วยการลงทะเบียนธรรมดาการดำเนินการอย่างง่ายสูงสุด 3 การดำเนินการด้วยการลงทะเบียน XMM / YMM และการดำเนินการที่ซับซ้อน 1-2 การลงทะเบียนใด ๆ รหัสของคุณมีการดำเนินการมากมายกับการลงทะเบียนดังนั้นการดำเนินการที่เก็บหน่วยความจำดัมมี่หนึ่งฟรี (เนื่องจากมีการดำเนินการลงทะเบียนมากกว่า 4 ครั้ง) แต่จะจัดเตรียมแคชหน่วยความจำสำหรับการดำเนินการจัดเก็บในภายหลัง หากต้องการทราบว่าหน่วยความจำทำงานอย่างไรโปรดอ้างอิงIntel 64 และ IA-32 คู่มือการใช้งานสถาปัตยกรรมการเพิ่มประสิทธิภาพการอ้างอิง

ทำลายการอ้างอิงเท็จ

แม้ว่าสิ่งนี้ไม่ได้อ้างถึงกรณีของคุณอย่างแน่นอน แต่บางครั้งใช้การดำเนินการ mov แบบ 32 บิตภายใต้ตัวประมวลผล 64 บิต (เช่นในกรณีของคุณ) ใช้เพื่อล้างบิตที่สูงกว่า (32-63) และแบ่งการพึ่งพาโซ่

เป็นที่ทราบกันดีว่าภายใต้ x86-64 การใช้ตัวถูกดำเนินการ 32 บิตจะเป็นการล้างบิตที่สูงกว่าของการลงทะเบียน 64 บิต โปรดอ่านหัวข้อที่เกี่ยวข้อง - 3.4.1.1 - จากคู่มือผู้พัฒนาซอฟต์แวร์สถาปัตยกรรมIntel® 64 และ IA-32 เล่มที่ 1 :

ตัวถูกดำเนินการแบบ 32 บิตสร้างผลลัพธ์แบบ 32 บิตซึ่งขยายเป็นศูนย์ไปเป็นผลลัพธ์แบบ 64 บิตในการลงทะเบียนเอนกประสงค์

ดังนั้นคำแนะนำในการเคลื่อนย้ายซึ่งอาจดูเหมือนไร้ประโยชน์ตั้งแต่แรกเห็นให้ล้างค่าการลงทะเบียนที่เหมาะสมให้สูงขึ้น มันให้อะไรกับเรา มันแบ่งโซ่การพึ่งพาและช่วยให้คำสั่งในการดำเนินการแบบขนานในลำดับสุ่มโดยอัลกอริทึม Out-of-Orderดำเนินการภายในโดย CPU ตั้งแต่ Pentium Pro ในปี 1995

ข้อความอ้างอิงจากคู่มืออ้างอิงการเพิ่มประสิทธิภาพสถาปัตยกรรมIntel® 64 และ IA-32 , ส่วน 3.5.1.8:

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

แอสเซมบลี / คอมไพเลอร์การเข้ารหัสกฎ 37. (ผลกระทบ M, MH ทั่วไป) : แบ่งการพึ่งพาส่วนต่าง ๆ ของการลงทะเบียนระหว่างคำแนะนำโดยการดำเนินการลงทะเบียน 32 บิตแทนการลงทะเบียนบางส่วน สำหรับการเคลื่อนไหวสิ่งนี้สามารถทำได้ด้วยการเคลื่อนที่แบบ 32 บิตหรือโดยใช้ MOVZX

MOVZX และ MOV ที่มีตัวถูกดำเนินการ 32 บิตสำหรับ x64 เทียบเท่ากัน - พวกเขาทั้งหมดแบ่งการพึ่งพาเครือข่าย

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

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

ฉันคิดว่าคุณเห็นแล้วว่ามันชัดเจนเกินไป


ทั้งหมดนี้เป็นความจริง แต่ไม่มีส่วนเกี่ยวข้องกับรหัสที่แสดงในคำถาม
Cody Gray

@CodyGray - ขอบคุณสำหรับความคิดเห็นของคุณ ฉันได้แก้ไขการตอบกลับและเพิ่มบทเกี่ยวกับเคส - นั่นคือ mov ไปยังหน่วยความจำที่ล้อมรอบด้วยการดำเนินการรีจิสเตอร์เตรียมแคชและมันฟรีเนื่องจากหน่วยร้านค้าไม่มีการใช้งานอยู่แล้ว ดังนั้นการดำเนินการร้านค้าที่ตามมาจะเร็วขึ้น
Maxim Masiutin

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