ทำไม std :: atomic <T> :: is_lock_free () ไม่คงที่เช่นเดียวกับ constexpr


9

ใครสามารถบอกฉันว่า std :: atomic :: is_lock_free () ไม่คงที่เช่นเดียวกับ constexpr? มีมันไม่คงที่และ / หรือเป็น constexpr ไม่ได้ทำให้รู้สึกสำหรับฉัน


3
คุณรู้is_always_lock_freeหรือไม่
Mike van Dyke

3
ฉันจะโยน "การจัดตำแหน่ง" ออกไปที่นั่น
Max Langhof

@MaxLanghof คุณหมายถึงว่าทุกกรณีจะไม่ได้รับการจัดตำแหน่งในลักษณะเดียวกัน?
curiousguy

1
ไมค์ไม่ฉันไม่รู้ แต่ขอบคุณสำหรับคำใบ้นี้ มันมีประโยชน์จริง ๆ สำหรับฉัน แต่ฉันถามตัวเองว่าทำไมถึงมีการตัดสินใจระหว่าง is_lock_free () และ is_always_lock_free อาจเป็นเพราะอะตอมมิกที่ไม่ได้ลงนาม แต่คนอื่น ๆ แนะนำที่นี่เนื่องจากภาษาจะกำหนดการเข้าถึงที่ไม่ได้จัดแนวเพื่อให้มีพฤติกรรมที่ไม่ได้กำหนด
Bonita Montero

คำตอบ:


10

ตามที่อธิบายไว้ในcppreference :

ชนิดอะตอมมิกทั้งหมดยกเว้น std :: atomic_flag อาจถูกนำมาใช้โดยใช้ mutexes หรือการดำเนินการล็อกอื่น ๆ แทนที่จะใช้คำสั่งซีพียูอะตอมล็อคฟรี ประเภทอะตอมนั้นยังอนุญาตให้ล็อคได้ในบางครั้งเช่นหากการเข้าถึงหน่วยความจำที่สอดคล้องกันนั้นเป็นอะตอมมิกตามธรรมชาติในสถาปัตยกรรมที่กำหนดวัตถุที่อยู่ในประเภทเดียวกันจะต้องใช้ล็อค

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

ตามที่กล่าวไว้โดยคนอื่น ๆ หลายคนstd::is_always_lock_freeอาจเป็นสิ่งที่คุณกำลังมองหา


แก้ไข: เพื่อชี้แจงชนิดของวัตถุ C ++ มีค่าการจัดตำแหน่งที่ จำกัด ที่อยู่ของอินสแตนซ์ของพวกเขาให้มีอำนาจทวีคูณเท่าของสอง ( [basic.align]) เท่านั้น ค่าการจัดตำแหน่งเหล่านี้ถูกกำหนดให้ใช้งานสำหรับประเภทพื้นฐานและไม่จำเป็นต้องเท่ากับขนาดของประเภท พวกเขาอาจเข้มงวดมากกว่าที่ฮาร์ดแวร์สามารถรองรับได้จริง

ตัวอย่างเช่น x86 (ส่วนใหญ่) รองรับการเข้าถึงที่ไม่ได้จัดแนว อย่างไรก็ตามคุณจะพบว่าคอมไพเลอร์ส่วนใหญ่มีalignof(double) == sizeof(double) == 8สำหรับ x86 เนื่องจากการเข้าถึงที่ไม่ได้ลงนามมีโฮสต์ของข้อเสีย (ความเร็วการแคชแคช atomicity ... ) แต่เช่น#pragma pack(1) struct X { char a; double b; };หรือalignas(1) double x;ช่วยให้คุณมี "unaligned" doubles ดังนั้นเมื่อ cppreference พูดถึง "การเข้าถึงหน่วยความจำที่จัดตำแหน่ง" มันน่าจะเป็นเช่นนั้นในแง่ของการจัดตำแหน่งตามธรรมชาติของประเภทสำหรับฮาร์ดแวร์ไม่ได้ใช้ประเภท C ++ ในลักษณะที่ขัดแย้งกับข้อกำหนดการจัดตำแหน่ง (ซึ่งจะเป็น UB)

นี่คือข้อมูลเพิ่มเติม: อะไรคือผลกระทบที่แท้จริงของการเข้าถึงที่ไม่ได้รับสิทธิแบบไม่สำเร็จบน x86?

โปรดตรวจสอบความคิดเห็นที่ลึกซึ้งโดย@Peter Cordesด้านล่าง!


1
32 บิต x86 alignof(double)==4เป็นตัวอย่างที่ดีของการที่คุณจะพบกับ แต่std::atomic<double>ยังคงมีalignof() = 8แทนที่จะตรวจสอบการจัดตำแหน่งที่รันไทม์ การใช้โครงสร้างแบบบีบอัดที่อะตอมมิกแบบแบ่ง ABI และไม่ได้รับการสนับสนุน (GCC สำหรับ 32- บิต x86 ชอบที่จะให้การจัดตำแหน่งตามธรรมชาติของวัตถุ 8 ไบต์ แต่กฎการจัดวางโครงสร้างจะแทนที่นั้นและอิงเฉพาะalignof(T)เช่นบน i386 System V. G ++ ที่ใช้ในการมีบั๊กที่atomic<int64_t>ภายใน struct อาจไม่เป็นอะตอม เพราะมันเพิ่งสันนิษฐาน GCC (สำหรับ C ไม่ใช่ C ++) ยังคงมีข้อผิดพลาดนี้!)
Peter Cordes

