“ เกิดขึ้นก่อนหน้านี้” หมายความว่าอะไร?


9

วลี "เกิดขึ้นอย่างแรงก่อน" ถูกใช้หลายครั้งในมาตรฐานฉบับร่าง C ++

ตัวอย่างเช่น: การยุติ [basic.start.term] / 5

หากความสมบูรณ์ของการเริ่มต้นของวัตถุที่มีระยะเวลาการจัดเก็บแบบคงที่เกิดขึ้นก่อนที่จะเรียก std :: atexit (ดู, [support.start.term]) การเรียกฟังก์ชั่นผ่านไปยัง std :: atexit ถูกจัดลำดับก่อนการเรียกไปยัง destructor สำหรับวัตถุ ถ้าการเรียกไปยัง std :: atexit เกิดขึ้นอย่างมากก่อนที่การเริ่มต้นของวัตถุที่มีระยะเวลาการจัดเก็บแบบสแตติกการเรียกไปยัง destructor สำหรับวัตถุนั้นจะถูกจัดลำดับก่อนที่การเรียกไปยังฟังก์ชันที่ส่งผ่านไปยัง std :: atexit . หากการเรียกไปยัง std :: atexit เกิดขึ้นอย่างยิ่งก่อนการเรียกไปยัง std :: atexit การเรียกใช้ฟังก์ชันที่ส่งไปยังการเรียก std :: atexit ที่สองจะถูกจัดลำดับก่อนการเรียกไปยังฟังก์ชันที่ส่งไปยัง first std :: atexit call

และกำหนดไว้ใน Data race [intro.races] / 12

การประเมิน A จะเกิดขึ้นก่อนการประเมิน D อย่างใดอย่างหนึ่ง

(12.1) A ได้รับการจัดลำดับก่อน D หรือ

(12.2) การซิงโครไนซ์กับ D และทั้ง A และ D เป็นการดำเนินการปรมาณูที่สอดคล้องกันตามลำดับ ([atomics.order]) หรือ

(12.3) มีการประเมินผล B และ C เช่นนั้น A จะถูกจัดลำดับก่อน B, B เกิดขึ้นก่อน C, และ C ถูกจัดลำดับก่อน D หรือ

(12.4) มีการประเมินผล B ซึ่งเกิดขึ้นก่อนที่ B และเกิดขึ้นก่อน D

[หมายเหตุ: อย่างไม่เป็นทางการหาก A เกิดขึ้นอย่างรุนแรงก่อน B ดังนั้น A จะปรากฏขึ้นเพื่อประเมินก่อน B ในบริบททั้งหมด เกิดขึ้นอย่างมากก่อนที่จะแยกการดำเนินการบริโภค - บันทึกท้าย]

ทำไม "เกิดขึ้นก่อน" แนะนำ? โดยสังหรณ์ใจความแตกต่างและความสัมพันธ์กับ "เกิดขึ้นก่อน" คืออะไร

"A ปรากฏว่าได้รับการประเมินก่อน B ในบริบททั้งหมด" ในบันทึกย่อหมายความว่าอะไร

(หมายเหตุ: แรงจูงใจสำหรับคำถามนี้คือความคิดเห็นของ Peter Cordes ภายใต้คำตอบนี้)

ใบเสนอราคามาตรฐานฉบับร่างเพิ่มเติม (ขอบคุณ Peter Cordes)

คำสั่งซื้อและความสอดคล้อง [atomics.order] / 4

มีการสั่งซื้อทั้งหมด S เดียวในการดำเนินการ memory_order :: seq_cst ทั้งหมดรวมถึงรั้วที่ตอบสนองข้อ จำกัด ดังต่อไปนี้ ก่อนอื่นถ้าการดำเนินการ A และ B คือ memory_order :: seq_cst และ A เกิดขึ้นอย่างรุนแรงก่อน B ดังนั้น A จะนำหน้า B ใน S วินาทีสำหรับการดำเนินการปรมาณู A และ B ทุกคู่ในวัตถุ M โดยที่ A สั่งการเชื่อมโยงกัน ก่อน B จะต้องมีสี่เงื่อนไขต่อไปนี้เพื่อให้เป็นที่พอใจโดย S:

(4.1) ถ้า A และ B เป็นการดำเนินการ memory_order :: seq_cst ดังนั้น A นำหน้า B ใน S; และ

(4.2) ถ้า A เป็นการดำเนินการ memory_order :: seq_cst และ B เกิดขึ้นก่อนที่ memory_order :: seq_cst ล้อมรอบ Y แล้ว A นำหน้า Y ใน S; และ

