[英]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 v
對int *p
沒有數據依賴性,因此您需要acquire
不consume
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.