การเปรียบเทียบอะตอมมิก, มัลติโปรเซสเซอร์, C/C++ (Linux)

ฉันมีตัวแปรในหน่วยความจำที่ใช้ร่วมกัน x บนระบบตัวประมวลผลหลายตัว

void MyFunction(volatile int* x) {
  if (*x != 0) {
     // do something
  }
}

กระบวนการอื่นๆ (อาจใช้โปรเซสเซอร์ที่แตกต่างกัน) จะถูกเขียนไปยัง x โดยใช้การดำเนินการอะตอมมิกในตัว gcc เช่น __sync_bool_compare_and_swap เป็นต้น

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

สิ่งที่ฉันต้องการคือ atomic_compare ชนิดหนึ่ง (โดยไม่ต้องสลับ) หากมีสิ่งนั้นอยู่? หรือ "อะตอมอ่าน" วิธีที่เร็วที่สุดในการทำเช่นนี้คืออะไร? (หลีกเลี่ยง mutexes, locks ฯลฯ)

ขอบคุณ

แก้ไข:

ฉันเพิ่งรู้ว่าวิธีแก้ปัญหาที่ค่อนข้างแฮ็กคือการใช้ __sync_val_compare_and_swap ด้วยค่าที่ฉันรู้ว่าไม่มีทางเป็นไปได้ นั่นจะช่วยแก้ปัญหาได้หรือไม่? (มีวิธีที่สะอาดกว่านี้ไหม?)


person Switch    schedule 30.06.2012    source แหล่งที่มา
comment
โปรดทราบว่าความผันผวนจะไม่ทำทุกสิ่งที่คุณต้องการสำหรับโปรแกรมแบบมัลติเธรด ดู: stackoverflow.com/questions/2484980/   -  person argentage    schedule 30.06.2012


คำตอบ (3)


มาตรฐาน C ใหม่ C11 มี _Atomic ประเภทข้อมูลและการดำเนินการเพื่อจัดการกับสิ่งนี้ มาตรฐานนี้ยังไม่ได้นำมาใช้ แต่ gcc และ clang ใกล้จะถึงแล้ว พวกเขาใช้ฟังก์ชันนี้แล้ว และอันที่จริงแล้ว ฟังก์ชัน __sync_bool_compare_and_swap เป็นส่วนหนึ่งของมัน ฉันได้รวมสิ่งนั้นไว้ในชุดส่วนหัวใน P99 ที่ให้คุณตั้งโปรแกรมได้ มีอินเทอร์เฟซ C11 แล้ว

ฟังก์ชัน C11 เพื่อทำสิ่งที่คุณต้องการจะเป็น atomic_load หรือถ้าคุณมีข้อกำหนดเฉพาะสำหรับการเชื่อมโยงกัน atomic_load_explicit และไม่น่าแปลกใจอย่างที่คุณสงสัย P99 จะแมปสิ่งนั้นใน __sync_val_compare_and_swap(&x, 0, 0) จากนั้น หากคุณดูแอสเซมเบลอร์ที่สิ่งนี้สร้างขึ้นบนสถาปัตยกรรมส่วนใหญ่ สิ่งนี้จะแปลเป็นการดำเนินการโหลดแบบง่ายในกรณีที่ x เป็น int แต่สิ่งนี้ไม่รับประกันด้วยภาษา มันขึ้นอยู่กับคอมไพเลอร์ที่จะรู้สิ่งเหล่านี้ และสังเคราะห์คำสั่งที่รับประกันว่าเป็นอะตอมมิก

person Jens Gustedt    schedule 30.06.2012

วิธีที่เร็วที่สุดในการทำเช่นนี้คืออะไร? (หลีกเลี่ยง mutexes, locks ฯลฯ)

ฉันค่อนข้างแน่ใจว่าคุณไม่ต้องการหลีกเลี่ยง mutexes futexes ของ linux ช่วยให้คุณสามารถใช้ประโยชน์จากการเปรียบเทียบและสลับ (ส่วนใหญ่) ในขณะที่ยังคงรักษาความหมาย mutex แบบคลาสสิก ('swap' ที่เกิดขึ้นเป็นหนึ่งใน mutex ไม่ใช่รหัส / ข้อมูลที่ได้รับการป้องกัน) ฉันขอแนะนำอย่างยิ่งให้คุณลองใช้และกำหนดโปรไฟล์วิธีแก้ปัญหา (perf, oprofile, VTune ฯลฯ) เพื่อดูว่าคอขวดของคุณเกี่ยวข้องกับกลไกการล็อคจริงๆ หรือไม่ และไม่ใช่สิ่งต่างๆ เช่น การใช้งานแคช ปริมาณการประมวลผลของหน่วยความจำ รอบของ CPU การเข้าถึง IO ระยะไกล - การเข้าถึงหน่วยความจำโหนด ฯลฯ

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

สมมติว่าคุณจำเป็นต้องโต้ตอบระหว่างโปรเซสเซอร์จริงๆ และคุณได้วัดค่าเวลาแฝงที่คุณได้รับจาก futex และคุณได้พิจารณาแล้วว่ามันไม่ตรงกับความต้องการของแอปพลิเคชันของคุณ ดังนั้น หากเป็นกรณีนี้ วิธีดำเนินการที่ค่อนข้างสมเหตุสมผลอาจเป็นดังนี้: สร้างอาร์เรย์ของจำนวนเต็ม 32 บิต โดยเสริมด้วยระยะห่างที่มากกว่าหรือเท่ากับขนาดของบรรทัดแคชของเป้าหมายของคุณ ใช้ CPU ที่กำลังดำเนินการอยู่และขนาดบรรทัดแคชเป็นดัชนีในค่าจริงในรายการนี้ (ดังนั้นหากบรรทัดแคชของคุณคือ 64 ไบต์ คุณจะต้องปรับขนาด CPU# ด้วย 16 เพื่อข้ามข้ามช่องว่างภายใน) คุณควรเขียนถึงค่าเหล่านี้จาก CPU ที่เหมาะสมเท่านั้น และคุณสามารถสำรวจความคิดเห็นจาก CPU อื่น ๆ ได้ (อาจควรเรียกคำสั่ง "หยุดชั่วคราว" ของ CPU ตัวใดตัวหนึ่งในส่วนเนื้อหาของการรอไม่ว่าง) นี่จะเป็นกลไกที่มีประสิทธิภาพในการตรวจสอบว่าเธรดการดำเนินการที่แตกต่างกันถึง/ตรงตามเงื่อนไขที่กำหนดหรือไม่

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

person Brian Cain    schedule 30.06.2012
comment
ไม่มี mutexes ใดที่เกินกำลังแม้แต่บน linux Modern C มีคำตอบให้ ซึ่งใกล้เคียงกับที่ Switch ประเมินไว้มากกว่าที่คุณคิดมาก - person Jens Gustedt; 30.06.2012

สิ่งที่ฉันต้องการคือ atomic_compare ชนิดหนึ่ง (โดยไม่ต้องสลับ) หากมีสิ่งนั้นอยู่? หรือ "อะตอมอ่าน"

การเปรียบเทียบเป็นแบบอะตอมมิกอยู่แล้ว มันอ่านอันเดียว..

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

person Adam    schedule 30.06.2012
comment
ไม่ ไม่มีการรับประกันว่าการดำเนินการโหลดหนึ่งครั้งในระดับ C จะแปลเป็นการอ่านระดับต่ำเพียงครั้งเดียว สำหรับสถาปัตยกรรมส่วนใหญ่จะใช้สำหรับ int แต่ไม่มีการรับประกันสำหรับสิ่งนั้น - person Jens Gustedt; 30.06.2012