簡體   English   中英

為什么 [[carries_dependency]] 不是 C++ 中的默認值?

[英]Why isn't [[carries_dependency]] the default in C++?

我知道memory_order_consume已被棄用,但我試圖了解原始設計中的邏輯以及[[carries_dependency]]kill_dependency應該如何工作。 為此,我想要一個特定的代碼示例,它會在 IBM PowerPC 或 DEC alpha 或什至具有假設編譯器的假設架構上中斷,該編譯器在 C++11 或 C++14 中完全實現了消費語義。

我能想到的最好的例子是這樣的:

int v;
std::atomic<int*> ap;

void
thread_1()
{
  v = 1;
  ap.store(&v, std::memory_order_release);
}

int
f(int *p [[carries_dependency]])
{
  return v;
}

void
thread_2()
{
  int *p;
  while (!(p = ap.load(std::memory_order_consume)))
    ;
  int v2 = f(p);
  assert(*p == v2);
}

我了解此代碼中的斷言可能會失敗。 但是,如果從f中刪除[[carries_dependency]] ,斷言應該失敗嗎? 如果是這樣,為什么會這樣? 畢竟,您請求了memory_order_consume ,那么您為什么希望其他對v的訪問能夠反映獲取語義呢? 如果刪除[[carries_dependency]]不會使代碼正確,那么[[carries_dependency]] (或使[[carries_dependency]]成為所有變量的默認值)破壞其他正確代碼的示例是什么?

我唯一能想到的是,這可能與寄存器溢出有關? 如果 function 將寄存器溢出到堆棧上,然后重新加載它,這可能會破壞依賴鏈。 So maybe [[carries_dependency]] makes things efficient in some cases (says no need to issue memory barrier in the caller before calling this function) but also requires the callee to issue a memory barrier before any register spills or calling another function, which could在其他情況下效率會降低嗎? 不過,我在這里抓住了稻草,所以仍然很想聽聽懂這些東西的人的來信……

return vint *p沒有數據依賴性,因此您需要acquireconsume ap.load(consume) / f(p)才能與發布存儲同步。

如果您使用了return *p那么這將是足夠的,這要歸功於依賴排序,因為該加載將對較早的加載具有數據依賴關系,CPU 無法更早地生成地址,因此在加載 from 之前從v加載ap看到你正在等待的價值。

有效地促進刪除依賴排序的東西需要在 function 調用之前使用 memory 屏障來促進consume acquire

DEC Alpha 即使consume工作也總是需要一個屏障,即它必須促進consume才能acquire ,因為 ISA 不保證硬件的依賴排序。

一些 ISA(大多數只是 x86)的排序非常強,它們不需要屏障,因為每個負載都是獲取負載,而不是與其他負載重新排序。 或者至少給人一種沒有被重新排序的錯覺; 實際實現推測性地提前加載,但如果檢測到錯誤推測,即在架構上允許加載發生時緩存行仍然無效的情況下,會破壞管道。

所以 x86 和 Alpha 可能仍然可以使用[[carries-dependency]]版本,因為它們要么太強要么太弱, mo_consume不是硬件可以實際做的事情(比mo_acquire更便宜)。

對於 Alpha,這將取決於編譯器將障礙放在哪里; 它可以將它放在f(p)return v之后,僅在實際取決於消耗負載的*p之前。 或者它可以像編譯器現在所做的那樣,在加載時促進消耗在現場獲取(因為在證明其當前設計難以支持后,消耗已被棄用。)


至於為什么 ISO C++11 決定在通過 function 邊界時促進consume結果有效acquire ,這可能是一個可用性考慮。 但也有表現。 否則,編譯器將失去對傳入的 function 參數進行一些優化的能力。

例如int ready = foo.load(consume); / if(ready == 1) return non_atomic[ready-ready]; 需要生成對消耗加載結果具有數據依賴性的 asm,這與編譯器僅優化它以return *non_atomic時不同。

(您可能熟悉 x86 xor eax,eax是一種將寄存器歸零的好方法。在保證依賴排序的弱排序 ISA 中,例如 ARM, eor r0, r0,r0保證不會破壞對舊值的依賴r0的。)

if(ready == 1)這樣的分支中的常量傳播也應該是可能的; 里面的代碼if只能在ready具有常量值1時運行。 所以即使我們不取消它, non_atomic[ready]也不能優化為non_atomic[1]; .

如果每個傳入的 function arg 都可能帶有依賴項,則編譯器將無法對任何 function args 或從它們派生的值進行那些通常的優化。


相關回復: consume是關於和/或其棄用的,並且它仍然以手動方式使用,在 Linux kernel 代碼(例如 RCU)中具有volatile

暫無
暫無

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

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