手動で指定した多数の計算を可能な限り高速に実行するWebサービスを作成するつもりであり、DLRの使用を検討してきました。
これが長い場合は申し訳ありませんが、お気軽にざっと目を通し、一般的な要点を理解してください。
計算を非常に簡単に指定できるため、IronPythonライブラリを使用しています。私の作品のラップトップは、次のことを行うと、毎秒約40万回の計算のパフォーマンスを提供します。
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クラスを作成すると、はるかに高いパフォーマンスが得られます(1秒あたり約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は事前に作成されたランダムなdoubleの配列です。この例は、Pythonコードでループを実行し、配列全体を渡すよりも実行速度が遅くなります。