假設我有一個運行計算的函式,例如點積 - 我傳入一個A, B向量陣列和一個浮點陣列C,函式分配:
C[i] = dot(A[i], B[i]);
如果我創建并啟動兩個將運行此函式的執行緒,并將相同的三個陣列傳遞給兩個執行緒,那么在什么情況下這種型別的操作(可能使用不同的非亂數學運算等)不能保證給出相同的結果(在同一臺機器上運行相同的應用程式而無需任何重新編譯)?我只對消費類 PC 的環境感興趣。
我知道浮點操作通常是確定性的,但我確實想知道是否可能會發生一些奇怪的事情,并且可能在一個執行緒上計算將使用一個中間 80 位暫存器,但在另一個執行緒中不會。
我假設幾乎可以保證相同的二進制代碼應該在兩個執行緒中運行(有什么方法不會發生這種情況嗎?由于某種原因,該函式被多次編譯,編譯器以某種方式確定它將在多個執行緒中運行,然后編譯出于某種原因,第二個執行緒再次出現?)。但我有點擔心 CPU 內核可能沒有相同的指令集,即使在消費級 PC 上也是如此。
附帶問題 - 在類似情況下 GPU 怎么樣?
//
我假設 x86_64、Windows、c dot和a.x * b.x a.y * b.y. 無法提供更多資訊 - 使用 Unity IL2CPP,不知道它如何編譯/使用哪些選項。
問題的動機:我正在撰寫一個修改網格的計算幾何程式 - 我將其稱為“幾何網格”。問題是“渲染網格”可能會發生某些幾何位置的多個頂點 - 例如平面著色需要它 - 您有多個具有不同法線的頂點。然而,實際的計算幾何程序僅使用空間位置的純幾何資料。
所以我看到了兩個選擇:
- 創建從渲染網格到幾何網格的映射(示例 - 將重復頂點映射到一個唯一頂點),在幾何網格上運行該程序,然后根據結果以某種方式修改渲染網格。
- 直接使用渲染網格。由于該程序對所有頂點進行計算,因此效率稍低,但從代碼角度來看要容易得多。但最重要的是,我有點擔心我可能會為實際上具有相同位置的兩個頂點獲得兩個不同的值,這是不應該發生的。僅使用位置,并且兩個這樣的頂點的位置相同。
uj5u.com熱心網友回復:
浮點 (FP) 操作不是關聯的(但它是可交換的)。因此,(x y) z可以給出與 不同的結果x (y z)。例如,對于(1e-13 (1 - 1e-13)) == ((1e-13 1) - 1e-13)64 位 IEEE-754 浮點數為 false。C 標準對浮點數的限制不是很嚴格。然而,廣泛使用的IEEE-754標準是。它指定 32 位和 64 位數字運算的精度,包括舍入模式。x86-64 處理器符合 IEEE-754,主流編譯器(例如 GCC、Clang 和 MSVC)默認也符合 IEEE-754。默認情況下,ICC 不兼容,因為它假定 FP 操作是關聯的以提高性能。主流編譯器具有編譯標志來做出這樣的假設,以加快代碼速度。它通常與其他一些相結合,例如假設所有 FP 值都不是 NaN(例如-ffast-math)。此類標志違反了 IEEE-754 合規性,但它們通常用于 3D 或視頻游戲行業,以加快代碼速度。C 標準不需要 IEEE-754,但您可以使用std::numeric_limits<T>::is_iec559.
默認情況下,執行緒可以有不同的舍入模式。但是,您可以使用此答案中提供的 C 代碼設定舍入模式。另外,請注意,非正規數有時會在某些平臺上被禁用,因為它們的開銷非常高(有關更多資訊,請參閱此內容)。
假設沒有破壞 IEEE-754 合規性,舍入模式相同并且執行緒以相同的順序執行操作,那么結果應該至少相同 1 ULP。在實踐中,如果它們使用同一個主流編譯器編譯,結果應該是完全一樣的。
問題是使用多個執行緒通常會導致應用的 FP 操作的順序不確定,從而導致結果不確定。更具體地說,對 FP 變數的原子操作通常會導致這樣的問題,因為操作的順序經常在運行時發生變化。如果你想要確定性的結果,你需要使用靜態磁區,避免對 FP 變數的原子操作或更一般的可能導致不同排序的原子操作。同樣的事情也適用于鎖或任何同步機制。
GPU 也是如此。事實上,當開發人員使用原子 FP 操作(例如對值求和)時,此類問題非常常見。他們經常這樣做是因為實作快速歸約很復雜(盡管它更具確定性)并且原子操作在現代 GPU 上非常快(因為它們使用專用的高效單元)。
uj5u.com熱心網友回復:
根據浮點處理器非確定性的公認答案?, C 浮點不是非確定性的。相同的指令序列將給出相同的結果。
不過有幾點需要考慮:
首先,執行 FP 計算的特定 C 源代碼的行為(即結果)可能取決于編譯器和選擇的編譯器選項。例如,它可能取決于編譯器是選擇發出 64 位還是 80 位 FP 指令。但這是確定性的。
其次,類似的 C 源代碼可能會給出不同的結果;例如,由于某些 FP 指令的非關聯行為。這也是確定性的。
默認情況下,確定性不會受到多執行緒的影響。C 編譯器可能不知道代碼是否是多執行緒的。它絕對沒有理由發出不同的 FP 代碼。
誠然,FP 行為取決于所選的舍入模式,并且可以基于每個執行緒進行設定。然而,要做到這一點,某些東西(應用程式代碼)必須為不同的執行緒顯式設定不同的舍入模式。再一次,這是確定性的。(對于應用程式代碼來說,這是一件非常愚蠢的事情,IMO。)
PC 會為不同執行緒使用具有不同行為的不同 FP 硬體的想法對我來說似乎有些牽強。當然,一臺 PC 可以有(比如說)一個 Intel 芯片組和一個 ARM 芯片組,但是同一個 C 應用程式(可執行檔案)的不同執行緒同時在兩個芯片組上運行是不合理的。
對于 GPU 也是如此。事實上,考慮到您需要以與普通(或執行緒)C 完全不同的方式對 GPU 進行編程,我懷疑它們甚至可以共享相同的源代碼。
簡而言之,我認為您正在擔心一個您在現實中不太可能遇到的假設問題……鑒于硬體和 C 編譯器的當前狀態。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/475204.html
