以原生速度运行动态编译的C#代码……怎么样?
我已经阅读了几篇关于编写和编译动态C#代码的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) Delegate.CreateDelegate(typeof (Func), 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 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()); }
是的,如果您通过MethodInfo
或非特定的Delegate
调用,那么它确实会很慢。 诀窍是: 不要那样做 。 各种方法:
-
对于单个方法,通过基本但类型化的委托(例如
Action
)或作为通用的catch-all,Func
– 并使用Delegate.CreateDelegate
创建类型化的委托:Action doSomething = (Action)Delegate.CreateDelegate(typeof(Action), method);
另一种变体是使用
Expression
API(具有.Compile()
方法)或DynamicMethod
(具有CreateDelegate()
)。 关键是:您必须获取一个类型化委托并使用类型化调用(而不是.DynamicInvoke
)进行调用。 -
对于生成整个类型的更复杂的情况,请考虑实现您知道的接口,即
IFoo foo = (IFoo)Activator.CreateInstance(...);
再次; 在初始演员(非常便宜)之后你可以使用静态代码:
foo.Bar();
如果您遇到任何类型的性能,请不要使用someDelegate.DynamicInvoke(...)
或someMethod.Invoke(...)
。
除了Marc的建议,你可以通过指定“optimize”编译器选项来提高速度:
var res = foo.CompileAssemblyFromSource( new System.CodeDom.Compiler.CompilerParameters() { GenerateInMemory = true, CompilerOptions = "/optimize" },
认为值得展示所有潜在选项的外观及其性能特征。 给出以下帮助程序类和函数:
public void Test(Func 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)Delegate.CreateDelegate(typeof(Func ), null, method); Test(() => action()); // delegate }
结果是:
8 // direct 771 // reflection invoke 41 // dynamic object invoke 7 // delegate
那么在那些你不能使用代表的情况下(如果你还不够了?),你可以试试dynamic
。
- 如何使用Paypal NVP API为第三方客户“即时”创建加密的PayNow按钮?
- c#将byte转换为string并写入txt文件
- UserControl中的INotifyPropertyChanged
- 如何提高反序列化速度?
- Lync:AVModality.VideoChannel的VideoWindows在成功调用BeginStart后为空(COMException HRESULT:0x80029C4A TYPE_E_CANTLOADLIBRARY)
- 使用Vector 运行比经典循环慢的SIMD矢量化C#代码
- String.Concat效率低下的代码?
- 仅允许文本框中的特定字符
- 我可以在C#中创建一个全局exception处理程序,让代码继续运行吗?