ต้นทุนการดำเนินการของอะตอม


92

ค่าใช้จ่ายของการดำเนินการของอะตอมคืออะไร (เปรียบเทียบและแลกเปลี่ยนหรือเพิ่ม / ลดอะตอม)? กินรอบเท่าไหร่? มันจะหยุดโปรเซสเซอร์อื่นบน SMP หรือ NUMA หรือจะบล็อกการเข้าถึงหน่วยความจำหรือไม่ จะล้างบัฟเฟอร์เรียงลำดับใหม่ใน CPU ที่ไม่ได้รับคำสั่งหรือไม่?

จะมีผลอะไรกับแคชบ้าง?

ฉันสนใจซีพียูรุ่นใหม่ยอดนิยม: x86, x86_64, PowerPC, SPARC, Itanium


@ เจสันเอสใด ๆ ความแตกต่างระหว่าง cas และ atomic inc / dec เป็นเรื่องเล็กน้อย
osgx

2
การดำเนินการของอะตอมบน x86 จะช้าลงเนื่องจากมีการแย่งชิงที่อยู่หน่วยความจำมากขึ้น ฉันเชื่อว่าโดยทั่วไปแล้วพวกมันจะมีลำดับความสำคัญช้ากว่าการดำเนินการที่ไม่ได้ล็อค แต่สิ่งนี้จะแตกต่างกันไปขึ้นอยู่กับการดำเนินการการช่วงชิงและอุปสรรคด้านความจำที่ใช้
Stephen Nutt

อืม การเขียนดูเหมือนจะเป็นอะตอมบน x86 'ทำความเข้าใจกับเคอร์เนล Linux' -> spin_unlock
osgx

การเขียนแบบ 32 บิตเป็นแบบอะตอมใน Java กล่าวคือเป็นอะตอมแบบพกพา (แต่ไม่มีความหมายของสิ่งกีดขวางหน่วยความจำดังนั้นจึงมักไม่เพียงพอสำหรับตัวชี้) โดยปกติการเพิ่ม 1 จะไม่ใช้อะตอมเว้นแต่คุณจะเพิ่มคำนำหน้า LOCK เกี่ยวกับเคอร์เนลลินุกซ์ไม่จำเป็นต้องดูที่ spin_unlock โปรดดูในรุ่นปัจจุบัน arch / x86 / include / asm / atomic_32.h (เคยเป็น include / asm-i386 / atomic.h)
Blaisorblade

@Blaisorblade, JAva ไม่ได้อยู่ที่นี่ ค่าใช้จ่ายในการดำเนินการ LOCKed คืออะไร?
osgx

คำตอบ:


61

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

ค่าใช้จ่ายของคำนำหน้า x86 LOCK (รวมถึงlock cmpxchgอะตอม CAS) ก่อน PentiumPro (ตามที่อธิบายไว้ในเอกสาร) คือการเข้าถึงหน่วยความจำ (เช่นการพลาดแคช) + การหยุดการทำงานของหน่วยความจำโดยโปรเซสเซอร์อื่น + การโต้แย้งใด ๆ กับโปรเซสเซอร์อื่น พยายามล็อครถบัส อย่างไรก็ตามเนื่องจาก PentiumPro สำหรับหน่วยความจำที่สามารถแคชการเขียนกลับได้ตามปกติ (หน่วยความจำทั้งหมดที่แอปเกี่ยวข้องเว้นแต่คุณจะพูดคุยโดยตรงกับฮาร์ดแวร์) แทนที่จะบล็อกการทำงานของหน่วยความจำทั้งหมดจะมีการบล็อกเฉพาะบรรทัดแคชที่เกี่ยวข้อง (ตามลิงก์ในคำตอบของ @ osgx ) .

กล่าวคือความล่าช้าหลักในการตอบรับการแชร์ MESI และคำขอ RFO สำหรับสายจนกว่าจะถึงส่วนเก็บของการlockดำเนินการ ed จริง สิ่งนี้เรียกว่า "การล็อกแคช" และมีผลกับแคชบรรทัดเดียวเท่านั้น คอร์อื่น ๆ สามารถโหลด / จัดเก็บหรือแม้แต่ CASing บรรทัดอื่น ๆ ได้ในเวลาเดียวกัน


