ใน C ++ 11 โดยปกติไม่เคยใช้volatile
สำหรับเธรดเฉพาะสำหรับ MMIO
แต่ TL: DR มัน "ทำงาน" เหมือนกับ atomic กับmo_relaxed
ฮาร์ดแวร์ที่มีแคชที่สอดคล้องกัน (เช่นทุกอย่าง); ก็เพียงพอแล้วที่จะหยุดคอมไพเลอร์ที่เก็บ vars ไว้ในรีจิสเตอร์ atomic
ไม่จำเป็นต้องมีอุปสรรคด้านหน่วยความจำในการสร้าง atomicity หรือการมองเห็นระหว่างเธรดเพียงเพื่อให้เธรดปัจจุบันรอก่อน / หลังการดำเนินการเพื่อสร้างลำดับระหว่างการเข้าถึงของเธรดนี้ไปยังตัวแปรต่างๆ mo_relaxed
ไม่ต้องมีอุปสรรคใด ๆ เพียงแค่โหลดจัดเก็บหรือ RMW
สำหรับอะตอมม้วนของคุณเองด้วยvolatile
(และอินไลน์ asm สำหรับอุปสรรค) ในวันเก่าเสียก่อน C ++ 11 std::atomic
,volatile
เป็นวิธีที่ดีเท่านั้นที่จะได้รับบางสิ่งบางอย่างในการทำงาน แต่ขึ้นอยู่กับสมมติฐานมากมายเกี่ยวกับวิธีการใช้งานและไม่เคยได้รับการรับรองจากมาตรฐานใด ๆ
ตัวอย่างเช่นเคอร์เนลลินุกซ์ยังคงใช้อะตอมที่รีดด้วยมือของตัวเองด้วยvolatile
แต่รองรับการใช้งาน C เฉพาะบางส่วนเท่านั้น (GNU C เสียงดังและอาจเป็น ICC) ส่วนหนึ่งเป็นเพราะส่วนขยาย GNU C และไวยากรณ์ asm แบบอินไลน์และความหมาย แต่ยังขึ้นอยู่กับสมมติฐานบางประการเกี่ยวกับวิธีการทำงานของคอมไพเลอร์
เกือบจะเป็นตัวเลือกที่ผิดสำหรับโครงการใหม่ คุณสามารถใช้std::atomic
(มีstd::memory_order_relaxed
) volatile
เพื่อให้ได้คอมไพเลอร์ที่จะปล่อยรหัสเครื่องเดียวที่มีประสิทธิภาพให้คุณได้ด้วย std::atomic
กับmo_relaxed
ล้าสมัยvolatile
เพื่อวัตถุประสงค์ในการทำเกลียว (ยกเว้นบางทีเพื่อแก้ไขข้อบกพร่องที่ไม่ได้รับการปรับให้เหมาะสมกับatomic<double>
คอมไพเลอร์บางตัว)
การใช้งานภายในของstd::atomic
คอมไพเลอร์กระแสหลัก (เช่น gcc และ clang) ไม่ได้ใช้แค่volatile
ภายในเท่านั้น คอมไพเลอร์เผยให้เห็นโหลดอะตอมจัดเก็บและฟังก์ชันในตัว RMW โดยตรง (เช่นGNU C __atomic
builtinsซึ่งทำงานบนวัตถุ "ธรรมดา")
ระเหยสามารถใช้ได้ในทางปฏิบัติ (แต่อย่าทำ)
ที่กล่าวว่าvolatile
สามารถใช้งานได้ในทางปฏิบัติสำหรับสิ่งต่างๆเช่นการexit_now
ตั้งค่าสถานะในการใช้งาน C ++ ที่มีอยู่ทั้งหมด (?) บนซีพียูจริงเนื่องจากซีพียูทำงานอย่างไร (แคชที่สอดคล้องกัน) และสมมติฐานที่ใช้ร่วมกันเกี่ยวกับวิธีการvolatile
ทำงาน แต่อย่างอื่นไม่มากและไม่แนะนำ คำตอบนี้มีจุดประสงค์เพื่ออธิบายว่า CPU และการใช้งาน C ++ ที่มีอยู่ทำงานอย่างไร หากคุณไม่สนใจสิ่งนั้นสิ่งที่คุณต้องรู้ก็คือstd::atomic
มี mo_relaxed obsoletes volatile
สำหรับเธรด
(มาตรฐาน ISO C ++ ค่อนข้างคลุมเครือเพียงแค่บอกว่าการvolatile
เข้าถึงควรได้รับการประเมินอย่างเคร่งครัดตามกฎของเครื่องนามธรรม C ++ ไม่ใช่การปรับให้เหมาะสมเนื่องจากการใช้งานจริงจะใช้พื้นที่แอดเดรสหน่วยความจำของเครื่องเพื่อจำลองพื้นที่ที่อยู่ C ++ ซึ่งหมายความว่าการvolatile
อ่านและการกำหนดต้องคอมไพล์เพื่อโหลด / จัดเก็บคำสั่งเพื่อเข้าถึงการแสดงวัตถุในหน่วยความจำ)
ดังที่คำตอบอื่นชี้ให้เห็นexit_now
แฟล็กเป็นกรณีง่ายๆของการสื่อสารระหว่างเธรดที่ไม่จำเป็นต้องมีการซิงโครไนซ์ใด ๆ : ไม่ได้เผยแพร่ว่าเนื้อหาอาร์เรย์พร้อมหรืออะไรทำนองนั้น มีเพียงร้านค้าที่สังเกตเห็นได้ทันทีจากการโหลดที่ไม่ได้รับการปรับให้เหมาะสมในเธรดอื่น
// global
bool exit_now = false;
// in one thread
while (!exit_now) { do_stuff; }
// in another thread, or signal handler in this thread
exit_now = true;
หากไม่มีการระเหยหรือปรมาณูกฎ as-if และสมมติฐานของการไม่มีข้อมูลการแข่งขัน UB ช่วยให้คอมไพเลอร์สามารถปรับให้เหมาะสมเป็น asm ที่ตรวจสอบแฟล็กเพียงครั้งเดียวก่อนที่จะเข้าสู่ (หรือไม่) ลูปที่ไม่มีที่สิ้นสุด นี่คือสิ่งที่เกิดขึ้นในชีวิตจริงสำหรับคอมไพเลอร์ตัวจริง (และโดยปกติจะปรับให้เหมาะสมที่สุดdo_stuff
เนื่องจากลูปไม่เคยออกดังนั้นโค้ดใด ๆ ในภายหลังที่อาจใช้ผลลัพธ์จะไม่สามารถเข้าถึงได้หากเราเข้าสู่ลูป)
// Optimizing compilers transform the loop into asm like this
if (!exit_now) { // check once before entering loop
while(1) do_stuff; // infinite loop
}
โปรแกรมมัลติเธรดติดอยู่ในโหมดปรับให้เหมาะสม แต่ทำงานตามปกติใน -O0เป็นตัวอย่าง (พร้อมคำอธิบายเอาต์พุต asm ของ GCC) ว่าสิ่งนี้เกิดขึ้นกับ GCC บน x86-64 ได้อย่างไร นอกจากนี้การเขียนโปรแกรม MCU - การเพิ่มประสิทธิภาพ C ++ O2 หยุดลงในขณะที่วนซ้ำบนอุปกรณ์อิเล็กทรอนิกส์ SE แสดงตัวอย่างอื่น
โดยปกติเราต้องการการเพิ่มประสิทธิภาพเชิงรุกที่ CSE และรอกโหลดจากลูปรวมถึงตัวแปรส่วนกลาง
ก่อน C ++ 11 volatile bool exit_now
เป็นวิธีหนึ่งในการทำให้งานนี้เป็นไปตามที่ตั้งใจไว้ (ในการใช้งาน C ++ ปกติ) แต่ใน C ++ 11 UB การแข่งขันข้อมูลยังคงมีผลบังคับใช้volatile
ดังนั้นจึงไม่ได้รับการรับรองจากมาตรฐาน ISO ว่าจะทำงานได้ทุกที่แม้จะสมมติว่าแคชที่เชื่อมโยงกันของ HW
โปรดทราบว่าสำหรับประเภทที่กว้างขึ้นvolatile
ไม่รับประกันว่าจะไม่มีการฉีกขาด ฉันไม่สนใจความแตกต่างตรงนี้bool
เพราะมันไม่ใช่ปัญหาในการใช้งานปกติ แต่นั่นก็เป็นส่วนหนึ่งของสาเหตุที่volatile
ยังคงอยู่ภายใต้การแข่งขันของข้อมูล UB แทนที่จะเทียบเท่ากับปรมาณูที่ผ่อนคลาย
โปรดทราบว่า "ตามที่ตั้งใจไว้" ไม่ได้หมายความว่าเธรดกำลังexit_now
รอให้เธรดอื่นออกจริง หรือแม้กระทั่งว่าจะรอให้exit_now=true
ร้านค้าระเหยปรากฏให้เห็นได้ทั่วโลกก่อนที่จะดำเนินการต่อในเธรดนี้ในภายหลัง ( atomic<bool>
ด้วยค่าเริ่มต้นmo_seq_cst
จะทำให้รอก่อนที่จะโหลด seq_cst ในภายหลังเป็นอย่างน้อยใน ISA จำนวนมากคุณจะได้รับสิ่งกีดขวางเต็มหลังร้านค้า)
C ++ 11 จัดเตรียมวิธีที่ไม่ใช่ UB ซึ่งรวบรวมแบบเดียวกัน
ธง "วิ่งต่อไป" หรือ "ออกทันที" ควรใช้std::atomic<bool> flag
กับmo_relaxed
การใช้
flag.store(true, std::memory_order_relaxed)
while( !flag.load(std::memory_order_relaxed) ) { ... }
ที่จะทำให้คุณ asm เดียวกันแน่นอน (โดยไม่มีคำแนะนำอุปสรรคแพง) volatile flag
ที่คุณต้องการได้รับจาก
รวมทั้งไม่มีการฉีกขาด, atomic
นอกจากนี้ยังช่วยให้คุณสามารถที่จะเก็บไว้ในหนึ่งหัวข้อและโหลดในอื่นโดยไม่ UB ดังนั้นคอมไพเลอร์ไม่สามารถยกภาระออกจากวงที่ (สมมติฐานของการไม่มี UB การแข่งขันข้อมูลคือสิ่งที่อนุญาตให้มีการเพิ่มประสิทธิภาพเชิงรุกที่เราต้องการสำหรับวัตถุที่ไม่ระเหยที่ไม่ใช่อะตอม) คุณสมบัติatomic<T>
นี้ค่อนข้างเหมือนกับสิ่งที่volatile
ทำกับโหลดบริสุทธิ์และร้านค้าที่บริสุทธิ์
atomic<T>
นอกจากนี้ยังสร้าง+=
และอื่น ๆ ในการดำเนินการ RMW แบบปรมาณู (มีราคาแพงกว่าการโหลดปรมาณูอย่างมีนัยสำคัญในการดำเนินการชั่วคราวจากนั้นจึงจัดเก็บอะตอมแยกต่างหากหากคุณไม่ต้องการ RMW แบบปรมาณูให้เขียนรหัสของคุณด้วย local ชั่วคราว)
ด้วยการseq_cst
สั่งซื้อเริ่มต้นที่คุณจะได้รับwhile(!flag)
นอกจากนี้ยังเพิ่มการรับประกันการสั่งซื้อด้วย การเข้าถึงที่ไม่ใช่อะตอมและการเข้าถึงอะตอมอื่น ๆ
(ตามทฤษฎีแล้วมาตรฐาน ISO C ++ ไม่ได้กำหนดว่าการเพิ่มประสิทธิภาพของอะตอมในการคอมไพล์ - ไทม์ แต่ในทางปฏิบัติคอมไพเลอร์ไม่ได้เป็นเพราะไม่มีวิธีควบคุมว่าเมื่อใดที่จะไม่เป็นไปตามนั้นมีบางกรณีที่volatile atomic<T>
อาจไม่ได้ สามารถควบคุมการเพิ่มประสิทธิภาพของอะตอมได้เพียงพอหากคอมไพเลอร์ทำการปรับให้เหมาะสมดังนั้นสำหรับตอนนี้คอมไพเลอร์ทำไม่ได้ดูเหตุใดคอมไพเลอร์จึงไม่ผสาน std ที่ซ้ำซ้อน :: atomic เขียน โปรดทราบว่า wg21 / p0062 แนะนำให้ใช้volatile atomic
ในโค้ดปัจจุบันเพื่อป้องกันการเพิ่มประสิทธิภาพของ อะตอม.)
volatile
ใช้งานได้จริงกับ CPU จริง (แต่ยังไม่ได้ใช้งาน)
แม้จะมีรุ่นหน่วยความจำที่ไม่ค่อยสั่งซื้อ (Non-x86) แต่ไม่ได้ใช้งานจริงมันใช้atomic<T>
กับmo_relaxed
แทน !! volatile
จุดของส่วนนี้คือการเข้าใจผิดเกี่ยวกับการทำงานอยู่ซีพียูวิธีจริงไม่ได้ที่จะปรับ หากคุณกำลังเขียนรหัสแบบไม่ต้องล็อกคุณอาจสนใจเกี่ยวกับประสิทธิภาพ การทำความเข้าใจแคชและต้นทุนของการสื่อสารระหว่างเธรดมักมีความสำคัญต่อประสิทธิภาพที่ดี
ซีพียูจริงมีแคช / หน่วยความจำที่ใช้ร่วมกัน: หลังจากที่เก็บจากคอร์หนึ่งกลายเป็นที่มองเห็นได้ทั่วโลกไม่มีคอร์อื่นใดที่สามารถโหลดค่าที่ค้างได้ (โปรดดูMyths Programmers Believe เกี่ยวกับ CPU Cachesซึ่งพูดถึง Java volatiles ซึ่งเทียบเท่ากับ C ++ atomic<T>
พร้อมลำดับหน่วยความจำ seq_cst)
เมื่อฉันพูดว่าloadฉันหมายถึงคำสั่ง asm ที่เข้าถึงหน่วยความจำ นั่นคือสิ่งที่การvolatile
เข้าถึงทำให้มั่นใจได้และไม่ใช่สิ่งเดียวกับการแปลงค่า lvalue-to-rvalue ของตัวแปร C ++ ที่ไม่ใช่อะตอม / ไม่ระเหย (เช่นlocal_tmp = flag
หรือwhile(!flag)
)
สิ่งเดียวที่คุณต้องเอาชนะคือการเพิ่มประสิทธิภาพเวลาคอมไพล์ซึ่งจะไม่โหลดซ้ำหลังจากการตรวจสอบครั้งแรก โหลด + ตรวจสอบการทำซ้ำแต่ละครั้งก็เพียงพอแล้วโดยไม่ต้องสั่งใด ๆ หากไม่มีการซิงโครไนซ์ระหว่างเธรดนี้และเธรดหลักก็ไม่มีความหมายที่จะพูดถึงเวลาที่ร้านค้าเกิดขึ้นหรือลำดับของการโหลด wrt การดำเนินการอื่น ๆ ในลูป เฉพาะเมื่อเธรดนี้มองเห็นได้เท่านั้นคือสิ่งที่สำคัญ เมื่อคุณเห็นชุดค่าสถานะ exit_now คุณจะออก แฝงอินเตอร์-core บน x86 ทั่วไป Xeon สามารถเป็นสิ่งที่ชอบ 40ns ระหว่างแกนทางแยก
ในทางทฤษฎี: เธรด C ++ บนฮาร์ดแวร์ที่ไม่มีแคชที่สอดคล้องกัน
ฉันไม่เห็นวิธีใดที่จะมีประสิทธิภาพจากระยะไกลด้วย ISO C ++ ที่บริสุทธิ์โดยไม่ต้องให้โปรแกรมเมอร์ทำการล้างข้อมูลอย่างชัดเจนในซอร์สโค้ด
ในทางทฤษฎีคุณสามารถมี C ++ การดำเนินงานในเครื่องที่ไม่ได้เป็นเช่นนี้ต้องวูบวาบอย่างชัดเจนคอมไพเลอร์ที่สร้างขึ้นเพื่อให้สิ่งที่มองเห็นหัวข้ออื่น ๆ บนแกนอื่น (หรือสำหรับการอ่านเพื่อไม่ใช้สำเนาที่อาจจะเก่า) มาตรฐาน C ++ ไม่ได้ทำให้สิ่งนี้เป็นไปไม่ได้ แต่โมเดลหน่วยความจำของ C ++ ได้รับการออกแบบมาเพื่อให้มีประสิทธิภาพบนเครื่องหน่วยความจำที่ใช้ร่วมกัน เช่นมาตรฐาน C ++ ยังพูดถึง "การเชื่อมโยงการอ่าน - การอ่าน", "การเชื่อมโยงกันของการอ่าน - เขียน" ฯลฯ หมายเหตุหนึ่งในมาตรฐานยังชี้ถึงการเชื่อมต่อกับฮาร์ดแวร์:
http://eel.is/c++draft/intro.races#19
[หมายเหตุ: ข้อกำหนดการเชื่อมโยงกันทั้งสี่ประการก่อนหน้านี้ไม่อนุญาตให้คอมไพเลอร์เรียงลำดับการดำเนินการอะตอมไปยังวัตถุชิ้นเดียวได้อย่างมีประสิทธิภาพแม้ว่าการดำเนินการทั้งสองจะเป็นการผ่อนแรงก็ตาม สิ่งนี้ทำให้การรับประกันการเชื่อมโยงกันของแคชมีประสิทธิภาพโดยฮาร์ดแวร์ส่วนใหญ่ที่มีให้สำหรับการทำงานของอะตอม C ++ - หมายเหตุ]
ไม่มีกลไกใดสำหรับrelease
ร้านค้าที่จะล้างตัวเองและช่วงที่อยู่ที่เลือกเพียงไม่กี่ช่วงเท่านั้น แต่จะต้องซิงค์ทุกอย่างเนื่องจากไม่ทราบว่าเธรดอื่น ๆ อาจต้องการอ่านอะไรหากการรับโหลดของพวกเขาเห็นที่เก็บเผยแพร่นี้ (สร้าง ลำดับการปล่อยที่สร้างความสัมพันธ์ที่เกิดขึ้นก่อนข้ามเธรดซึ่งรับประกันได้ว่าการดำเนินการที่ไม่ใช่อะตอมก่อนหน้านี้ที่ทำโดยเธรดการเขียนจะปลอดภัยในการอ่านเว้นแต่ว่าจะเขียนเพิ่มเติมหลังจากที่เก็บรีลีส ... ) หรือคอมไพเลอร์จะมี ต้องฉลาดจริงๆเพื่อพิสูจน์ว่ามีแคชเพียงไม่กี่บรรทัดเท่านั้นที่ต้องการการล้างข้อมูล
ที่เกี่ยวข้อง: คำตอบของฉันเกี่ยวกับmov + mfence ปลอดภัยใน NUMA หรือไม่ กล่าวถึงรายละเอียดเกี่ยวกับการไม่มีอยู่ของระบบ x86 โดยไม่มีหน่วยความจำร่วมกัน นอกจากนี้ยังเกี่ยวข้องกับ: โหลดและร้านค้าการจัดเรียงใหม่บนแขนสำหรับข้อมูลเพิ่มเติมเกี่ยวกับการโหลด / ร้านค้าไปที่เดียวกันสถานที่ตั้ง
มีอยู่ผมคิดว่ากลุ่มที่มีหน่วยความจำไม่สอดคล้องกันที่ใช้ร่วมกัน แต่พวกเขาไม่ได้เครื่องเดียวระบบภาพ โดเมน Coherency แต่ละโดเมนจะรันเคอร์เนลแยกกันดังนั้นคุณจึงไม่สามารถรันเธรดของโปรแกรม C ++ เดียวข้ามมันได้ แต่คุณเรียกใช้อินสแตนซ์ของโปรแกรมแยกกัน (แต่ละอินสแตนซ์มีพื้นที่ที่อยู่ของตัวเอง: พอยน์เตอร์ในอินสแตนซ์หนึ่งใช้ไม่ได้กับอีกอินสแตนซ์)
เพื่อให้พวกเขาสื่อสารกันผ่านการฟลัชแบบชัดแจ้งโดยทั่วไปคุณจะใช้ MPI หรือ API การส่งข้อความอื่น ๆ เพื่อให้โปรแกรมระบุช่วงที่อยู่ที่ต้องการการล้าง
ฮาร์ดแวร์จริงไม่ทำงานstd::thread
ข้ามขอบเขตการเชื่อมโยงกันของแคช:
ชิป ARM แบบไม่สมมาตรบางตัวมีอยู่โดยมีพื้นที่ที่อยู่ทางกายภาพที่ใช้ร่วมกัน แต่ไม่ใช่โดเมนแคชภายในที่แชร์ได้ ดังนั้นไม่สอดคล้องกัน (เช่นคอมเมนต์เธรดแกน A8 และ Cortex-M3 เช่น TI Sitara AM335x)
แต่เคอร์เนลที่แตกต่างกันจะทำงานบนคอร์เหล่านั้นไม่ใช่อิมเมจระบบเดียวที่สามารถรันเธรดในทั้งสองคอร์ได้ ฉันไม่ทราบถึงการใช้งาน C ++ ใด ๆ ที่รันstd::thread
เธรดข้ามคอร์ CPU โดยไม่มีแคชที่สอดคล้องกัน
สำหรับ ARM โดยเฉพาะ GCC และ clang จะสร้างโค้ดโดยสมมติว่าเธรดทั้งหมดทำงานในโดเมนที่แชร์ได้ภายในเดียวกัน ในความเป็นจริงคู่มือ ARMv7 ISA กล่าวว่า
สถาปัตยกรรมนี้ (ARMv7) เขียนขึ้นโดยคาดหวังว่าโปรเซสเซอร์ทั้งหมดที่ใช้ระบบปฏิบัติการเดียวกันหรือไฮเปอร์ไวเซอร์จะอยู่ในโดเมน Inner Shareable Shareable
ดังนั้นหน่วยความจำแบบแบ่งใช้ที่ไม่สอดคล้องกันระหว่างโดเมนที่แยกจากกันจึงเป็นเพียงสิ่งเดียวสำหรับการใช้พื้นที่หน่วยความจำแบบแบ่งใช้เฉพาะระบบอย่างชัดเจนสำหรับการสื่อสารระหว่างกระบวนการต่างๆภายใต้เคอร์เนลที่แตกต่างกัน
ดูการอภิปรายเกี่ยวกับCoreCLRเกี่ยวกับ code-gen โดยใช้dmb ish
(Inner Shareable barrier) เทียบกับdmb sy
(System) memory barriers ในคอมไพเลอร์นั้น
ฉันยืนยันว่าไม่มีการใช้งาน C ++ สำหรับ ISA อื่นใดที่ทำงานstd::thread
ข้ามคอร์ด้วยแคชที่ไม่ต่อเนื่องกัน ฉันไม่มีหลักฐานว่าไม่มีการใช้งานดังกล่าว แต่ดูเหมือนว่าไม่น่าเป็นไปได้สูง เว้นแต่คุณจะกำหนดเป้าหมายไปยังส่วนที่แปลกใหม่ของ HW ที่ทำงานในลักษณะนั้นความคิดของคุณเกี่ยวกับประสิทธิภาพควรถือว่าการเชื่อมโยงกันของแคชเหมือน MESI ระหว่างเธรดทั้งหมด (ควรใช้atomic<T>
ในรูปแบบที่รับประกันความถูกต้องแม้ว่า!)
แคชที่สอดคล้องกันทำให้ง่าย
แต่ในระบบมัลติคอร์ที่มีแคชที่สอดคล้องกันการใช้รีลีสสโตร์นั้นหมายถึงการสั่งคอมมิตในแคชสำหรับร้านค้าของเธรดนี้โดยไม่ทำการล้างข้อมูลใด ๆ อย่างชัดเจน ( https://preshing.com/20120913/acquire-and-release-semantics/และhttps://preshing.com/20120710/memory-barriers-are-like-source-control-operations/ ) (และการรับโหลดหมายถึงการสั่งให้เข้าถึงแคชในคอร์อื่น ๆ )
คำสั่งกั้นหน่วยความจำจะบล็อกการโหลดของเธรดปัจจุบันและ / หรือจัดเก็บจนกว่าบัฟเฟอร์ที่จัดเก็บจะหมด ที่มักจะเกิดขึ้นเร็วที่สุดด้วยตัวมันเอง ( แผงกั้นหน่วยความจำช่วยให้มั่นใจได้ว่าการเชื่อมโยงกันของแคชเสร็จสมบูรณ์หรือไม่กล่าวถึงความเข้าใจผิดนี้) ดังนั้นหากคุณไม่ต้องการการสั่งซื้อเพียงแค่แจ้งการเปิดเผยในเธรดอื่นmo_relaxed
ก็ใช้ได้ (และก็เป็นเช่นนั้นvolatile
แต่อย่าทำอย่างนั้น)
ดูการแมป C / C ++ 11 กับโปรเซสเซอร์ด้วย
ข้อเท็จจริงที่น่าสนใจ: ใน x86 ร้านค้า asm ทุกตัวเป็นรีลีสสโตร์เนื่องจากโมเดลหน่วยความจำ x86 นั้นโดยทั่วไปแล้ว seq-cst บวกบัฟเฟอร์ร้านค้า (พร้อมการส่งต่อร้านค้า)
กึ่งเกี่ยวข้อง re: บัฟเฟอร์การจัดเก็บการมองเห็นทั่วโลกและการเชื่อมโยงกัน: C ++ 11 รับประกันน้อยมาก ISAs จริงส่วนใหญ่ (ยกเว้น PowerPC) รับประกันได้ว่าเธรดทั้งหมดสามารถเห็นด้วยกับลำดับการปรากฏของร้านค้าสองแห่งโดยอีกสองเธรด (ในคำศัพท์เกี่ยวกับโมเดลหน่วยความจำสถาปัตยกรรมคอมพิวเตอร์อย่างเป็นทางการพวกเขาคือ "ปรมาณูหลายสำเนา")
ความเข้าใจผิดก็คือว่าคำแนะนำรั้วหน่วยความจำ asm ที่มีความจำเป็นในการล้างบัฟเฟอร์ร้านสำหรับแกนอื่น ๆ เพื่อดูร้านค้าของเราได้ทั้งหมด จริงๆแล้วบัฟเฟอร์ของร้านค้าพยายามระบายตัวเอง (ผูกมัดกับแคช L1d) ให้เร็วที่สุดมิฉะนั้นจะเต็มและหยุดการดำเนินการ สิ่งที่กั้น / รั้วเต็มคือการถ่วงด้ายปัจจุบันจนกว่าบัฟเฟอร์ของร้านค้าจะหมดลงดังนั้นการโหลดในภายหลังของเราจึงปรากฏในคำสั่งซื้อทั่วโลกหลังจากร้านค้าก่อนหน้านี้ของเรา
(โมเดลหน่วยความจำ asm ที่ได้รับคำสั่งอย่างรุนแรงของ x86 หมายความว่าvolatile
ใน x86 อาจทำให้คุณเข้าใกล้ได้มากขึ้นmo_acq_rel
ยกเว้นว่าการเรียงลำดับเวลาคอมไพล์ใหม่ด้วยตัวแปรที่ไม่ใช่อะตอมยังคงเกิดขึ้นได้ แต่ส่วนใหญ่ที่ไม่ใช่ x86 จะมีโมเดลหน่วยความจำที่มีลำดับต่ำดังนั้นvolatile
และrelaxed
มีค่าประมาณเท่า อ่อนแอเท่าที่mo_relaxed
อนุญาต)