在通用 Windows 平台中將 Vector<T> 用於 SIMD
我正在嘗試使用 System.Numerics.Vector(T) 對算法進行矢量化並利用 CPU 的 SIMD 操作。但是,我的向量實現比我的原始實現慢得多。使用可能沒有記錄的向量有什麼技巧嗎?這裡的具體用途是嘗試加速 kb 數據的異或運算。
不幸的是,我能找到的幾乎所有文件都是基於 RyuJIT 的預發布版本,我不知道有多少材料可以移植到 .NET Native。
當我在 Vector xor 操作期間檢查反彙編時,它顯示:
00007FFB040A9C10 xor eax,eax 00007FFB040A9C12 mov qword ptr [rcx],rax 00007FFB040A9C15 mov qword ptr [rcx+8],rax 00007FFB040A9C19 mov rax,qword ptr [r8] 00007FFB040A9C1C xor rax,qword ptr [rdx] 00007FFB040A9C1F mov qword ptr [rcx],rax 00007FFB040A9C22 mov rax,qword ptr [r8+8] 00007FFB040A9C26 xor rax,qword ptr [rdx+8] 00007FFB040A9C2A mov qword ptr [rcx+8],rax 00007FFB040A9C2E mov rax,rcx為什麼它不為此使用 xmm 寄存器和 SIMD 指令?同樣奇怪的是,SIMD 指令是為我沒有明確矢量化的程式碼版本生成的,但它們從未被執行,有利於正常寄存器和指令。
我確保我執行的是 Release、x64、Optimize code 啟用。我在 x86 編譯中看到了類似的行為。我對機器級的東西有點新手,所以這裡可能只是發生了一些我沒有正確理解的事情。
框架版本為 4.6,Vector.IsHardwareAccelerated 在執行時為 false。
更新: “使用 .NET Native 工具鏈編譯”是罪魁禍首。啟用它會導致 Vector.IsHardwareAccelerated == false; 禁用它會導致 Vector.IsHardwareAccelerated == true。我已經確認,當禁用 .NET Native 時,編譯器正在使用 ymm 寄存器生成 AVX 指令。這就引出了一個問題……為什麼在 .NET Native 中未啟用 SIMD?有什麼辦法可以改變嗎?
**更新切線:**我發現未執行自動 SSE 向量化數組程式碼的原因是編譯器插入了一條指令,該指令查看數組的開頭是否位於比最後一個元素低的地址數組,如果是的話,只使用普通寄存器。我認為這一定是編譯器中的一個錯誤,因為按照慣例,數組的開頭應始終位於比其最後一個元素低的地址。它是測試每個操作數數組的記憶體地址的一組指令的一部分,我認為是為了確保它們不重疊。我為此送出了 Microsoft Connect 錯誤報告:https ://connect.microsoft.com/VisualStudio/feedback/details/1831117
我聯繫了微軟,微軟發布了 .Net Native 問題和疑慮的聯繫地址:https ://msdn.microsoft.com/en-us/vstudio/dotnetnative.aspx
我的問題已送出給 Microsoft 程式碼生成和優化技術團隊的首席軟體工程經理 Ian Bearman:
目前 .NET Native 不優化 System.Numerics 庫並依賴於預設庫實現。這可能(閱讀:很可能)導致使用 System.Numerics 編寫的程式碼在 .NET Native 中的性能不如其他 CLR 實現。
雖然這很不幸,但 .NET Native 確實支持使用上述 C++ 優化帶來的自動矢量化。目前發布的 .NET Native 編譯器在 x86 和 x64 上的自動矢量化中支持 SSE2 ISA,在 ARM 上支持 NEON ISA。
他還提到,他們希望從 C++ 編譯器中引入生成所有向量指令(AVX、SSE 等)的能力,並根據執行時指令集的檢測進行分支。
然後他建議,如果指令的使用真的很關鍵,可以用 C++ 建構組件,它可以訪問編譯器內在函式(並且可能還有這種分支能力?),然後輕鬆地與剩餘的 C# 應用程序介面。
至於跳過的 SSE2 指令,我需要做的就是將循環的“a = a ^ b”替換為“a ^ = b”。由於它們應該是等效的表達式,因此它似乎是一個錯誤,但幸運的是有一個解決方法。