จริงๆแล้วกรณี CAS อาจซับซ้อนกว่านี้ตามที่อธิบายไว้ในหน้านี้โดยไม่มีการกำหนดเวลา แต่มีคำอธิบายเชิงลึกโดยวิศวกรที่น่าเชื่อถือ (อย่างน้อยสำหรับกรณีการใช้งานปกติที่คุณทำการโหลดบริสุทธิ์ก่อน CAS จริง)

ก่อนที่จะลงรายละเอียดมากเกินไปฉันจะบอกว่าการดำเนินการที่ LOCKed มีค่าใช้จ่ายแคชหนึ่งครั้ง + การขัดแย้งที่เป็นไปได้กับโปรเซสเซอร์อื่นบนแคชไลน์เดียวกันในขณะที่ CAS + โหลดก่อนหน้า (ซึ่งเกือบจะจำเป็นตลอดเวลายกเว้นใน mutexes ซึ่งคุณมักจะ CAS 0 และ 1) อาจเสียค่าแคชสองครั้ง

เขาอธิบายว่า load + CAS ในตำแหน่งเดียวอาจทำให้เสียแคชสองครั้งได้เช่น Load-Linked / Store-Conditional (ดูที่นั่นในภายหลัง) คำอธิบายของเขาอาศัยอยู่กับความรู้ของโปรโตคอลการเชื่อมโยงกันแคช MESI ใช้ 4 สถานะสำหรับ cacheline: M (odified), E (xclusive), S (hared), I (nvalid) (ดังนั้นจึงเรียกว่า MESI) ตามที่อธิบายไว้ด้านล่างหากจำเป็น สถานการณ์อธิบายมีดังต่อไปนี้:

  • LOAD ทำให้แคชพลาด - แคชไลน์ที่เกี่ยวข้องถูกโหลดจากหน่วยความจำในสถานะ Shared (เช่นโปรเซสเซอร์อื่น ๆ ยังคงได้รับอนุญาตให้เก็บแคชไลน์นั้นไว้ในหน่วยความจำไม่อนุญาตให้ทำการเปลี่ยนแปลงในสถานะนี้) หากตำแหน่งอยู่ในหน่วยความจำแคชพลาดนี้จะถูกข้ามไป ต้นทุนที่เป็นไปได้: พลาดแคช 1 ครั้ง (ข้ามไปหากแคชไลน์อยู่ในสถานะแชร์เอกสิทธิ์หรือดัดแปลงกล่าวคือข้อมูลอยู่ในแคช L1 ของ CPU นี้)
  • โปรแกรมจะคำนวณค่าใหม่ที่จะจัดเก็บ
  • และรันคำสั่งอะตอม CAS
    • ต้องหลีกเลี่ยงการแก้ไขพร้อมกันดังนั้นจึงต้องลบสำเนาของแคชไลน์ออกจากแคชของซีพียูอื่นเพื่อย้ายแคชไลน์ไปยังสถานะเอกสิทธิ์ ต้นทุนที่เป็นไปได้: พลาดแคช 1 ครั้ง สิ่งนี้ไม่จำเป็นหากเป็นเจ้าของ แต่เพียงผู้เดียวอยู่แล้วเช่นในสถานะ Exclusive หรือ Modified ในทั้งสองสถานะไม่มีซีพียูอื่นใดที่ถือแคชไลน์ แต่ในสถานะเอกสิทธิ์นั้นยังไม่ได้รับการแก้ไข (ยัง)
    • หลังจากการสื่อสารนี้ตัวแปรจะถูกแก้ไขในแคชภายในของ CPU ของเราซึ่ง ณ จุดนั้นจะมองเห็นได้ทั่วโลกสำหรับซีพียูอื่น ๆ ทั้งหมด (เนื่องจากแคชของมันเชื่อมโยงกับของเรา) ในที่สุดมันจะถูกเขียนไปยังหน่วยความจำหลักตามอัลกอริทึมปกติ
    • โปรเซสเซอร์อื่นที่พยายามอ่านหรือแก้ไขตัวแปรนั้นก่อนอื่นจะต้องได้รับแคชไลน์นั้นในโหมดแชร์หรือเอ็กซ์คลูซีฟและการทำเช่นนั้นจะติดต่อโปรเซสเซอร์นี้และรับแคชไลน์เวอร์ชันที่อัปเดต การดำเนินการที่ถูกล็อกจะทำให้เสียค่าแคชเท่านั้น (เนื่องจากแคชไลน์จะถูกร้องขอโดยตรงในสถานะพิเศษ)