(4.3) ถ้า memory_order :: seq_cst fence X เกิดขึ้นก่อน A และ B คือการดำเนินการ memory_order :: seq_cst ดังนั้น X นำหน้า B ใน S; และ

(4.4) ถ้า memory_order :: seq_cst fence X เกิดขึ้นก่อน A และ B เกิดขึ้นก่อน memory_order :: seq_cst fence Y แล้ว X นำหน้า Y ใน S


1
ร่างมาตรฐานในปัจจุบันยังมีการอ้างอิง "เป็นอย่างยิ่งที่เกิดขึ้นก่อนที่จะ B" เป็นเงื่อนไขสำหรับกฎการยื่นขอseq_cstในอะตอม 31.4 สั่งซื้อและความสอดคล้อง: 4 นั่นไม่ใช่ใน C ++ 17 n4659มาตรฐานที่ 32.4-3 กำหนดการดำรงอยู่ของยอดสั่งซื้อเพียงครั้งเดียวของ Ops seq_cst สอดคล้องกับ“เกิดขึ้นก่อนที่” การสั่งซื้อและการปรับเปลี่ยนการสั่งซื้อสำหรับสถานที่ได้รับผลกระทบทั้งหมด ; เพิ่ม "มั่น" ในแบบร่างภายหลัง
Peter Cordes

2
@PeterCordes ฉันคิดว่าความคิดเห็นที่ไม่รวมการบริโภคระบุว่ามันเป็น HB“ ในทุกบริบท” /“ แข็งแกร่ง” และการพูดคุยเกี่ยวกับการโทรไปที่ตัวชี้ฟังก์ชั่นการทำงานเป็นสิ่งที่แจกให้คนตาย ถ้าโปรแกรมแบบมัลติเธรดเรียกใช้atexit()ในเธรดหนึ่งและexit()อีกเธรดหนึ่งไม่เพียงพอสำหรับ initializers ที่จะใช้การอ้างอิงแบบอิงการสิ้นเปลืองเท่านั้นเนื่องจากผลลัพธ์นั้นแตกต่างจากถ้าexit()ถูกเรียกโดยเธรดเดียวกัน คำตอบที่เก่ากว่าของฉันเกี่ยวข้องกับความแตกต่างนี้
Iwillnotexist Idonotexist


@IwillnotexistIdonotexist คุณยังสามารถออกจากโปรแกรม MT ได้หรือไม่? มันไม่ได้เป็นความคิดที่แตกหักหรือไม่?
curiousguy

1
@curiousguy exit()นั่นคือวัตถุประสงค์ของการ เธรดใด ๆสามารถฆ่าโปรแกรมทั้งหมดโดยออกจากโปรแกรมหรือเธรดหลักสามารถออกได้โดยreturn-ing มันส่งผลให้เกิดการเรียกatexit()ตัวจัดการและความตายของกระทู้ทั้งหมดไม่ว่าพวกเขาจะทำอะไร
Iwillnotexist Idonotexist

คำตอบ:


5

ทำไม "เกิดขึ้นก่อน" แนะนำ? โดยสังหรณ์ใจความแตกต่างและความสัมพันธ์กับ "เกิดขึ้นก่อน" คืออะไร

รั้งตัวเองสำหรับ "ก็เกิดขึ้นก่อน" เช่นกัน! ดูภาพรวมปัจจุบันของ cppref https://en.cppreference.com/w/cpp/atomic/memory_order

ป้อนคำอธิบายรูปภาพที่นี่

ดูเหมือนว่า "เพิ่งเกิด - ก่อน" ถูกเพิ่มใน C ++ 20

เพียงเกิดขึ้นก่อน

การประเมิน A จะเกิดขึ้นก่อนการประเมิน B หากสิ่งใดสิ่งหนึ่งต่อไปนี้เป็นจริง:

1) A ถูกจัดลำดับ - ก่อน B

2) A - ประสานกับ B

3) A เกิดขึ้นก่อน X และ X เกิดขึ้นก่อน B

หมายเหตุ: โดยไม่ต้องดำเนินการบริโภคเพียงแค่เกิดขึ้นก่อนและเกิดขึ้นก่อนที่ความสัมพันธ์จะเหมือนกัน

ดังนั้น Simply-HB และ HB จึงเหมือนกันยกเว้นวิธีจัดการกับการใช้งาน ดู HB

ที่เกิดขึ้นมาก่อน

