我已经阅读了几篇关于编写和编译动态C#代码的帖子。例如,this post。我知道它可以通过多种方式完成。
但是,调用代码调用程序很慢。我做了一个简单的基准测试,它比调用本机方法慢了约500倍。
我想要做的是相当于加载DLL并直接调用其中一个方法(“本机”),这将带来我想要的速度优势。
最简单的方法是什么?将动态代码编译为dll然后加载它?可以在记忆中完成吗?
修改
我不关心编译时间。只执行。
编辑2,3
这是我写的基准代码:
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 :(得分:8)
是的,如果您通过MethodInfo
或非特定Delegate
调用,那么它确实会很慢。诀窍是:不要那样做。各种方法:
对于单个方法,请通过基本的但是键入的委托(例如Action
),或作为通用的catch-all,Func<object[], object>
- 并使用{ {1}}创建类型委托:
Delegate.CreateDelegate
另一种变体是使用Action doSomething = (Action)Delegate.CreateDelegate(typeof(Action), method);
API(具有Expression
方法)或.Compile()
(具有DynamicMethod
)。关键是:您必须获得类型委托,并使用键入调用(不是CreateDelegate()
)进行调用。
对于生成整个类型的更复杂情况,请考虑实现您知道的接口,即
.DynamicInvoke
再次;在初始演员(非常便宜)之后你可以使用静态代码:
IFoo foo = (IFoo)Activator.CreateInstance(...);
不 如果您正在进行任何表演,请使用foo.Bar();
或someDelegate.DynamicInvoke(...)
。
答案 1 :(得分:3)
除了Marc的建议,您还可以通过指定“优化”编译器选项来提高速度:
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true,
CompilerOptions = "/optimize"
},
答案 2 :(得分:1)
认为值得展示所有潜在选项的外观及其性能特征。给出以下辅助类和函数:
public void Test(Func<int> func)
{
var watch = new Stopwatch();
watch.Start();
for (var i = 0; i <= 1000000; i++)
{
var test = func();
}
Console.WriteLine(watch.ElapsedMilliseconds);
}
public class FooClass { public int Execute() { return 1;}}
设置并执行:
using (Microsoft.CSharp.CSharpCodeProvider foo =
new Microsoft.CSharp.CSharpCodeProvider())
{
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true
},
"public class FooClass { public int Execute() { return 1;}}"
);
var real = new FooClass();
Test(() => real.Execute()); // benchmark, direct call
var type = res.CompiledAssembly.GetType("FooClass");
var obj = Activator.CreateInstance(type);
var method = type.GetMethod("Execute");
var input = new object[] { };
Test(() => (int)method.Invoke(obj, input)); // reflection invoke
dynamic dyn = Activator.CreateInstance(type);
Test(() => dyn.Execute()); // dynamic object invoke
var action = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), null, method);
Test(() => action()); // delegate
}
结果是:
8 // direct
771 // reflection invoke
41 // dynamic object invoke
7 // delegate
那么在那些你不能使用代表的情况下(如果你不够了?),你可以试试dynamic
。