ในทุกกรณีการร้องขอแคชไลน์สามารถหยุดการทำงานได้โดยโปรเซสเซอร์อื่นที่แก้ไขข้อมูลแล้ว


เหตุใดการเปลี่ยนสถานะของต้นทุนซีพียูอื่น ๆ เนื่องจากการพลาดแคช 1 ครั้ง
osgx

1
เนื่องจากเป็นการสื่อสารภายนอก CPU จึงช้ากว่าการเข้าถึงแคช ในขณะที่แคชพลาดจะต้องส่งผ่านจากซีพียูอื่น ๆ อยู่ดี จริงๆแล้วอาจเป็นกรณีที่การพูดคุยกับซีพียูตัวอื่นเร็วกว่าการพูดคุยกับหน่วยความจำหากมีการใช้การเชื่อมต่อโดยตรงเช่น AMD Hypertransport (ตั้งแต่ครั้งใหญ่ที่ผ่านมา) หรือ Intel QuickPath Interconnect จาก Intel บนโปรเซสเซอร์ Xeon รุ่นล่าสุด อิงจาก Nehalem มิฉะนั้นการสื่อสารกับซีพียูอื่นจะเกิดขึ้นบน FSB เดียวกันกับหน่วยความจำ ค้นหา HyperTransport และ Front Side Bus ใน Wikipedia สำหรับข้อมูลเพิ่มเติม
Blaisorblade

ว้าวไม่เคยคิดว่าของเขาแพงขนาดนี้การพลาดแคชอาจมีได้ไม่กี่พันรอบ
Lothar

2
จริงๆ? รูปที่ฉันคุ้นเคยคือ: หนึ่งร้อยรอบสำหรับแคชพลาดและหลายพันรอบสำหรับสวิตช์บริบท / สิทธิ์ (รวมถึง syscalls)
Blaisorblade

1
การพลาดแคชไม่ใช่สองสามพันรอบ! ประมาณ 100ns ซึ่งโดยทั่วไปจะเป็น 300-350 รอบ CPU ....
user997112

37

ฉันทำโปรไฟล์ด้วยการตั้งค่าต่อไปนี้: เครื่องทดสอบ (AMD Athlon64 x2 3800+) ถูกบูตเปลี่ยนเป็นโหมดยาว (ปิดใช้งานการขัดจังหวะ) และคำสั่งที่น่าสนใจจะดำเนินการแบบวนซ้ำ 100 ครั้งยกเลิกการควบคุมและ 1,000 รอบลูป เนื้อหาของลูปถูกจัดแนวเป็น 16 ไบต์ เวลาถูกวัดด้วยคำสั่ง rdtsc ก่อนและหลังลูป นอกจากนี้ยังมีการดำเนินการลูปจำลองโดยไม่มีคำสั่งใด ๆ (ซึ่งวัดได้ 2 รอบต่อการวนซ้ำและ 14 รอบสำหรับส่วนที่เหลือ) และผลลัพธ์จะถูกลบออกจากผลลัพธ์ของเวลาการทำโปรไฟล์คำสั่ง

คำแนะนำต่อไปนี้วัดได้:

  • " lock cmpxchg [rsp - 8], rdx" (ทั้งที่มีการจับคู่เปรียบเทียบและไม่ตรงกัน)
  • " lock xadd [rsp - 8], rdx",
  • " lock bts qword ptr [rsp - 8], 1"

ในทุกกรณีเวลาที่วัดได้ประมาณ 310 รอบข้อผิดพลาดอยู่ที่ประมาณ +/- 8 รอบ

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