การประเมิน A เกิดขึ้นก่อนการประเมิน B หากสิ่งใดสิ่งหนึ่งต่อไปนี้เป็นจริง:

1) A ถูกจัดลำดับ - ก่อน B

2) การอินเตอร์เธรดเกิดขึ้นก่อน B

การดำเนินการที่จำเป็นเพื่อให้แน่ใจว่าเกิดขึ้นก่อนที่จะมีความสัมพันธ์เป็นวงจรโดยการแนะนำการประสานเพิ่มเติมในกรณีที่จำเป็น (มันเป็นสิ่งจำเป็นเฉพาะถ้าเกี่ยวข้องกับการดำเนินการบริโภคดู Batty et al)

พวกเขาแตกต่างกันอย่างไรเกี่ยวกับการบริโภค? ดู Inter-Thread-HB

Inter-thread เกิดขึ้นก่อนหน้า

ระหว่างเธรดการประเมินอินเตอร์เธรดเกิดขึ้นก่อนการประเมิน B หากมีสิ่งใดสิ่งหนึ่งต่อไปนี้เป็นจริง

1) A ประสานกับ B

2) A นั้นขึ้นอยู่กับการสั่งซื้อก่อน B

3) ...

...

การดำเนินการที่ได้รับคำสั่งในการพึ่งพา (เช่นใช้การเปิดตัว / ใช้งาน) เป็น HB แต่ไม่จำเป็นต้องมีเพียงแค่ HB

การกินนั้นผ่อนคลายกว่าการได้มาดังนั้นถ้าฉันเข้าใจอย่างถูกต้อง HB นั้นจะผ่อนคลายกว่า Simply-HB

เกิดขึ้นอย่างมากก่อน

ไม่ว่าจะมีเธรดใดการประเมิน A จะเกิดขึ้นก่อนการประเมิน B หากสิ่งใดสิ่งหนึ่งต่อไปนี้เป็นจริง:

1) A ถูกจัดลำดับ - ก่อน B

2) การซิงโครไนซ์กับ B และทั้ง A และ B เป็นการดำเนินงานอะตอมที่สอดคล้องกันตามลำดับ

3) A ได้รับการจัดลำดับก่อน X, X จะเกิดขึ้นก่อน Y และ Y ถูกจัดลำดับตามก่อน B

4) A เกิดขึ้นอย่างมากก่อน X และ X รุนแรงเกิดขึ้นก่อน B

หมายเหตุ: อย่างไม่เป็นทางการหาก A เกิดขึ้นอย่างรุนแรงก่อน B ดังนั้น A จะปรากฏขึ้นเพื่อประเมินก่อน B ในบริบททั้งหมด

หมายเหตุ: เกิดขึ้นอย่างยิ่งก่อนที่จะแยกการดำเนินการที่ใช้

ดังนั้นการดำเนินการปล่อย / ใช้ไม่สามารถเป็น Strongly-HB ได้

การเปิดตัว / การรับสามารถเป็น HB และ Simply-HB (เพราะการเปิด / รับการซิงโครไนซ์กับ) แต่ไม่จำเป็นอย่างยิ่ง HB เนื่องจาก Strongly-HB กล่าวโดยเฉพาะว่า A ต้องซิงโครไนซ์กับ B และเป็นการดำเนินการที่สอดคล้องตามลำดับ

                            Is happens-before guaranteed?

                        HB             Simply-HB          Strongly-HB

relaxed                 no                 no                 no
release/consume        yes                 no                 no      
release/acquire        yes                yes                 no
S.C.                   yes                yes                yes

"A ปรากฏว่าได้รับการประเมินก่อน B ในบริบททั้งหมด" ในบันทึกย่อหมายความว่าอะไร

บริบททั้งหมด: กระทู้ทั้งหมด / ซีพียูทั้งหมดเห็น (หรือ "ในที่สุดจะเห็นด้วย") คำสั่งเดียวกัน นี่คือการรับประกันความสอดคล้องของลำดับ - ลำดับการเปลี่ยนแปลงทั้งหมดของตัวแปรทั้งหมด รับ / ปล่อยกลุ่มรับประกันการรับรู้เฉพาะการแก้ไขลำดับสำหรับเธรดที่เข้าร่วมในห่วงโซ่เท่านั้น หัวข้อนอกห่วงโซ่ได้รับอนุญาตในทางทฤษฎีเพื่อดูคำสั่งที่แตกต่างกัน

