Mar*_*inF 24 c# delegates expression func expression-trees
为什么Func<>从Expression<Func<>>via .Compile()创建的文件比直接使用Func<>声明要慢得多?
我刚刚使用Func<IInterface, object>声明直接更改为Expression<Func<IInterface, object>>在我正在处理的应用程序中创建的一个,我注意到性能下降了.
我刚做了一点测试,Func<>从一个Expression创建的"几乎"是Func<>直接声明的时间的两倍.
在我的机器上,Direct Func<>大约需要7.5秒,Expression<Func<>>大约需要12.6秒.
这是我使用的测试代码(运行Net 4.0)
// Direct
Func<int, Foo> test1 = x => new Foo(x * 2);
int counter1 = 0;
Stopwatch s1 = new Stopwatch();
s1.Start();
for (int i = 0; i < 300000000; i++)
{
counter1 += test1(i).Value;
}
s1.Stop();
var result1 = s1.Elapsed;
// Expression . Compile()
Expression<Func<int, Foo>> expression = x => new Foo(x * 2);
Func<int, Foo> test2 = expression.Compile();
int counter2 = 0;
Stopwatch s2 = new Stopwatch();
s2.Start();
for (int i = 0; i < 300000000; i++)
{
counter2 += test2(i).Value;
}
s2.Stop();
var result2 = s2.Elapsed;
public class Foo
{
public Foo(int i)
{
Value = i;
}
public int Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
有什么我可以做的,以便Func<>从Expression<Func<>>直接声明的执行创建?
Gab*_*abe 19
正如其他人所提到的,调用动态委托的开销导致您的速度减慢.在我的电脑上,我的CPU处于3GHz,开销约为12ns.解决这个问题的方法是从已编译的程序集加载方法,如下所示:
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("assembly"), AssemblyBuilderAccess.Run);
var mod = ab.DefineDynamicModule("module");
var tb = mod.DefineType("type", TypeAttributes.Public);
var mb = tb.DefineMethod(
"test3", MethodAttributes.Public | MethodAttributes.Static);
expression.CompileToMethod(mb);
var t = tb.CreateType();
var test3 = (Func<int, Foo>)Delegate.CreateDelegate(
typeof(Func<int, Foo>), t.GetMethod("test3"));
int counter3 = 0;
Stopwatch s3 = new Stopwatch();
s3.Start();
for (int i = 0; i < 300000000; i++)
{
counter3 += test3(i).Value;
}
s3.Stop();
var result3 = s3.Elapsed;
Run Code Online (Sandbox Code Playgroud)
当我添加上面的代码时,result3总是只有一秒的高度result1,大约1ns的开销.
那么为什么test2当你可以有一个更快的委托(test3)时,甚至打扰编译的lambda ()?因为创建动态程序集通常会产生更多的开销,并且每次调用时只会节省10-20ns.
(这不是一个正确的答案,但是有助于发现答案的材料.)
从Mono 2.6.7收集的统计数据 - Debian Lenny - Linux 2.6.26 i686 - 2.80GHz单核:
Func: 00:00:23.6062578
Expression: 00:00:23.9766248
Run Code Online (Sandbox Code Playgroud)
因此,在Mono上,至少两种机制似乎都会产生等效的IL.
这是由Mono gmcs为匿名方法生成的IL :
// method line 6
.method private static hidebysig
default class Foo '<Main>m__0' (int32 x) cil managed
{
.custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
// Method begins at RVA 0x2204
// Code size 9 (0x9)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.2
IL_0002: mul
IL_0003: newobj instance void class Foo::'.ctor'(int32)
IL_0008: ret
} // end of method Default::<Main>m__0
Run Code Online (Sandbox Code Playgroud)
我将致力于提取表达式编译器生成的IL.