2
แต่การใช้งาน C ++ 20 ที่ถูกต้องstd::atomic_ref<double>จะปฏิเสธการจัดตำแหน่งdoubleทั้งหมดหรือจะตรวจสอบการจัดตำแหน่งที่รันไทม์บนแพลตฟอร์มที่มันถูกกฎหมายสำหรับธรรมดาdoubleและint64_tจะน้อยกว่าการจัดตำแหน่งตามธรรมชาติ (เพราะatomic_ref<T>ทำงานบนวัตถุที่ถูกประกาศว่าเป็นที่ราบTและมีการจัดตำแหน่งขั้นต่ำalignof(T)โดยไม่มีโอกาสที่จะให้การจัดตำแหน่งแบบพิเศษ)
Peter Cordes

2
ดูgcc.gnu.org/bugzilla/show_bug.cgi?id=62259สำหรับ libstdc ตอนนี้คง ++ ข้อผิดพลาดและgcc.gnu.org/bugzilla/show_bug.cgi?id=65146สำหรับข้อผิดพลาด C ยังคงเสียรวมทั้ง บริสุทธิ์ ISO C11 TestCase แสดงให้เห็นว่าการฉีกขาดของเมื่อรวบรวมกับปัจจุบัน_Atomic int64_t gcc -m32อย่างไรก็ตามประเด็นของฉันคือคอมไพเลอร์ตัวจริงไม่สนับสนุนอะตอมมิกที่อยู่ในแนวเดียวกันและไม่ทำการตรวจสอบรันไทม์ (ยัง?) ดังนั้น#pragma packหรือ__attribute__((packed))จะนำไปสู่การไม่มีอะตอมมิก lock_freeวัตถุจะยังคงรายงานว่าพวกเขามี
Peter Cordes

1
แต่ใช่วัตถุประสงค์ของis_lock_free()การอนุญาตให้การใช้งานในการทำงานที่แตกต่างจากวิธีการที่คนปัจจุบันทำจริง; ด้วยการตรวจสอบรันไทม์ตามการจัดตำแหน่งที่แท้จริงเพื่อใช้คำแนะนำอะตอมมิกที่สนับสนุน HW หรือใช้การล็อค
Peter Cordes

3

คุณสามารถใช้ std::is_always_lock_free

is_lock_free ขึ้นอยู่กับระบบจริงและไม่สามารถระบุได้ในเวลารวบรวม

คำอธิบายที่เกี่ยวข้อง:

ประเภทอะตอมนั้นยังอนุญาตให้ล็อคได้ในบางครั้งเช่นหากการเข้าถึงหน่วยความจำที่สอดคล้องกันนั้นเป็นอะตอมมิกตามธรรมชาติในสถาปัตยกรรมที่กำหนดวัตถุที่อยู่ในประเภทเดียวกันจะต้องใช้ล็อค


1
std::numeric_limits<int>::maxconstexprขึ้นอยู่กับสถาปัตยกรรมที่ยังจะเป็นแบบคงที่และ ฉันเดาว่าไม่มีอะไรผิดในคำตอบ แต่ฉันไม่ซื้อส่วนแรกของการให้เหตุผล
idclev 463035818

1
ไม่ได้กำหนดการเข้าถึงที่ไม่ได้จัดแนวภาษาเพื่อให้มีพฤติกรรมที่ไม่ได้กำหนดไว้ดังนั้นการประเมินผลการล็อคแบบฟรีหรือไม่ใช้งานในขณะรันไทม์จะเป็นเรื่องไร้สาระ?
Bonita Montero

1
มันไม่มีเหตุผลที่จะตัดสินใจเลือกระหว่างการเข้าถึงที่จัดแนวและไม่จัดแนวเนื่องจากภาษาจะกำหนดพฤติกรรมหลังที่ไม่ได้กำหนด
Bonita Montero

@BonitaMontero มีความรู้สึก "ไม่ตรงแนวในการจัดเรียงวัตถุ C ++" และ "ไม่ได้จัดแนวในสิ่งที่ฮาร์ดแวร์ชอบ" ความรู้สึก สิ่งเหล่านี้ไม่จำเป็นต้องเหมือนกัน แต่ในทางปฏิบัติพวกเขามักจะเป็น ตัวอย่างที่คุณแสดงเป็นหนึ่งตัวอย่างเช่นที่เห็นได้ชัดว่าคอมไพเลอร์ได้ในตัวสันนิษฐานว่าทั้งสองมีเดียวกัน - ซึ่งหมายความว่าis_lock_freeจะไม่มีจุดหมายในคอมไพเลอร์ว่า
Max Langhof