ฉันไม่รู้ว่าทำไม Strongly-HB และ Simply-HB ถูกนำมาใช้ อาจช่วยชี้แจงวิธีการใช้บริโภคได้อย่างไร Strongly-HB มีคุณสมบัติที่ดี - หากมีเธรดหนึ่งอันที่เกิดขึ้นอย่างรุนแรงก่อนที่ B จะรู้ว่าเธรดทั้งหมดจะสังเกตเห็นสิ่งเดียวกัน

ประวัติความเป็นมาของการบริโภค:

Paul E. McKenney รับผิดชอบการบริโภคที่อยู่ในมาตรฐาน C และ C ++ ใช้รับประกันการสั่งซื้อระหว่างการกำหนดตัวชี้และหน่วยความจำมันชี้ไปที่ มันถูกประดิษฐ์เพราะ DEC อัลฟ่า DEC Alpha สามารถเก็งกำไรตัวชี้ได้ดังนั้นจึงมีกรอบหน่วยความจำเพื่อป้องกันสิ่งนี้ DEC Alpha ไม่ได้ถูกสร้างขึ้นแล้วและไม่มีโปรเซสเซอร์ในวันนี้ที่มีพฤติกรรมนี้ การบริโภคมีวัตถุประสงค์เพื่อให้ผ่อนคลายมาก


1
ความเศร้าโศกที่ดี ฉันเกือบเสียใจที่ถามคำถามนั้น ฉันต้องการกลับไปที่การจัดการปัญหาC ++ อย่างง่ายเช่นกฎการตรวจสอบความถูกต้องตัววนซ้ำการค้นหาชื่อที่อ้างถึงอาร์กิวเมนต์ผู้ใช้กำหนดตัวดำเนินการแปลงเทมเพลตการหักล้างอาร์กิวเมนต์เทมเพลตเมื่อการค้นหาชื่อดูในคลาสพื้นฐานในสมาชิกของเทมเพลต สามารถแปลงเป็นฐานเสมือนในจุดเริ่มต้นของการสร้างวัตถุ
curiousguy

Re: บริโภค คุณอ้างว่าชะตากรรมของการสั่งซื้อบริโภคนั้นเชื่อมโยงกับชะตากรรมของ DEC Alpha และไม่มีค่าใด ๆ นอกเหนือจากซุ้มประตูนั้น
curiousguy

1
นั่นเป็นคำถามที่ดี เมื่อพิจารณาดูเพิ่มเติมในตอนนี้ดูเหมือนว่าการบริโภคจะช่วยเพิ่มประสิทธิภาพในทางทฤษฎีสำหรับอาร์คที่มีคำสั่งอ่อน ๆ เช่น ARM และ PowerPC ให้เวลาฉันดูเพิ่มเติมหน่อย
Humphrey Winnebago

1
ฉันจะบอกว่าการบริโภคนั้นมีอยู่เพราะ ISAs ที่อ่อนแออย่างอื่นนอกเหนือจากอัลฟ่า ใน Alpha asm มีเพียงตัวเลือกเดียวที่ผ่อนคลายและได้รับ (และ seq-cst) ไม่ใช่การสั่งซื้ออ้างอิง mo_consumeมีวัตถุประสงค์เพื่อใช้ประโยชน์จากการสั่งซื้อการพึ่งพาข้อมูลบน CPU จริงและทำให้เป็นระเบียบว่าคอมไพเลอร์ไม่สามารถทำลายการพึ่งพาข้อมูลผ่านการทำนายสาขา เช่นint *p = load(); tmp = *p;อาจถูกทำลายโดยคอมไพเลอร์แนะนำif(p==known_address) tmp = *known_address; else tmp=*p;ถ้ามันมีเหตุผลบางอย่างที่คาดว่าค่าตัวชี้บางอย่างจะเป็นเรื่องธรรมดา ถูกกฎหมายสำหรับการผ่อนคลาย แต่ไม่บริโภค
Peter Cordes

@PeterCordes ถูกต้อง ... ส่วนโค้งที่มีการสั่งซื้อต่ำจะต้องปล่อยสิ่งกีดขวางหน่วยความจำเพื่อให้ได้มา แต่ (ในทางทฤษฎี) ไม่ใช่เพื่อการบริโภค ดูเหมือนคุณคิดว่าถ้าอัลฟ่าไม่เคยมีเราจะยังคงกิน? นอกจากนี้คุณยังบอกว่าการบริโภคเป็นอุปสรรคคอมไพเลอร์แฟนซี (หรือ "มาตรฐาน")
Humphrey Winnebago
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.