可能大家談到反射面部肌肉都開(kāi)始抽搐了吧!因?yàn)樵谕泄苷Z(yǔ)言里面,最臭名昭著的就是反射!它的性能實(shí)在是太低了,甚至在很多時(shí)候讓我們無(wú)法忍受。不過(guò)不用那么糾結(jié)了,老陳今天就來(lái)分享一下如何來(lái)優(yōu)化反射!
概述
本文涉及到的反射優(yōu)化的途徑有如下兩種:
通過(guò)Delegate.CreateDelegate()創(chuàng)建委托進(jìn)行優(yōu)化
通過(guò).NET4的動(dòng)態(tài)運(yùn)行時(shí)進(jìn)行優(yōu)化
如果您還知道其他更加有效的優(yōu)化途徑,請(qǐng)不吝賜教!
準(zhǔn)備工作
今天我們總計(jì)要對(duì)比五種不同的調(diào)用對(duì)象成員的方式,也算是一種性能測(cè)評(píng)。
在開(kāi)始之前,我們首先定義一個(gè)簡(jiǎn)單的對(duì)象和一個(gè)方法,以供測(cè)試之用:
復(fù)制代碼 代碼如下:
namespace ReflectionOptimization
{
public sealed class TestObject
{
public int Add(int a, int b)
{
// 簡(jiǎn)單演示
return a + b;
}
}
}
這個(gè)類(lèi)非常簡(jiǎn)單,只提供了一個(gè)方法,這個(gè)方法返回兩個(gè)整形的和。接下來(lái)我們看看執(zhí)行時(shí)間測(cè)量的代碼,很簡(jiǎn)單,想必您已經(jīng)駕輕就熟了:
復(fù)制代碼 代碼如下:
private static double _Run(string description, Actionint, int> action, int a, int b)
{
if (action == null) throw new ArgumentNullException("action");
// 啟動(dòng)計(jì)時(shí)器
var stopwatch = Stopwatch.StartNew();
// 運(yùn)行要測(cè)量的代碼
action(a, b);
// 終止計(jì)時(shí)
stopwatch.Stop();
// 輸出結(jié)果
Console.WriteLine("{0}: {1}", description, stopwatch.Elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
// 返回執(zhí)行時(shí)間
return stopwatch.Elapsed.TotalMilliseconds;
}
以上測(cè)量時(shí)間的方法返回了執(zhí)行時(shí)間,因?yàn)槲覀円诤竺嬗玫竭@個(gè)值,在執(zhí)行多次之后取個(gè)平均值,以求測(cè)試的公平性、權(quán)威性。
編碼實(shí)現(xiàn)
首先我們來(lái)看看原生反射的實(shí)現(xiàn):
復(fù)制代碼 代碼如下:
var obj = new TestObject();
var add = obj.GetType().GetMethod("Add");
for (var i = 0; i _TIMES; i++) add.Invoke(obj, new object[] {a, b});
然后我們看看.NET4動(dòng)態(tài)編程的實(shí)現(xiàn):
復(fù)制代碼 代碼如下:
dynamic obj = new TestObject();
// 有木有發(fā)現(xiàn)這個(gè)代碼超級(jí)簡(jiǎn)單?
for (var i = 0; i _TIMES; i++) obj.Add(a, b);
最后我們看看如何使用委托來(lái)優(yōu)化反射:
復(fù)制代碼 代碼如下:
// 委托
public delegate int AddMethod(int a, int b);
// 實(shí)現(xiàn)
var obj = new TestObject();
var objType = obj.GetType();
var add = objType.GetMethod("Add");
var d = (AddMethod)Delegate.CreateDelegate(typeof(AddMethod), obj, add);
for (var i = 0; i _TIMES; i++) d(a, b);
上面的代碼看起來(lái)多了幾行,而且還需要自定義一個(gè)委托,寫(xiě)起來(lái)挺麻煩的。因此我們的測(cè)試代碼里面還實(shí)現(xiàn)了另外一種形式,其實(shí)它也是委托:
var d = (FuncTestObject, int, int, int>)Delegate.CreateDelegate(typeof(FuncTestObject, int, int, int>), add);
測(cè)試總結(jié)
我們首先在Debug模式下將整個(gè)測(cè)試代碼運(yùn)行5遍,然后分別記錄平均值,然后再到Release模式下重復(fù)該測(cè)試。
測(cè)試的過(guò)程不再闡述,測(cè)試結(jié)果整理如下:
Debug模式:
調(diào)用方式 |
第一次 |
第二次 |
第三次 |
第四次 |
第五次 |
Generic Call |
1.022425 |
1.012885 |
0.990775 |
1.020950 |
1.046880 |
Reflection |
147.489220 |
146.012010 |
142.690080 |
139.189335 |
141.663475 |
dynamic |
9.645850 |
9.979965 |
9.307235 |
9.532665 |
9.730030 |
Func |
1.201860 |
1.214800 |
1.170215 |
1.189280 |
1.239485 |
Delegate |
1.062215 |
1.061635 |
1.067510 |
1.047180 |
1.075190 |
Release模式:
調(diào)用方式 |
第一次 |
第二次 |
第三次 |
第四次 |
第五次 |
Generic Call |
0.745600 |
0.741365 |
0.722145 |
0.732630 |
0.725645 |
Reflection |
141.778260 |
142.855410 |
142.346095 |
139.649990 |
138.541285 |
dynamic |
9.631460 |
10.341850 |
9.284230 |
9.457580 |
9.060470 |
Func |
0.882100 |
0.852680 |
0.875695 |
0.854655 |
0.831670 |
Delegate |
0.710280 |
0.722465 |
0.723355 |
0.727175 |
0.693320 |
點(diǎn)評(píng)結(jié)論:
- 使用委托優(yōu)化反射之后,其性能與直接調(diào)用相差無(wú)幾,保持在同一個(gè)數(shù)量級(jí)之內(nèi),對(duì)性能要求極度苛刻時(shí)推薦此方案;
- 顯式委托(Delegate)和匿名委托(Func)性能差異非常不明顯,但顯式委托的性能還是好一點(diǎn);
- 原生委托比直接調(diào)用慢出了兩個(gè)數(shù)量級(jí),性能差異達(dá)到了200倍之多!
- .NET 4的動(dòng)態(tài)編程語(yǔ)法相當(dāng)簡(jiǎn)潔,其性能只比直接調(diào)用高出一個(gè)數(shù)量級(jí),由于其語(yǔ)法相當(dāng)簡(jiǎn)潔,我們推薦這種做法!
- 原生反射技術(shù)在Debug模式和Release模式下沒(méi)有太大差異,但其他方式有較為明顯的優(yōu)化效果(請(qǐng)思考為什么);
- 雖然我們今天的測(cè)試不能完全意味著反射優(yōu)化之后可以和直接調(diào)用相媲美,但至少可以從某種程度上擊敗那些個(gè)謠言——誰(shuí)說(shuō)反射就一定會(huì)慢(嘻嘻)!
代碼下載:淺談反射優(yōu)化
您可能感興趣的文章:- .net中 關(guān)于反射的詳細(xì)介紹
- asp.net 反射減少代碼書(shū)寫(xiě)量
- asp.net反射簡(jiǎn)單應(yīng)用實(shí)例
- .NET/C#利用反射調(diào)用含ref或out參數(shù)的方法示例代碼
- .NET/C#如何使用反射注冊(cè)事件詳解
- .NET Core/Framework如何創(chuàng)建委托大幅度提高反射調(diào)用的性能詳解