Запустите динамически скомпилированный код C# на собственной скорости... как?
Я прочитал несколько сообщений о so о написании и компиляции динамического кода C#. Например, этот пост. Я понимаю, это можно сделать несколькими способами.
, назвав призывателя код медленно. Я сделал простой тест, и это примерно на 500 X медленнее, чем вызов собственного метода.то, что я хочу иметь возможность сделать, эквивалентно загрузке DLL и вызову один из его методов напрямую ("изначально"), что даст преимущества скорости I хотеть.
каков самый простой способ сделать это? Скомпилировать динамический код в 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());
}
3 ответов
да, если вы вызываете через MethodInfo
или неспецифичный Delegate
, тогда это действительно будет медленно. Фокус в том, чтобы:не надо. Различные подходы:
-
для отдельных методов перейдите через basic но набрал делегат, такие как
Action
, или как общий catch-all,Func<object[], object>
иDelegate.CreateDelegate
создать набрал делегат:Action doSomething = (Action)Delegate.CreateDelegate(typeof(Action), method);
другой вариант этого-использовать
Expression
API (который имеет.Compile()
способ), илиDynamicMethod
(имеющегоCreateDelegate()
). Ключевая вещь: вы должны получить набрал делегировать и вызывать с помощью набрал invoke (не.DynamicInvoke
). -
для более сложных случаев, когда вы генерируете целые типы, рассмотрите возможность реализации интерфейса, о котором вы знаете, т. е.
IFoo foo = (IFoo)Activator.CreateInstance(...);
снова; после первого бросания (что очень дешево), вы можете просто использовать статический код:
foo.Bar();
Do не использовать someDelegate.DynamicInvoke(...)
или someMethod.Invoke(...)
если вы после какой-либо производительности.
помимо советов Марка, вы можете улучшить скорость, указав опцию компилятора" оптимизировать":
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true,
CompilerOptions = "/optimize"
},
подумал, что стоит показать, как выглядят все потенциальные варианты и их эксплуатационные характеристики. Учитывая следующие вспомогательные классы и функции:
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
.