日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#类与结构体究竟谁快——各种函数调用模式速度评测

發布時間:2023/12/9 C# 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#类与结构体究竟谁快——各种函数调用模式速度评测 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以前我一直有個疑惑——在C#中,究竟是類(class)比較快,還是結構體(struct)比較快?
當時沒有深究。

最近我遇到一個難題,需要將一些運算大的指針操作代碼給封裝一下。原先為了性能,這些代碼是以硬編碼的形式混雜在算法邏輯之中,不但影響了算法邏輯的可讀性,其本身的指針操作代碼枯燥、難懂、易寫錯,不易維護。所以我希望將其封裝一下,簡化代碼編寫、提高可維護性,但同時要盡可能地保證性能。
由于那些指針操作代碼很靈活,簡單的封裝不能解決問題,還需要用到接口(interface)以實現一些動態調用功能。
為了簡化代碼,還打算實現一些泛型方法。
本來還想因32位指針、64位指針的不同而構造泛型類,可惜發現C#不支持將int/long作為泛型類型約束,只好作罷。將設計改為——分別為32位指針、64位指針編寫不同的類,它們實現同一個接口。

在C#中,有兩類封裝技術——
1.基于類(class)的封裝。在基類中定義好操作方法,然后在派生類中實現操作方法。
2.基于結構體(struct)的封裝。在接口中定義好操作方法,然后在結構體中實現該接口的操作方法。
我分別使用這兩類封裝技術編寫測試代碼,然后做性能測試。

經過反復思索,考慮 類、結構體、接口、泛型 的組合,我找出了15種函數調用模式——
硬編碼
靜態調用
調用派生類
調用結構體
調用基類
調用派生類的接口
調用結構體的接口
基類泛型調用派生類
基類泛型調用基類
接口泛型調用派生類
接口泛型調用結構體
接口泛型調用結構體引用
接口泛型調用基類
接口泛型調用派生類的接口
接口泛型調用結構體的接口


測試代碼為——?

