我之前发布了this question关于在C#中动态编译代码的问题,答案引出了另一个问题。
一个建议是我使用代理,我尝试过并且效果很好。然而,它们比直接呼叫慢约8.4倍,这没有任何意义。
此代码有什么问题?
我的结果,.Net 4.0,64位,直接运行exe:62,514,530
public static int Execute(int i) { return i * 2; }
private void button30_Click(object sender, EventArgs e)
{
CSharpCodeProvider foo = new CSharpCodeProvider();
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true,
CompilerOptions = @"/optimize",
},
@"public class FooClass { public static int Execute(int i) { return i * 2; }}"
);
var type = res.CompiledAssembly.GetType("FooClass");
var obj = Activator.CreateInstance(type);
var method = type.GetMethod("Execute");
int i = 0, t1 = Environment.TickCount, t2;
//var input = new object[] { 2 };
//for (int j = 0; j < 10000000; j++)
//{
// input[0] = j;
// var output = method.Invoke(obj, input);
// i = (int)output;
//}
//t2 = Environment.TickCount;
//MessageBox.Show((t2 - t1).ToString() + Environment.NewLine + i.ToString());
t1 = Environment.TickCount;
for (int j = 0; j < 100000000; j++)
{
i = Execute(j);
}
t2 = Environment.TickCount;
MessageBox.Show("Native: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
var func = (Func<int, int>) Delegate.CreateDelegate(typeof (Func<int, int>), method);
t1 = Environment.TickCount;
for (int j = 0; j < 100000000; j++)
{
i = func(j);
}
t2 = Environment.TickCount;
MessageBox.Show("Dynamic delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
Func<int, int> funcL = Execute;
t1 = Environment.TickCount;
for (int j = 0; j < 100000000; j++)
{
i = funcL(j);
}
t2 = Environment.TickCount;
MessageBox.Show("Delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
}
答案 0 :(得分:5)
正如汉斯在你的问题评论中提到的那样,Execute
方法非常简单,几乎可以肯定的是,你的“原生”测试中的抖动也是如此。
所以你看到的不是标准方法调用和委托调用之间的比较,而是内联i * 2
操作和委托调用之间的比较。 (并且i * 2
操作可能只归结为一条机器指令,大约可以达到最快速度。)
使Execute
方法更加复杂,以防止内联(和/或使用MethodImplOptions.NoInlining
编译器提示执行此操作);那么你将在标准方法调用和委托调用之间进行更现实的比较。在大多数情况下,差异可以忽略不计:
[MethodImpl(MethodImplOptions.NoInlining)]
static int Execute(int i) { return ((i / 63.53) == 34.23) ? -1 : (i * 2); }
public static volatile int Result;
private static void Main(string[] args)
{
const int iterations = 100000000;
{
Result = Execute(42); // pre-jit
var s = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
Result = Execute(i);
}
s.Stop();
Console.WriteLine("Native: " + s.ElapsedMilliseconds);
}
{
Func<int, int> func;
using (var cscp = new CSharpCodeProvider())
{
var cp = new CompilerParameters { GenerateInMemory = true, CompilerOptions = @"/optimize" };
string src = @"public static class Foo { public static int Execute(int i) { return ((i / 63.53) == 34.23) ? -1 : (i * 2); } }";
var cr = cscp.CompileAssemblyFromSource(cp, src);
var mi = cr.CompiledAssembly.GetType("Foo").GetMethod("Execute");
func = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), mi);
}
Result = func(42); // pre-jit
var s = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
Result = func(i);
}
s.Stop();
Console.WriteLine("Dynamic delegate: " + s.ElapsedMilliseconds);
}
{
Func<int, int> func = Execute;
Result = func(42); // pre-jit
var s = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
Result = func(i);
}
s.Stop();
Console.WriteLine("Delegate: " + s.ElapsedMilliseconds);
}
}
答案 1 :(得分:4)
这很有道理。代表不是函数指针。它们意味着类型检查,安全性和许多其他东西。它们更接近于虚拟函数调用的速度(参见this post),即使性能影响来自完全不同的东西。
为了更好地比较不同的调用技术(问题中未提及其中一些),请阅读this article。