คำตอบของ Scheff อธิบายวิธีแก้ไขรหัสของคุณ ฉันคิดว่าฉันจะเพิ่มข้อมูลเล็กน้อยเกี่ยวกับสิ่งที่เกิดขึ้นจริงในกรณีนี้
ฉันรวบรวมรหัสของคุณที่godboltโดยใช้ระดับการเพิ่มประสิทธิภาพ 1 ( -O1
) ฟังก์ชั่นของคุณรวบรวมเช่น:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
แล้วเกิดอะไรขึ้นที่นี่? อันดับแรกเรามีการเปรียบเทียบ: cmp BYTE PTR finished[rip], 0
- การตรวจสอบนี้เพื่อดูว่าfinished
เป็นเท็จหรือไม่
หากไม่ใช่เท็จ (aka จริง) เราควรออกจากลูปในการเรียกใช้ครั้งแรก สิ่งนี้ทำได้โดยjne .L4
ที่j umps เมื่อn ot e qual to label .L4
ที่ค่าของi
( 0
) ถูกเก็บไว้ใน register สำหรับใช้ในภายหลังและฟังก์ชันจะคืนค่า
หากเป็นเท็จเราจะย้ายไปที่
.L5:
jmp .L5
นี่คือการกระโดดแบบไม่มีเงื่อนไขเพื่อกำหนดป้ายกำกับ.L5
ซึ่งเกิดขึ้นเป็นคำสั่งการกระโดดเอง
กล่าวอีกนัยหนึ่งเธรดจะถูกใส่เข้าในการวนรอบไม่ว่าง
แล้วทำไมสิ่งนี้ถึงเกิดขึ้น
เท่าที่เครื่องมือเพิ่มประสิทธิภาพเกี่ยวข้องเธรดอยู่นอกขอบเขต มันจะถือว่าเธรดอื่นไม่ได้อ่านหรือเขียนตัวแปรพร้อมกัน (เพราะจะเป็น data-race UB) คุณต้องบอกว่าไม่สามารถเพิ่มประสิทธิภาพการเข้าถึงได้ นี่คือคำตอบของ Scheff ที่เข้ามาฉันจะไม่สนใจที่จะทำซ้ำเขา
เนื่องจากเครื่องมือเพิ่มประสิทธิภาพไม่ได้บอกว่าfinished
ตัวแปรอาจมีการเปลี่ยนแปลงระหว่างการดำเนินการของฟังก์ชั่นจึงเห็นfinished
ว่าไม่ได้ทำการแก้ไขโดยฟังก์ชันเองและถือว่าเป็นค่าคงที่
โค้ดที่ได้รับการปรับปรุงให้มีเส้นทางรหัสสองเส้นทางที่จะเป็นผลมาจากการเข้าสู่ฟังก์ชันด้วยค่าบูลคงที่ มันจะวนรอบไม่สิ้นสุดหรือวนซ้ำไม่เคยวิ่ง
ที่-O0
คอมไพเลอร์ (ตามที่คาดไว้) ไม่ได้เพิ่มประสิทธิภาพการวนลูปและเปรียบเทียบ:
func():
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], 0
.L148:
movzx eax, BYTE PTR finished[rip]
test al, al
jne .L147
add QWORD PTR [rbp-8], 1
jmp .L148
.L147:
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
ดังนั้นฟังก์ชั่นเมื่อไม่ได้เพิ่มประสิทธิภาพใช้งานได้การขาดอะตอมมิกซิตี้ที่นี่จึงไม่ใช่ปัญหาเพราะรหัสและประเภทข้อมูลนั้นง่าย อาจเป็นสิ่งที่แย่ที่สุดที่เราอาจพบได้ในที่นี้คือคุณค่าของi
สิ่งนั้นคือสิ่งที่ควรทำ
ระบบที่ซับซ้อนมากขึ้นพร้อมโครงสร้างข้อมูลมีแนวโน้มที่จะส่งผลให้ข้อมูลเสียหายหรือการดำเนินการที่ไม่เหมาะสม