1

できるだけ早く多くの手動指定の計算を実行し、DLRの使用を検討しているWebサービスを作成しようとしています。DLR&パフォーマンス

申し訳ありませんが、これは長いですが、スキムオーバーして、一般的な要点を自由に感じる。

IronPythonライブラリは、計算を非常に簡単にするために使用しています。私の作品のラップトップは、第二次やっあたり40万の計算のパフォーマンスを提供します:DynamicValueは、事前に構築された配列(播種し、実行時に構築された)から乱数を返すクラスがある

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."); 

同じことをするための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; 
     } 
    } 
} 

と以下のようにして、同じ方法で試験/と呼ばれている:クラスは次のとおりですAR1とAR2は、事前に構築されている

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; 

、乱数のランタイム播種配列を。

速度はこのように素晴らしいですが、計算を指定するのは簡単ではありません。私は基本的に自分自身のレクサー&パーサーを作成していますが、IronPythonにはすでに必要なものがすべてあります。

IronPythonはDLRの上に実装されているため、パフォーマンスが向上すると思っていました。

私はIronPythonエンジンを最大限に活用していますか?それよりもはるかに優れたパフォーマンスを得ることは可能ですか?

実施例1とが、C#でのループと同じ(編集)、変数を設定し、Python関数を呼び出す:PYSをPYからScriptScopeであり、そしてargs1が事前に構築されたアレイである

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); 
} 

ランダムな二倍。この例では、Pythonコードでループを実行し、配列全体を渡すよりも遅く実行します。

+2

私はIronPythonについて、あるいは私の提案のパフォーマンスへの影響についてよく分かりませんが、Pythonをよく知っている人はほとんど誰でも、このような関数を書くでしょう: 'return [b.GetValue()+ 1 for _ in xrange(1000000)] ' – delnan

+0

私はPythonをよく知らないので、チップのおかげで! –

答えて

2

delnanさんのコメントは、ここでのいくつかの問題につながります。しかし、私は違いが何であるかについて具体的に説明します。 C#版では、Python版ではかなりの量の動的呼び出しを取り除いています。まず、ループはintにタイプされ、ar1とar2は強く型付けされた配列のように聞こえます。したがって、C#バージョンでは、obj.Add(C#では1回の操作)を呼び出すだけであり、オブジェクトに入力されていないと思われる場合は結果に割り当てることが可能です。また、このコードはすべてロックフリーです。

Pythonバージョンでは、最初にリストの割り当てがあります。これは、C#のように見えないタイマーの間にも表示されます。そして、あなたは範囲への動的な呼び出しを持っています、幸運にも一度だけ起こります。しかし、それは再び記憶に巨大なリストを作成します - デルレンジの提案はxrangeの改善です。次に、ループを通過するたびにオブジェクトに囲まれたループカウンタiがあります。次に、b.GetValue()を呼び出すと、実際には2つの動的invocatiosnが呼び出されます。まず、GetValueメソッドを取得するgetメンバ、次にそのバインドされたメソッドオブジェクトの呼び出しを行います。これは、ループの繰り返しごとに1つの新しいオブジェクトを作成しています。それからbの結果が出ます。GetValue()は、すべての反復で囲まれた別の値かもしれません。その後、その結果に1を加えて、繰り返しごとに別のボクシング操作を行います。最後に、これをあなたのリストに格納します。これは、もう一つの動的な操作です。リストの整合性を保つために、この最終操作をロックする必要があります(リストの理解を使用するというdelnanの提案もこれを改善します)。

      C#  IronPython 
Dynamic Operations   1   4 
Allocations     1   4 
Locks Acquired    0   1 

だから、基本的にPythonの動的挙動は、C#の対費用で来るん:私たちが持っているループ中に要約中のSO

。あなたは両方の世界のベストをしたい場合は、C#とPythonで何をするかのバランスを試してバランスをとることができます。たとえば、C#でループを記述し、Python関数であるデリゲートを呼び出すことができます(スコープからデリゲートとして関数を取得するにはscope.GetVariableを使用できます)。また、実際のパフォーマンスを最終的に取得する必要がある場合は、結果セットに.NET配列を割り当てることを検討することもできます。これは、ボックス化された値を保持しないで作業セットとGCのコピーを減らす可能性があるからです。今、あなたが行うことができます

CompiledCode compiled = src.Compile(); 
compiled.Execute(pys); 
var computer = pys.GetVariable<Func<object,object>>("computeValue"); 

:あなたがしたいC#コードで次に

def computeValue(value): 
    return value + 1 

:ユーザーの書き込みを持っている可能性がデリゲートを行うに

for (int i = 0; i < 10000000; i++) 
{ 
    results[i] = computer(i); 
} 
+0

私は既にC#でループを実行しようとしましたが、パフォーマンスが遅いことがわかりましたが、私のメソッドが有効ではなかった可能性があり、ScriptScopeオブジェクトのSetParameterを複数回呼び出すことによってオーバーヘッドが最大になっているようです。これを見直して、あなたの考えを教えてください。私はそれを私の元の質問に付け加えました。 –

+0

私は代理人を使わなかったことを述べておかなければならない。私はC#で少し新しく、パラメータを使ってCompiledCodeオブジェクトを呼び出すように設定する最良の方法が不明です。 –

+0

デリゲートはSetVariableコールよりもはるかに優れています。私は自分の答えを編集し、その明確な例を追加します。 –

0

計算速度が心配な方は、ローレベルの方が良いでしょうか?仕様書? PythonとC#は高水準言語であり、実装ランタイムは隠密作業に多くの時間を費やす可能性があります。このLLVMラッパー・ライブラリーに

ルック:http://www.llvmpy.org

  • 使用してそれをインストールします。pip install llvmpy ply
  • やDebian Linux上:apt install python-llvmpy python-ply

あなたはまだいくつかの小さなコンパイラを記述する必要がある(あなたが使用することができますPLY library)、それをLLVM JIT呼び出し(LLVM実行エンジンを参照)でバインドしますが、この方法はより効果的です(生成されたコードは実際のCPUコードに非常に近い)。mult iplatformは、.NET刑務所と比較しています。

LLVMは、多くのオプティマイザ・ステージ・モジュールや大規模なユーザーおよび開発者コミュニティを含む、インフラストラクチャの最適化コンパイラを使用する準備ができています。

もここを見て:http://gmarkall.github.io/tutorials/llvm-cauldron-2016

PS:それに興味があれば、私は並列に私のプロジェクトのマニュアルに貢献し、コンパイラのお手伝いをすることができます。しかし、それはジャンプスタートではありません、このテーマは私にも新しいです。

関連する問題