ในการประเมินค่าใช้จ่ายของคำสั่งที่ถูกล็อกในแคชพลาดฉันได้เพิ่มwbinvldคำสั่งก่อนคำสั่งที่ถูกล็อกและใส่เครื่องหมายwbinvldบวกadd [rsp - 8], raxลงในลูปการเปรียบเทียบ ในทั้งสองกรณีค่าใช้จ่ายประมาณ 80,000 รอบต่อคู่คำสั่ง! ในกรณีที่ล็อค bts เวลาต่างกันประมาณ 180 รอบต่อคำสั่ง

โปรดทราบว่านี่คือทรูพุตซึ่งกันและกัน แต่เนื่องจากการดำเนินการที่ล็อกเป็นการดำเนินการแบบอนุกรมจึงอาจไม่มีความแตกต่างกับเวลาในการตอบสนอง

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

ในการบูตเครื่องฉันใช้ FreeLdr เวอร์ชัน x64 จากโครงการ ReactOS นี่คือซอร์สโค้ด asm:

#define LOOP_COUNT 1000
#define UNROLLED_COUNT 100

PUBLIC ProfileDummy
ProfileDummy:

    cli

    // Get current TSC value into r8
    rdtsc
    mov r8, rdx
    shl r8, 32
    or r8, rax

    mov rcx, LOOP_COUNT
    jmp looper1

.align 16
looper1:

REPEAT UNROLLED_COUNT
    // nothing, or add something to compare against
ENDR

    dec rcx
    jnz looper1

    // Put new TSC minus old TSC into rax
    rdtsc
    shl rdx, 32
    or rax, rdx
    sub rax, r8

    ret

PUBLIC ProfileFunction
ProfileFunction:

    cli

    rdtsc
    mov r8, rdx
    shl r8, 32
    or r8, rax
    mov rcx, LOOP_COUNT

    jmp looper2

.align 16
looper2:

REPEAT UNROLLED_COUNT
    // Put here the code you want to profile
    // make sure it doesn't mess up non-volatiles or r8
    lock bts qword ptr [rsp - 8], 1
ENDR

    dec rcx
    jnz looper2

    rdtsc
    shl rdx, 32
    or rax, rdx
    sub rax, r8

    ret

ขอบคุณ! คุณสามารถเผยแพร่รหัสทดสอบของคุณหรือทดสอบ Core2 / Core i3 / i5 / i7 ด้วยตัวเองได้หรือไม่? คอร์ทั้งหมดเริ่มต้นในการตั้งค่าการทดสอบของคุณหรือไม่
osgx

ฉันเพิ่มซอร์สโค้ด เริ่มต้นเพียงคอร์เดียว ชอบที่จะเห็นผลลัพธ์จากเครื่องอื่น ๆ
Timo

CLFLUSH ควรเป็นวิธีล้างแคชที่เบากว่า WBINVD ของแคชทั้งหมด WBINVDจะล้างแคชคำแนะนำด้วยซึ่งทำให้แคชส่วนเกินพลาด
Peter Cordes

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

4

บนรถบัสที่ใช้ SMP คำนำหน้าอะตอมLOCKไม่ยืนยัน (เปิด) LOCK#สัญญาณลวดรถบัส จะห้ามไม่ให้ cpus / อุปกรณ์อื่น ๆ บนรถบัสใช้งาน

หนังสือPpro & P2 http://books.google.com/books?id=3gDmyIYvFH4C&pg=PA245&dq=lock+instruction+pentium&lr=&ei=_E61S5ehLI78zQSzrqwI&cd=1#v=onepage&q=lock%20instruction%20pentium&f=falseหน้า 244-246

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

/ about SMP / semaphore อยู่ในแคชในสถานะ S ... การออกทรานแซคชันการอ่านและทำให้ไม่ถูกต้องสำหรับวันที่ 0 ไบต์ (นี่คือการฆ่า / สำเนาที่ใช้ร่วมกันของบรรทัดแคชในซีพียูที่อยู่ติดกัน /)

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