反射(4)反射性能問題:直接調用vs反射調用

来源:http://www.cnblogs.com/mcgrady/archive/2017/06/13/7001545.html
-Advertisement-
Play Games

很多人都說使用反射會有性能問題,那到底會比直接調用慢多少呢,下麵就來測試一下。 直接調用vs反射調用 下麵就來寫個demo來驗證下直接調用和反射調用的性能差異,代碼如下: 測試結果: 從100萬次調用結果來看,確實就像很多人所說的,兩者在性能上具有數量級的差距。 為什麼反射有性能損失 既然反射性能有 ...


  很多人都說使用反射會有性能問題,那到底會比直接調用慢多少呢,下麵就來測試一下。

直接調用vs反射調用

下麵就來寫個demo來驗證下直接調用和反射調用的性能差異,代碼如下:

 1 namespace ConsoleApplication7
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //比較直接調用和反射調用的性能差異
 8             //7ms vs 365ms
 9             int times = 1000000;
10             var program = new Program();
11             CodeTimerHelper.Initialize();
12 
13             CodeTimerHelper.Time("直接調用", times, () =>
14             {
15                 program.Call();
16             });
17 
18             var t = typeof(Program);
19             var obj = Activator.CreateInstance(t);
20             CodeTimerHelper.Time("反射調用", times, () =>
21             {
22                 t.InvokeMember("Call", BindingFlags.InvokeMethod, null, obj, null);
23             });
24 
25             Console.ReadKey();
26         }
27 
28         /// <summary>
29         /// 測試方法
30         /// </summary>
31         public void Call()
32         {
33         }
34 
35     }
36 }

 

測試結果:

從100萬次調用結果來看,確實就像很多人所說的,兩者在性能上具有數量級的差距。 

 

為什麼反射有性能損失

既然反射性能有損失,那具體損失在哪裡呢?

1,反射是基於程式集和元數據的,在使用反射的時候,會搜索元數據,而元數據是基於字元串的,並且無法預編譯,所以這一系列的操作對性能有影響。

2,大量的裝箱拆箱也對性能有影響。由於我們對目標類型是未知的,而且向方法傳遞的參數通常是object類型的,所以會有大量的裝箱和拆箱。

 

反射性能優化方案

我們已經知道使用反射有性能問題,但是有些場景下又不得不使用反射技術,所以要想辦法優化反射性能。

這裡就引用老趙公開的System.Linq.Expressions.Expression<TDelegate>表達式樹的類,與直接調用進行對比,代碼如下:

1 //3,基於表達式樹
2 var methodInfo = t.GetMethod("Call");
3 var executor = new DynamicMethodExecutor(methodInfo);
4 CodeTimerHelper.Time("Dynamic executor", times, () =>
5 {
6     executor.Execute(obj, null);
7 });

 

測試結果:

哇,同樣的100萬次調用,使用DynamicMethodExecutor調用跟直接調用的性能相差無幾。

附DynamicMethodExecutor的封裝代碼:

 1 /// <summary>
 2 /// 
 3 /// </summary>
 4 public class DynamicMethodExecutor
 5 {
 6     private Func<object, object[], object> m_execute;
 7 
 8     public DynamicMethodExecutor(MethodInfo methodInfo)
 9     {
10         this.m_execute = this.GetExecuteDelegate(methodInfo);
11     }
12 
13     public object Execute(object instance, object[] parameters)
14     {
15         return this.m_execute(instance, parameters);
16     }
17 
18     private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)
19     {
20         // parameters to execute
21         ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");
22         ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
23 
24         // build parameter list
25         List<Expression> parameterExpressions = new List<Expression>();
26         ParameterInfo[] paramInfos = methodInfo.GetParameters();
27         for (int i = 0; i < paramInfos.Length; i++)
28         {
29             // (Ti)parameters[i]
30             BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
31             UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);
32             parameterExpressions.Add(valueCast);
33         }
34 
35         // non-instance for static method, or ((TInstance)instance)
36         Expression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceParameter, methodInfo.ReflectedType);
37 
38         // static invoke or ((TInstance)instance).Method
39         MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions);
40 
41         // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
42         if (methodCall.Type == typeof(void))
43         {
44             Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter);
45             Action<object, object[]> execute = lambda.Compile();
46             return (instance, parameters) =>
47             {
48                 execute(instance, parameters);
49                 return null;
50             };
51         }
52         else
53         {
54             UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
55             Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter);
56             return lambda.Compile();
57         }
58     }

 

 除了使用linq的表達式樹生成Delegate的方法外,還有比如,CodeDom生成代碼並動態編譯,或者使用Emit來直接編寫IL的方法來提高反射的性能,但是相對來說,上面這個方法是最簡單的。

至此,整個反射的總結就完成了!

參考文章

方法的直接調用,反射調用與……Lambda表達式調用

C#基礎知識梳理系列十五:反射

二、什麼是反射、反射可以做些什麼


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linux系統IO中write原型為 ssize_t write(int filedes, const void * buff, size_t nbytes) ; 當調用write寫數據的時候,調用完成後write直接返回,但是磁碟是個慢速設備,操作系統會將數據保存在內核中的緩衝區中,並負責非同步地將 ...
  • 定義 進程的典型定義:進程是程式的一次動態執行 進程在傳統OS中的定義: 進程是進程實體的運行過程,是系統進行資源分配和調度的獨立單位. 一般情況下,我們所說的進程實體(也叫進程映像)簡稱進程,進程實體包括程式段,數據段和進程式控制制塊(PCB). PCB 創建進程的實質就是創建PCB,撤銷進程實質也是 ...
  • 用專業術語來說, 進程是程式的一次動態執行.說簡單點, 就是進程是系統中的某個任務.操作系統中有多個任務需要執行, 那麼怎樣執行才能使它們同步呢? 即如何讓任務併發執行互不影響呢? 這就引出了進程同步中的經典問題: 生產者消費者問題, 哲學家進餐問題, 讀寫問題 生產者-消費者問題 有一群生產者進程 ...
  • 上一篇遠程桌面採用的獨立服務配置不適用於過多用戶,這一篇採用超級Internet伺服器搭建vnc服務可以解決多用戶問題。 vnc之xinetd服務搭建配置 Linux遠程桌面(一):vnc之獨立服務配置 步驟一: (1)檢查是否安裝vnc服務和xinetd服務 (2)若沒有這兩項服務就需要安裝 /e ...
  • 前提: 公司除了領導和開發人員具有管理員許可權,其他人員使用的都是域賬號。 過程: 應要求開發一個 winfrom項目,使用data.oracleclient 本地開發,調試無誤,放到伺服器共用域用戶使用,報錯 OCIEnvCreate 失敗, 返回代碼為-1 , 百思不得其解,網上也有很多這種錯誤, ...
  • 1. Create a windows service project 2. Add Reference to the assembly which contains the contract and its implementation. 3. Remove the Service1.cs, ad ...
  • 一、Parallel類 Parallel類提供了數據和任務的並行性; 二、Paraller.For() Paraller.For()方法類似於C#的for迴圈語句,也是多次執行一個任務。使用Paraller.For()方法,可以並行運行迭代,迭代的順序沒有定義。 在For()方法中,前兩個參數是固定 ...
  • Hello all , 我又回來了 這次我們真是開始來聊聊開源項目里,小而有用的模塊或者組件的開發思想。 同時,軟體已經更新到1.60的版本了,支持新用戶註冊,可以不再使用統一的test賬戶了。 您可以通過以下路徑進行下載: 1、在GitHub上fellow一下項目,下載到本地,生成一下,即可獲取最 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...