using System; using System.Collections.Generic; using System.Text; using System.Diagnostics;namespace TryPointerCall {/// <summary>/// 指針操作接口/// </summary>public interface IPointerCall{/// <summary>/// 指針操作/// </summary>/// <param name="p">源指針</param>/// <returns>修改后指針</returns>unsafe byte* Ptr(byte* p);}#region 非泛型/// <summary>/// [非泛型] 指針操作基類/// </summary>public abstract class PointerCall : IPointerCall{public abstract unsafe byte* Ptr(byte* p);}/// <summary>/// [非泛型] 指針操作派生類: 指針+偏移/// </summary>public class PointerCallAdd : PointerCall{/// <summary>/// 偏移值/// </summary>public int Offset = 0;public override unsafe byte* Ptr(byte* p){return unchecked(p + Offset);}}/// <summary>/// [非泛型] 指針操作結構體: 指針+偏移/// </summary>public struct SPointerCallAdd : IPointerCall{/// <summary>/// 偏移值/// </summary>public int Offset;public unsafe byte* Ptr(byte* p){return unchecked(p + Offset);}}#endregion#region 泛型// !!! C#不支持將整數類型作為泛型約束 !!!//public abstract class GenPointerCall<T> : IPointerCall where T: int, long//{// public abstract unsafe byte* Ptr(byte* p);// void d()// {// }//}#endregion#region 全部測試/// <summary>/// 指針操作的一些常用函數/// </summary>public static class PointerCallTool{private const int CountLoop = 200000000; // 循環次數/// <summary>/// 調用指針操作/// </summary>/// <typeparam name="T">具有IPointerCall接口的類型。</typeparam>/// <param name="ptrcall">調用者</param>/// <param name="p">源指針</param>/// <returns>修改后指針</returns>public static unsafe byte* CallPtr<T>(T ptrcall, byte* p) where T : IPointerCall{return ptrcall.Ptr(p);}public static unsafe byte* CallClassPtr<T>(T ptrcall, byte* p) where T : PointerCall{return ptrcall.Ptr(p);}public static unsafe byte* CallRefPtr<T>(ref T ptrcall, byte* p) where T : IPointerCall{return ptrcall.Ptr(p);}// C#不允許將特定的結構體作為泛型約束。所以對于結構體只能采用上面那個方法,通過IPointerCall接口進行約束,可能會造成性能下降。//public static unsafe byte* SCallPtr<T>(T ptrcall, byte* p) where T : SPointerCallAdd//{// return ptrcall.Ptr(p);//}private static int TryIt_Static_Offset;private static unsafe byte* TryIt_Static_Ptr(byte* p){return unchecked(p + TryIt_Static_Offset);}/// <summary>/// 執行測試 - 靜態調用/// </summary>/// <param name="sOut">文本輸出</param>private static unsafe void TryIt_Static(StringBuilder sOut){TryIt_Static_Offset = 1;// == 性能測試 ==byte* p = null;Stopwatch sw = new Stopwatch();int i;unchecked{#region 測試// 硬編碼sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = p + TryIt_Static_Offset;}sw.Stop();sOut.AppendLine(string.Format("硬編碼:\t{0}", sw.ElapsedMilliseconds));// 靜態調用sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = TryIt_Static_Ptr(p);}sw.Stop();sOut.AppendLine(string.Format("靜態調用:\t{0}", sw.ElapsedMilliseconds));#endregion // 測試}}/// <summary>/// 執行測試 - 非泛型/// </summary>/// <param name="sOut">文本輸出</param>private static unsafe void TryIt_NoGen(StringBuilder sOut){// 創建PointerCallAdd pca = new PointerCallAdd();SPointerCallAdd spca;pca.Offset = 1;spca.Offset = 1;// 轉型PointerCall pca_base = pca;IPointerCall pca_itf = pca;IPointerCall spca_itf = spca;// == 性能測試 ==byte* p = null;Stopwatch sw = new Stopwatch();int i;unchecked{#region 調用#region 直接調用// 調用派生類sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = pca.Ptr(p);}sw.Stop();sOut.AppendLine(string.Format("調用派生類:\t{0}", sw.ElapsedMilliseconds));// 調用結構體sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = spca.Ptr(p);}sw.Stop();sOut.AppendLine(string.Format("調用結構體:\t{0}", sw.ElapsedMilliseconds));#endregion // 直接調用#region 間接調用// 調用基類sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = pca_base.Ptr(p);}sw.Stop();sOut.AppendLine(string.Format("調用基類:\t{0}", sw.ElapsedMilliseconds));// 調用派生類的接口sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = pca_itf.Ptr(p);}sw.Stop();sOut.AppendLine(string.Format("調用派生類的接口:\t{0}", sw.ElapsedMilliseconds));// 調用結構體的接口sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = spca_itf.Ptr(p);}sw.Stop();sOut.AppendLine(string.Format("調用結構體的接口:\t{0}", sw.ElapsedMilliseconds));#endregion // 間接調用#endregion // 調用#region 泛型調用#region 泛型基類約束// 基類泛型調用派生類sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallClassPtr(pca, p);}sw.Stop();sOut.AppendLine(string.Format("基類泛型調用派生類:\t{0}", sw.ElapsedMilliseconds));// 基類泛型調用基類sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallClassPtr(pca_base, p);}sw.Stop();sOut.AppendLine(string.Format("基類泛型調用基類:\t{0}", sw.ElapsedMilliseconds));#endregion // 泛型基類約束#region 泛型接口約束 - 直接調用// 接口泛型調用派生類sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallPtr(pca, p);}sw.Stop();sOut.AppendLine(string.Format("接口泛型調用派生類:\t{0}", sw.ElapsedMilliseconds));// 接口泛型調用結構體sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallPtr(spca, p);}sw.Stop();sOut.AppendLine(string.Format("接口泛型調用結構體:\t{0}", sw.ElapsedMilliseconds));// 接口泛型調用結構體引用sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallRefPtr(ref spca, p);}sw.Stop();sOut.AppendLine(string.Format("接口泛型調用結構體引用:\t{0}", sw.ElapsedMilliseconds));#endregion // 直接調用#region 間接調用// 接口泛型調用基類sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallPtr(pca_base, p);}sw.Stop();sOut.AppendLine(string.Format("接口泛型調用基類:\t{0}", sw.ElapsedMilliseconds));// 接口泛型調用派生類的接口sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallPtr(pca_itf, p);}sw.Stop();sOut.AppendLine(string.Format("接口泛型調用派生類的接口:\t{0}", sw.ElapsedMilliseconds));// 接口泛型調用結構體的接口sw.Reset();sw.Start();for (i = 0; i < CountLoop; ++i){p = CallPtr(spca_itf, p);}sw.Stop();sOut.AppendLine(string.Format("接口泛型調用結構體的接口:\t{0}", sw.ElapsedMilliseconds));#endregion // 間接調用#endregion // 泛型調用}}/// <summary>/// 執行測試 - 泛型/// </summary>/// <param name="sOut">文本輸出</param>private static unsafe void TryIt_Gen(StringBuilder sOut){// !!! C#不支持將整數類型作為泛型約束 !!!}/// <summary>/// 執行測試/// </summary>public static string TryIt(){StringBuilder sOut = new StringBuilder();sOut.AppendLine("== PointerCallTool.TryIt() ==");TryIt_Static(sOut);TryIt_NoGen(sOut);TryIt_Gen(sOut);sOut.AppendLine();return sOut.ToString();}} #endregion}


編譯器——
VS2005:Visual Studio 2005 SP1。
VS2010:Visual Studio 2010 SP1。
采用上述編譯器編譯為Release版程序,最大速度優化。


機器A——
HP CQ42-153TX
處理器:Intel Core i5-430M(2.26GHz, Turbo 2.53GHz, 3MB L3)
內存容量:2GB (DDR3-1066)

機器B——
DELL Latitude E6320
處理器:Intel i3-2310M(2.1GHz, 3MB L3)
內存容量:4GB (DDR3-1333,雙通道)

測試環境——
A_2005:機器A,VS2005,Window 7 32位。
A_2010:機器A,VS2010,Window 7 32位。
B_2005:機器B,VS2005,Window 7 64位(x64)。
B_2010:機器B,VS2010,Window 7 64位(x64)。
B_2010xp:機器B,VS2010,Window XP SP3 32位。

測試結果(單位:毫秒)——

模式A_2005A_2010B_2005B_2010B_2010xp
硬編碼163162232495
靜態調用162161232395
調用派生類570487456487606
調用結構體16216095620100
調用基類565571453513874
調用派生類的接口810728779708929
調用結構體的接口10521055117511751267
基類泛型調用派生類97556810551148671
基類泛型調用基類98456910551152671
接口泛型調用派生類1383729134615311062
接口泛型調用結構體5661627671149107
接口泛型調用結構體引用487164752816100
接口泛型調用基類1378812133715351072
接口泛型調用派生類的接口1376810133815331102
接口泛型調用結構體的接口15421133248620131365


結果分析——
先看第1列數據(A_2005),發現“靜態調用”、“調用結構體”與“硬編碼”的時間幾乎一致,很可能做了函數展開優化。其次最快的是“接口泛型調用結構體引用”,比“接口泛型調用結構體”快了16%。但是“接口泛型調用結構體的接口”最慢,“調用結構體的接口”也比較慢。其他的基于類的調用模式的速度排在中間。而且發現泛型方法速度較慢。
然后看第2列數據(A_2010),發現“接口泛型調用結構體”、“接口泛型調用結構體引用”也與“硬編碼”的時間幾乎一致,表示它們也是做了函數展開優化的,看來在VS2010中不需要使用ref優化結構體參數。“調用結構體的接口”、“接口泛型調用結構體的接口”兩個都成了墊底。泛型方法的速度有了很大的提高,幾乎與非泛型調用速度相當。
再看第3列數據(B_2005),并與第1列(A_2005)進行比較,發現“靜態調用”與“硬編碼”的時間幾乎一致,而“調用結構體”要慢一些。“接口泛型調用結構體”、“接口泛型調用結構體引用”比較慢,排在了“調用基類”、“調用派生類”的后面。可能是64位環境(x64)的特點吧。
再看第4列數據(B_2010),并與第3列(B_2005)進行比較,發現大部分變慢了,尤其是結構體相關的,難道VS2010的x64性能還不如VS2005?我將平臺改為“x64”又編譯了一次,結果依舊。
再看第5列數據(B_2010xp),發現32位WinXP下的大部分項目比64位Win7下要快,真詭異。而且發現“靜態調用”、“調用結構體”與“硬編碼”的時間幾乎一致,看來“調用結構體”一直是被函數展開優化的,而64位下的靜態調用有著更深層次的優化,所以比不過。


我覺得在要求性能的情況下,使用結構體封裝指針操作比較好,因為直接調用時會做函數展開優化,大多數情況下與硬編碼的性能一致。在遇到需要一些靈活功能時,可考慮采用“接口泛型調用結構體引用”的方式,速度有所下降。接口方式最慢,盡可能不用。一定要用接口的話,應優先選擇非泛型版。

(完)

測試程序exe——
http://115.com/file/dn6hvcm3

http://download.csdn.net/detail/zyl910/3614511

源代碼下載——
http://115.com/file/aqz70zy3

http://download.csdn.net/detail/zyl910/3614514



目錄——
C#類與結構體究竟誰快——各種函數調用模式速度評測:http://www.cnblogs.com/zyl910/archive/2011/09/19/2186623.html
再探C#類與結構體究竟誰快——考慮棧變量、棧分配、64位整數、密封類:http://www.cnblogs.com/zyl910/archive/2011/09/20/2186622.html
三探C#類與結構體究竟誰快——MSIL(微軟中間語言)解讀:http://www.cnblogs.com/zyl910/archive/2011/09/24/2189403.html
四探C#類與結構體究竟誰快——跨程序集(assembly)調用:http://www.cnblogs.com/zyl910/archive/2011/10/01/2197844.html

轉載于:https://www.cnblogs.com/zyl910/archive/2011/09/19/2186623.html

總結

以上是生活随笔為你收集整理的C#类与结构体究竟谁快——各种函数调用模式速度评测的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。