簡體   English   中英

在使用 memory_order_relaxed 編譯一個 std::atomic 加載時,編譯器能否發明多個加載?

[英]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 訪問是寬松的,不是原子性保證。 在這方面, relaxedacquireseq_cst沒有什么不同。 我猜你有一些誤解,認為relaxed可能與此相關,但事實並非如此。


你是對的,將加載結果分配給volatile float tmp會使編譯器不太可能想要發明加載,但如果這是一個真正的問題(不是),更好的方法是使用volatile atomic<float> value; . 同樣,這不是必需的。 特別是現在,因為編譯器目前根本不優化原子( 為什么編譯器不合並冗余的 std::atomic 寫入? )但即使在未來編譯器只給你最低限度的 ISO C++ 標准並通過例如優化原子將冗余的背靠背讀取合並為一個,他們不允許做另一件事並發明額外的寫入,或發明額外的讀取並假設值相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM