我在此 (Generating Dynamic Methods ) 上找到了 Microsoft 博客,并比较了静态方法、编译表达式树和 IL 注入之间的性能。
代码如下:
static void Main(string[] args)
{
double acc = 0;
var il = ILFact();
il.Invoke(1);
var et = ETFact();
et(1);
Stopwatch sw = new Stopwatch();
for (int k = 0; k < 10; k++)
{
long time1, time2;
sw.Restart();
for (int i = 0; i < 30000; i++)
{
var result = CSharpFact(i);
acc += result;
}
sw.Stop();
time1 = sw.ElapsedMilliseconds;
sw.Restart();
for (int i = 0; i < 30000; i++)
{
double result = il.Invoke(i);
acc += result;
}
sw.Stop();
time2 = sw.ElapsedMilliseconds;
sw.Restart();
for (int i = 0; i < 30000; i++)
{
var result = et(i);
acc += result;
}
sw.Stop();
Console.WriteLine("{0,6} {1,6} {2,6}", time1, time2, sw.ElapsedMilliseconds);
}
Console.WriteLine("\n{0}...\n", acc);
Console.ReadLine();
}
static Func<int, int> ILFact()
{
var method = new DynamicMethod(
"factorial", typeof(int),
new[] { typeof(int) }
);
var il = method.GetILGenerator();
var result = il.DeclareLocal(typeof(int));
var startWhile = il.DefineLabel();
var returnResult = il.DefineLabel();
// result = 1
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Stloc, result);
// if (value <= 1) branch end
il.MarkLabel(startWhile);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ble_S, returnResult);
// result *= (value--)
il.Emit(OpCodes.Ldloc, result);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Sub);
il.Emit(OpCodes.Starg_S, 0);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Stloc, result);
// end while
il.Emit(OpCodes.Br_S, startWhile);
// return result
il.MarkLabel(returnResult);
il.Emit(OpCodes.Ldloc, result);
il.Emit(OpCodes.Ret);
return (Func<int, int>)method.CreateDelegate(typeof(Func<int, int>));
}
static Func<int, int> ETFact()
{
// Creating a parameter expression.
ParameterExpression value = Expression.Parameter(typeof(int), "value");
// Creating an expression to hold a local variable.
ParameterExpression result = Expression.Parameter(typeof(int), "result");
// Creating a label to jump to from a loop.
LabelTarget label = Expression.Label(typeof(int));
// Creating a method body.
BlockExpression block = Expression.Block(
// Adding a local variable.
new[] { result },
// Assigning a constant to a local variable: result = 1
Expression.Assign(result, Expression.Constant(1)),
// Adding a loop.
Expression.Loop(
// Adding a conditional block into the loop.
Expression.IfThenElse(
// Condition: value > 1
Expression.GreaterThan(value, Expression.Constant(1)),
// If true: result *= value --
Expression.MultiplyAssign(result,
Expression.PostDecrementAssign(value)),
// If false, exit from loop and go to a label.
Expression.Break(label, result)
),
// Label to jump to.
label
)
);
// Compile an expression tree and return a delegate.
return Expression.Lambda<Func<int, int>>(block, value).Compile();
}
static int CSharpFact(int value)
{
int result = 1;
while (value > 1)
{
result *= value--;
}
return result;
}
这是在 i7-920 上运行的 3 次。构建 - 发布 x64
583 542 660
577 578 666
550 558 652
576 575 648
570 574 641
560 554 640
558 551 650
561 551 666
624 638 683
564 581 647
-3778851060...
482 482 557
489 490 580
514 517 606
541 537 626
551 524 641
563 555 631
552 558 644
572 541 652
591 549 652
562 552 639
-3778851060...
482 482 560
507 503 591
525 543 596
555 531 609
553 556 634
540 552 640
579 598 635
607 554 639
588 585 679
547 560 643
-3778851060...
平均:554 549 634
静态 vs IL - IL 快 1%(!)不知道为什么
静态 vs ET - 静态比表达式树快 14%
编辑(2014 年 2 月):我刚刚在 .NET 4.5 和更快的 CPU 上运行了上面的代码(稍作修改)并获得了新的结果集:Method / ET - 9%,方法 / IL - 4%
因此之前的结果不再有效 - 静态方法调用总是更快..
*不确定是新硬件 (i7-3820) 还是新 .NET,或者我在旧测试中做错了什么。*
另一个有趣的结果是,在 32 位 中,完全相同的代码在 3 之间显示绝对没有区别。
Method IL ET
--------------------
368 382 399
367 382 399
367 382 399
367 382 400
367 383 400
367 382 399
367 383 399
367 382 399
367 382 399
367 383 400
367 382 399
367 382 399
367 382 399
367 382 399
367 383 400
367 382 400
367 383 399
367 383 400
367 382 399
367 382 400
-7557702120...
--------------------
367.05 382.30 399.35