1. 程式人生 > 其它 >[.NET學習筆記] - C++/CLI專案遷移至.NET5.0時Marshal::StructureToPtr的效能問題

[.NET學習筆記] - C++/CLI專案遷移至.NET5.0時Marshal::StructureToPtr的效能問題

技術標籤:C#/.NET錯誤處理

背景

手頭上有個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 Versionv4.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個標準時鍾可以用來計時:

  1. system_clock - 系統提供的實時時鐘
  2. high_resolution_clock - 當前系統時鐘週期最短的時鐘
  3. steady_clock - 不會被調整的單調時鐘

測出來的結果讓我大跌眼鏡。針對C++/CLI專案程式碼,.NET FW 4.8的效能大概是.NET 5.0的3-10倍(測得比較隨意,都是些簡單程式碼重複執行)。一開始我以為是遷移後的C++/CLI專案需要其他設定,所以花了一天時間各種配置調優,結果變化不大。
我嘗試一行行程式碼做效能對比,發現效能差異主要提現在Marshal::StructureToPtrMarshal::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::StructureToPtrMarshal::PtrToStructure的場景比較多,所以受到了影響。但尋根溯源,這還是.NET 5.0的問題。畢竟是新版本,一不小心就踩坑翻車了。本文也是提醒大家,如果需要在.NET 5.0中使用Marshal::StructureToPtrMarshal::PtrToStructure等方法,務必小心謹慎。