我打算创建一个尽可能快地执行大量手动指定计算的 Web 服务,并且一直在探索 DLR 的使用。
对不起,如果这很长,但请随意浏览并了解一般要点。
我一直在使用 IronPython 库,因为它使计算非常容易指定。我的工作笔记本电脑在执行以下操作时提供了每秒约 400,000 次计算的性能:
ScriptEngine py = Python.CreateEngine();
ScriptScope pys = py.CreateScope();
ScriptSource src = py.CreateScriptSourceFromString(@"
def result():
res = [None]*1000000
for i in range(0, 1000000):
res[i] = b.GetValue() + 1
return res
result()
");
CompiledCode compiled = src.Compile();
pys.SetVariable("b", new DynamicValue());
long start = DateTime.Now.Ticks;
var res = compiled.Execute(pys);
long end = DateTime.Now.Ticks;
Console.WriteLine("...Finished. Sample data:");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(res[i]);
}
Console.WriteLine("Took " + (end - start) / 10000 + "ms to run 1000000 times.");
其中 DynamicValue 是一个从预构建数组(在运行时播种和构建)返回随机数的类。
当我创建一个 DLR 类来做同样的事情时,我得到了更高的性能(每秒约 10,000,000 次计算)。课程如下:
class DynamicCalc : IDynamicMetaObjectProvider
{
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
{
return new DynamicCalcMetaObject(parameter, this);
}
private class DynamicCalcMetaObject : DynamicMetaObject
{
internal DynamicCalcMetaObject(Expression parameter, DynamicCalc value) : base(parameter, BindingRestrictions.Empty, value) { }
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
Expression Add = Expression.Convert(Expression.Add(args[0].Expression, args[1].Expression), typeof(System.Object));
DynamicMetaObject methodInfo = new DynamicMetaObject(Expression.Block(Add), BindingRestrictions.GetTypeRestriction(Expression, LimitType));
return methodInfo;
}
}
}
并通过执行以下操作以相同的方式调用/测试:
dynamic obj = new DynamicCalc();
long t1 = DateTime.Now.Ticks;
for (int i = 0; i < 10000000; i++)
{
results[i] = obj.Add(ar1[i], ar2[i]);
}
long t2 = DateTime.Now.Ticks;
其中 ar1 和 ar2 是预先构建的、运行时播种的随机数数组。
这种方式速度很好,但是指定计算并不容易。我基本上会考虑创建自己的词法分析器和解析器,而 IronPython 已经拥有我需要的一切。
我原以为我可以从 IronPython 获得更好的性能,因为它是在 DLR 之上实现的,而且我可以做得比我得到的更好。
我的示例是否充分利用了 IronPython 引擎?是否有可能从中获得更好的性能?
(编辑)与第一个示例相同,但在 C# 中使用循环,设置变量并调用 python 函数:
ScriptSource src = py.CreateScriptSourceFromString(@"b + 1");
CompiledCode compiled = src.Compile();
double[] res = new double[1000000];
for(int i=0; i<1000000; i++)
{
pys.SetVariable("b", args1[i]);
res[i] = compiled.Execute(pys);
}
其中 pys 是来自 py 的 ScriptScope,而 args1 是预先构建的随机双精度数组。此示例的执行速度比在 Python 代码中运行循环并传入整个数组要慢。