1
คุณสามารถมั่นใจได้ว่าอะตอมจะมีการจัดตำแหน่งที่เหมาะสมหากมีความต้องการการจัดตำแหน่ง
Bonita Montero

1

ฉันได้ติดตั้ง Visual Studio 2019 บน Windows-PC ของฉันแล้ว devenv นี้ก็มีคอมไพเลอร์ ARMv8 ด้วย ARMv8 อนุญาตการเข้าถึงที่ไม่ได้แนว แต่การเปรียบเทียบและการแลกเปลี่ยน, การล็อคเพิ่ม ฯลฯ จะได้รับการจัดตำแหน่ง และนอกจากนี้ยังมีการโหลดแท้ / การจัดเก็บแบบบริสุทธิ์โดยใช้ldpหรือstp(คู่โหลดหรือคู่การจัดเก็บของการลงทะเบียนแบบ 32 บิต) เท่านั้นที่รับประกันว่าจะเป็นแบบปรมาณูเมื่อจัดแนวตามธรรมชาติ

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

#include <atomic>
#include <cstddef>

using namespace std;

bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
    return a64->is_lock_free();
}

และนี่คือการถอดของ isLockFreeAtomic

|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
    movs        r0,#1
    bx          lr
ENDP

นี่เป็นเพียงอาคาreturns true1

การดำเนินงานนี้เลือกที่จะใช้alignof( atomic<int64_t> ) == 8เพื่อให้ทุกatomic<int64_t>สอดคล้องถูกต้อง สิ่งนี้หลีกเลี่ยงความจำเป็นในการตรวจสอบการจัดแนวรันไทม์ในทุก ๆ โหลดและจัดเก็บ

(หมายเหตุจากบรรณาธิการ: นี่เป็นเรื่องปกติการใช้งาน C ++ ในชีวิตจริงส่วนใหญ่ทำงานในลักษณะนี้นี่คือเหตุผลที่std::is_always_lock_freeมีประโยชน์มากเพราะโดยปกติแล้วมันจะเป็นจริงสำหรับประเภทที่is_lock_free()เคยเป็นจริง)


1
ใช่การใช้งานส่วนใหญ่เลือกที่จะให้atomic<uint64_t>และalignof() == 8เพื่อให้พวกเขาไม่ต้องตรวจสอบการจัดตำแหน่งที่รันไทม์ API เก่านี้ให้ตัวเลือกแก่พวกเขาที่จะไม่ทำเช่นนั้น แต่ใน HW ปัจจุบันมันสมเหตุสมผลมากกว่าที่จะต้องจัดตำแหน่ง (มิฉะนั้น UB เช่นไม่ใช่อะตอมมิก) แม้ในรหัส 32 บิตที่int64_tอาจมีการจัดตำแหน่ง 4 ไบต์เท่านั้นatomic<int64_t>ต้องใช้ 8 ไบต์ ดูความคิดเห็นของฉันเกี่ยวกับคำตอบอื่น
Peter Cordes

ใส่คำที่แตกต่าง: หากคอมไพเลอร์เลือกที่จะสร้างalignofมูลค่าให้กับประเภทพื้นฐานเช่นเดียวกับการจัดตำแหน่ง "ดี" ของฮาร์ดแวร์แล้ว is_lock_freeจะเป็นtrue(และจะis_always_lock_free) คอมไพเลอร์ของคุณที่นี่ทำสิ่งนี้อย่างแน่นอน แต่ API มีอยู่แล้วคอมไพเลอร์อื่น ๆ สามารถทำสิ่งต่าง ๆ ได้
Max Langhof

1
คุณสามารถมั่นใจได้ว่าถ้าภาษาบอกว่าการเข้าถึงที่ไม่ได้ลงแนวนั้นมีพฤติกรรมที่ไม่ได้กำหนดไว้อะตอมมิกทั้งหมดต้องได้รับการจัดตำแหน่งอย่างเหมาะสม ไม่มีการใช้งานจะทำการตรวจสอบรันไทม์ใด ๆ เนื่องจากการที่
Bonita Montero

@BonitaMontero ใช่ แต่ไม่มีสิ่งใดในภาษาที่ห้ามalignof(std::atomic<double>) == 1(ดังนั้นจะไม่มี "การเข้าถึงที่ไม่ได้ลงนาม" ในความรู้สึก C ++ ดังนั้นจึงไม่มี UB) แม้ว่าฮาร์ดแวร์จะรับประกันได้ว่าการดำเนินการของอะตอมแบบไม่ล็อคสำหรับdoubles 4 หรือ 8 ไบต์ขอบเขต คอมไพเลอร์จะต้องใช้ล็อคในกรณีที่ไม่ได้จัดแนว (และคืนค่าบูลีนที่เหมาะสมจากis_lock_freeขึ้นอยู่กับตำแหน่งหน่วยความจำของอินสแตนซ์ของวัตถุ)
Max Langhof
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.