ใครสามารถบอกฉันว่า std :: atomic :: is_lock_free () ไม่คงที่เช่นเดียวกับ constexpr? มีมันไม่คงที่และ / หรือเป็น constexpr ไม่ได้ทำให้รู้สึกสำหรับฉัน
ใครสามารถบอกฉันว่า std :: atomic :: is_lock_free () ไม่คงที่เช่นเดียวกับ constexpr? มีมันไม่คงที่และ / หรือเป็น constexpr ไม่ได้ทำให้รู้สึกสำหรับฉัน
คำตอบ:
ตามที่อธิบายไว้ใน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" double
s ดังนั้นเมื่อ cppreference พูดถึง "การเข้าถึงหน่วยความจำที่จัดตำแหน่ง" มันน่าจะเป็นเช่นนั้นในแง่ของการจัดตำแหน่งตามธรรมชาติของประเภทสำหรับฮาร์ดแวร์ไม่ได้ใช้ประเภท C ++ ในลักษณะที่ขัดแย้งกับข้อกำหนดการจัดตำแหน่ง (ซึ่งจะเป็น UB)
นี่คือข้อมูลเพิ่มเติม: อะไรคือผลกระทบที่แท้จริงของการเข้าถึงที่ไม่ได้รับสิทธิแบบไม่สำเร็จบน x86?
โปรดตรวจสอบความคิดเห็นที่ลึกซึ้งโดย@Peter Cordesด้านล่าง!
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 ++) ยังคงมีข้อผิดพลาดนี้!)
std::atomic_ref<double>
จะปฏิเสธการจัดตำแหน่งdouble
ทั้งหมดหรือจะตรวจสอบการจัดตำแหน่งที่รันไทม์บนแพลตฟอร์มที่มันถูกกฎหมายสำหรับธรรมดาdouble
และint64_t
จะน้อยกว่าการจัดตำแหน่งตามธรรมชาติ (เพราะatomic_ref<T>
ทำงานบนวัตถุที่ถูกประกาศว่าเป็นที่ราบT
และมีการจัดตำแหน่งขั้นต่ำalignof(T)
โดยไม่มีโอกาสที่จะให้การจัดตำแหน่งแบบพิเศษ)
_Atomic int64_t
gcc -m32
อย่างไรก็ตามประเด็นของฉันคือคอมไพเลอร์ตัวจริงไม่สนับสนุนอะตอมมิกที่อยู่ในแนวเดียวกันและไม่ทำการตรวจสอบรันไทม์ (ยัง?) ดังนั้น#pragma pack
หรือ__attribute__((packed))
จะนำไปสู่การไม่มีอะตอมมิก lock_free
วัตถุจะยังคงรายงานว่าพวกเขามี
is_lock_free()
การอนุญาตให้การใช้งานในการทำงานที่แตกต่างจากวิธีการที่คนปัจจุบันทำจริง; ด้วยการตรวจสอบรันไทม์ตามการจัดตำแหน่งที่แท้จริงเพื่อใช้คำแนะนำอะตอมมิกที่สนับสนุน HW หรือใช้การล็อค
คุณสามารถใช้ std::is_always_lock_free
is_lock_free
ขึ้นอยู่กับระบบจริงและไม่สามารถระบุได้ในเวลารวบรวม
คำอธิบายที่เกี่ยวข้อง:
ประเภทอะตอมนั้นยังอนุญาตให้ล็อคได้ในบางครั้งเช่นหากการเข้าถึงหน่วยความจำที่สอดคล้องกันนั้นเป็นอะตอมมิกตามธรรมชาติในสถาปัตยกรรมที่กำหนดวัตถุที่อยู่ในประเภทเดียวกันจะต้องใช้ล็อค
std::numeric_limits<int>::max
constexpr
ขึ้นอยู่กับสถาปัตยกรรมที่ยังจะเป็นแบบคงที่และ ฉันเดาว่าไม่มีอะไรผิดในคำตอบ แต่ฉันไม่ซื้อส่วนแรกของการให้เหตุผล
is_lock_free
จะไม่มีจุดหมายในคอมไพเลอร์ว่า
ฉันได้ติดตั้ง 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 true
1
การดำเนินงานนี้เลือกที่จะใช้alignof( atomic<int64_t> ) == 8
เพื่อให้ทุกatomic<int64_t>
สอดคล้องถูกต้อง สิ่งนี้หลีกเลี่ยงความจำเป็นในการตรวจสอบการจัดแนวรันไทม์ในทุก ๆ โหลดและจัดเก็บ
(หมายเหตุจากบรรณาธิการ: นี่เป็นเรื่องปกติการใช้งาน C ++ ในชีวิตจริงส่วนใหญ่ทำงานในลักษณะนี้นี่คือเหตุผลที่std::is_always_lock_free
มีประโยชน์มากเพราะโดยปกติแล้วมันจะเป็นจริงสำหรับประเภทที่is_lock_free()
เคยเป็นจริง)
atomic<uint64_t>
และalignof() == 8
เพื่อให้พวกเขาไม่ต้องตรวจสอบการจัดตำแหน่งที่รันไทม์ API เก่านี้ให้ตัวเลือกแก่พวกเขาที่จะไม่ทำเช่นนั้น แต่ใน HW ปัจจุบันมันสมเหตุสมผลมากกว่าที่จะต้องจัดตำแหน่ง (มิฉะนั้น UB เช่นไม่ใช่อะตอมมิก) แม้ในรหัส 32 บิตที่int64_t
อาจมีการจัดตำแหน่ง 4 ไบต์เท่านั้นatomic<int64_t>
ต้องใช้ 8 ไบต์ ดูความคิดเห็นของฉันเกี่ยวกับคำตอบอื่น
alignof
มูลค่าให้กับประเภทพื้นฐานเช่นเดียวกับการจัดตำแหน่ง "ดี" ของฮาร์ดแวร์แล้ว is_lock_free
จะเป็นtrue
(และจะis_always_lock_free
) คอมไพเลอร์ของคุณที่นี่ทำสิ่งนี้อย่างแน่นอน แต่ API มีอยู่แล้วคอมไพเลอร์อื่น ๆ สามารถทำสิ่งต่าง ๆ ได้
alignof(std::atomic<double>) == 1
(ดังนั้นจะไม่มี "การเข้าถึงที่ไม่ได้ลงนาม" ในความรู้สึก C ++ ดังนั้นจึงไม่มี UB) แม้ว่าฮาร์ดแวร์จะรับประกันได้ว่าการดำเนินการของอะตอมแบบไม่ล็อคสำหรับdouble
s 4 หรือ 8 ไบต์ขอบเขต คอมไพเลอร์จะต้องใช้ล็อคในกรณีที่ไม่ได้จัดแนว (และคืนค่าบูลีนที่เหมาะสมจากis_lock_free
ขึ้นอยู่กับตำแหน่งหน่วยความจำของอินสแตนซ์ของวัตถุ)
is_always_lock_free
หรือไม่