[英]std::memory_order_relaxed atomicity with respect to the same atomic variable
[英]Can a compiler invent multiple loads when compiling one std::atomic load using memory_order_relaxed?
采取以下代碼
struct SharedMemory
{
std::atomic<float> value;
bool shouldReturnTrue()
{
float tmp = value.load(std::memory_order_relaxed);
float a = tmp;
float b = tmp;
return a == b;
}
};
編譯器如下優化它並破壞線程安全是否合法?
struct SharedMemory
{
std::atomic<float> value;
bool shouldReturnTrue()
{
float a = value.load(std::memory_order_relaxed);
float b = value.load(std::memory_order_relaxed);
return a == b;
}
};
如果是這樣,我是否應該強制它不使用volatile
關鍵字優化tmp
,如下所示?
struct SharedMemory
{
std::atomic<float> value;
bool shouldReturnTrue()
{
volatile float tmp = value.load(std::memory_order_relaxed);
float a = tmp;
float b = tmp;
return a == b;
}
};
不,那不是合法的優化。 正如您所說,如果有作者,它可能會在tmp
為 NaN 以外的情況下返回 false。 因此,根據 as-if 規則,它是無效的,因為 C++ 抽象機確實總是返回!isnan(tmp)
。
(如果你選擇了一個像int
這樣的類型,其中self == self
總是為真,那么談論它會更容易!)
您詢問的轉換僅對非原子變量合法,正是因為可以假定該值不會改變。 如果是,那將是數據競爭未定義行為,並且允許編譯器假設沒有 UB。 因此,如果寄存器分配更方便,他們可以發明另一種非原子非易失性負載。
但是必須假定atomic
變量在任意兩次讀取之間更改值(在這種方式下,就編譯器必須假設的內容而言,類似於volatile
),因為這是編譯器必須遵守的明確定義的行為。
所以編譯器永遠不會發明atomic<>
objects 的讀取。 (那篇鏈接的文章是關於 Linux kernel 代碼的,他們在編譯器上使用volatile
來手動滾動原子,這些編譯器以他們想要的方式工作,原因與我們在現代 C++ 中使用std::atomic<>
的原因相同。)
memory_order_relaxed
意味着訂購wrt。 周圍的 memory 訪問是寬松的,不是原子性保證。 在這方面, relaxed
與acquire
或seq_cst
沒有什么不同。 我猜你有一些誤解,認為relaxed
可能與此相關,但事實並非如此。
你是對的,將加載結果分配給volatile float tmp
會使編譯器不太可能想要發明加載,但如果這是一個真正的問題(不是),更好的方法是使用volatile atomic<float> value;
. 同樣,這不是必需的。 特別是現在,因為編譯器目前根本不優化原子( 為什么編譯器不合並冗余的 std::atomic 寫入? )但即使在未來編譯器只給你最低限度的 ISO C++ 標准並通過例如優化原子將冗余的背靠背讀取合並為一個,他們將不允許做另一件事並發明額外的寫入,或發明額外的讀取並假設值相同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.