簡體   English   中英

編譯器在優化期間是否需要關心其他線程?

[英]Does compiler need to care about other threads during optimizations?

這是從關於 C# 線程安全保證的討論中衍生出來的。

我有以下假設:

在沒有線程感知原語(互斥體、std::atomic* 等,為了簡單起見,我們也排除 volatile),一個有效的 C++ 編譯器可以進行任何類型的轉換,包括從內存中引入讀取(或者例如,如果它想要寫入to),如果從當前線程的角度來看,當前線程中的代碼語義(即輸出和[本題中排除的] volatile訪問)保持不變,即不考慮其他線程的存在。 引入讀/寫可能會改變其他線程的行為(例如,因為其他線程在沒有正確同步或執行其他類型的 UB 的情況下讀取數據)這一事實可以被標准編譯器完全忽略。

這個假設正確與否? 我希望這將遵循 as-if 規則。 (我相信是這樣,但其他人似乎不同意我的觀點。)如果可能,請包括適當的規范性參考資料。

是的,當並非所有訪問都是讀取時,C++ 將數據競爭 UB 定義為對非atomic對象的潛在並發訪問。 另一個最近的問答引用了該標准,包括。

[intro.races]/2 - 如果其中一個修改了內存位置,則兩個表達式計算沖突……而另一個讀取或修改相同的內存位置。

[intro.races]/21 ...如果程序的執行包含兩個潛在的並發沖突動作,則該程序的執行包含數據競爭,其中至少一個不是原子的,並且兩者都不會在另一個之前發生,...

任何此類數據競爭都會導致未定義的行為。


這使編譯器可以自由地以保留執行函數的線程的行為的方式優化代碼,而不是其他線程(或調試器)在查看不應該查看的內容時可能會看到的內容。 (即數據競爭 UB 意味着讀取/寫入非原子變量的順序不是優化器必須保留的可觀察行為的一部分。)

引入讀/寫可能會改變其他線程的行為

as-if 規則允許您發明讀取,但不,您不能發明對該線程尚未寫入的對象的寫入 這就是為什么if(a[i] > 10) a[i] = 10; 不同於a[i] = a[i]>10 ? 10 : a[i] a[i] = a[i]>10 ? 10 : a[i]

兩個不同的線程同時寫入a[1]a[2]是合法的,一個線程加載a[0..3]然后存儲一些已修改和未修改的元素可能會被寫a[2]線程。

icc 崩潰:編譯器能否發明抽象機器中不存在的寫入? 詳細介紹了 ICC 在使用 SIMD 混合進行自動矢量化時所做的編譯器錯誤。 包括指向 Herb Sutter 的原子武器演講的鏈接,其中他討論了編譯器不得發明寫入的事實。

相比之下,AVX-512 屏蔽和 AVX vmaskmovps等,如我認為的 ARM SVE 和 RISC-V 矢量擴展,確實具有適當的屏蔽和故障抑制功能,實際上根本不存儲到某些 SIMD 元素,沒有分支。


發明原子RMW 是合法的(除了沒有修改部分),例如,如果您想修改該區域中的某些字節,則可以使用 8 字節lock cmpxchg [rcx], rdx 但在實踐中,這比單獨存儲修改后的字節成本更高,因此編譯器不會這樣做。


當然無條件寫入a[2]的函數可以多次寫入,並且在最終將其更新為最終值之前使用不同的臨時值。 (可能只有 Deathstation 9000 會發明不同值的臨時內容,例如將a[2] = 3變為a[2] = 2; a[2]++;

有關編譯器可以合法做什么的更多信息,請參閱誰害怕一個糟糕的優化編譯器? 在 LWN 上。 那篇文章的背景是 Linux 內核開發,他們依靠 GCC 來超越 ISO C 標准並且實際上以合理的方式運行,從而可以使用volatile int*和內聯 asm 滾動他們自己的原子。 它解釋了讀取或寫入非atomic共享變量的許多實際危險。

暫無
暫無

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

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