之前发现了一个很有意思的事,如果将方法作为委托参数传递给另一个方,循环N次执行这个操作,好像方法隐式转换成委托时,会有额外的性能消耗,相比直接弄一个匿名委托,要慢很多,代码如下
static void Start() {
for (int i = 0; i < 5; i++) {
CompareSpeed(AAA);
CompareSpeed(BBB);
CompareSpeed(AAA);
Console.WriteLine("===========");
}
}
readonly static int len = 1024 * 1024;
static void CompareSpeed(Action action) {
Stopwatch sw = new Stopwatch();
sw.Start();
action();
Console.WriteLine(action.Method.Name + ":" + sw.ElapsedTicks);
}
static void AAA() {
for (int i = 0; i < len; i++) {
Test(DoSomething);
}
}
static void BBB() {
for (int i = 0; i < len; i++) {
Test(() => {
int a = 1;
});
}
}
static void Test(Action action) {
action();
}
static void DoSomething() {
int a = 1;
}
结果如下:
在1M的循环次数下,明显可以看到AAA普遍慢于BBB
BBB我理解的是因为里面的匿名函数只创建一次,之后在循环时,都是直接拿已经创建好的
那么AAA慢是不是因为每次都要执行方法隐式转成Action啊,这个隐式转成具体是做了什么啊,我知道委托怎么用,但是不知道委托内部是怎么实现的,网上也收不到相关的内容,有大佬能详细的讲讲吗
而且我发现,如果将循环次数减少,就没有这么明显了,基本都是刚开始的几次AAA比BBB快,次数上来后才体现出BBB比AAA快,这么看的话,是不是AAA的隐式转换要比新创建一个匿名函数要快很多啊,() => { int a = 1; }按我的理解是匿名函数,它和普通的方法有什么区别啊,它可以直接赋值给Action,它的类型是委托吗,还是说它依然是个方法,依然是隐式转换成Action的啊
Test(DoSomething); ==> Test(new Action(DoSomething))
Test(() => {int a = 1;});
==>
static 自动生成的静态类 {static action=new Action(()=>int a=1;)}
Test(自动生成的静态类.action) // 直接引用传递.
//会编译成一个静态类包含这个静态Action方法.
所以AAA方法的多出来的消耗在每次循环的new Action上.
方法隐式转成Action委托类型,具体是做了什么啊
new Action(DoSomething) 是创建了一个委托实例,然后把DoSomething传进去作为成员了吗,调用action()的时候,内部是怎么处理的啊
@WmW:
Action 就是delegate. Action的定义是 public delegate void Action()
delegate可以大致理解为是一个引用类型 object, 里面保存了传进去的这个方法的函数指针地址.
delegate的定义:
public abstract class Delegate : ICloneable, ISerializable
{
public MethodInfo Method {get;}
public object Target {get;}
}
调用action() 等同于 action.Invoke(); 这行代码也就等于告诉运行时, 我要call 这个函数指针所代表的method.
所以你的测试方法1 每次都要new一个object(delegate, 也就是Action)
方法2 只new一次, 后续的调用都是直接传递这个object引用.
至于你的问题, 如果要深究 可以看看文章: https://mattwarren.org/2017/01/25/How-do-.NET-delegates-work/ 以及文章给给出的CLI参考设计文档.
@czd890: 明白了,感谢!