[.NET學習筆記] - C++/CLI專案遷移至.NET5.0時Marshal::StructureToPtr的效能問題
背景
手頭上有個C++/CLI
專案,主要是用來封裝C++
的dll,方便.NET呼叫的。之前是在.NET FW 4.8 runtime
上,最近想遷移至.NET Core runtime
。當前的時間點有兩個選擇,一個是.NET Core 3.1
,一個是.NET 5.0
。一個是LTS
版,一個是新鮮出爐版。沒有多想,覺得.NET 5.0
雖然很新,有很多坑不穩定,但不至於被我踩上,加上網上吹的那麼多效能提升,首選了升級至.NET 5.0
。
C++/CLI專案的migrate
C++/CLI
專案從.NET FW 遷移至.NET改動不大,操作如下:
主要就是選擇新版的rumtime
/clr
更改為/clr:netcore
。調整對應的.NET Traget Framework Version
。v4.8
-> .NET Core 3.1 or .NET 5.0
。
分析
由於是個C++/CLI
專案,對效能還是比較敏感的,雖然網上有很多效能測試的文章,但少有C++/CLI專案在不同runtime
下的測試對比。
測試程式碼在C++/CLI
專案中,執行耗時計算使用<chrono>
。
// 示例虛擬碼 #include <chrono> auto tp1 = chrono::high_resolution_clock::now(); // code auto tp2 = chrono::high_resolution_clock::now(); auto duration = chrono::duration_cast<chrono::microseconds>(tp2 - tp1).count();
C++11 chrono標頭檔案提供了3個標準時鍾可以用來計時:
- system_clock - 系統提供的實時時鐘
- high_resolution_clock - 當前系統時鐘週期最短的時鐘
- steady_clock - 不會被調整的單調時鐘
測出來的結果讓我大跌眼鏡。針對C++/CLI
專案程式碼,.NET FW 4.8
的效能大概是.NET 5.0
的3-10倍(測得比較隨意,都是些簡單程式碼重複執行)。一開始我以為是遷移後的C++/CLI
專案需要其他設定,所以花了一天時間各種配置調優,結果變化不大。
我嘗試一行行程式碼做效能對比,發現效能差異主要提現在Marshal::StructureToPtr
和Marshal::PtrToStructure
就在我心灰意冷,準備回到
.NET FW 4.8
懷抱的時候,我查到了這個。
Github dotnet 5.0 Marshal.PtrToStructure is slower 8x than netcore3.1 #45100
這個github的issue
和的我情況差不多,雖然他並不是C++/CLI
專案,但也能說明一定的問題。即.NET 5.0
裡的Marshal::PtrToStructure
效能有問題,至少比.NET Core 3.1
差了很多。
受到啟發,我把C++/CLI
專案runtime
改成了.NET Core 3.1
並與.NET FW 4.8
做比較測試。測下來,基本上效能差不多,.NET Core 3.1
比.NET FW 4.8
有略微的優勢。至少這給了我信心,可以暫時先遷往3.1
,等時機成熟,再前往LTS
版的.NET 6.0
。
總結
本篇文章遇到的問題,嚴格意義上,不能算是C++/CLI
專案的問題,但由於C++/CLI
專案用到Marshal::StructureToPtr
和Marshal::PtrToStructure
的場景比較多,所以受到了影響。但尋根溯源,這還是.NET 5.0
的問題。畢竟是新版本,一不小心就踩坑翻車了。本文也是提醒大家,如果需要在.NET 5.0
中使用Marshal::StructureToPtr
和Marshal::PtrToStructure
等方法,務必小心謹慎。