2016-12-23 10 views
3

サブプロセスPythonモジュールを使用してC++プログラムに繰り返し計算を委託して、Pythonプログラムをスピードアップしようとしています。サブプロセスを使用したPythonとC++プログラム間の通信が非常に遅い

私の問題を説明するために、入力の倍数を返す単純なC++コードを使用しました。数百万の整数に対しては16秒かかるが、これは非常に遅い。ここで

C++プログラム(double.exe)です:

#include <iostream> 

using namespace std; 

int main() 
{ 
    int a; 
    bool goon = true; 
    while (goon) 
    { 
     cin >> a; 
     cout << 2 * a << endl; 
     if (a == 0) 
      goon= false; 
    } 
} 

そしてここではPython 3のコード:通信する

from time import time 
from subprocess import PIPE,Popen 

cmd = ["double"] 
process = Popen(cmd, stdin=PIPE,stdout=PIPE, bufsize=32,universal_newlines=True, shell=True) 

t0 = time() 
for i in range(1,int(1e6)): 
    print(i, file=process.stdin, flush=True) 
    output = int(process.stdout.readline()) 
dt = time() - t0 
print("Time to communicate : %fs" % dt) 
print(0,file=process.stdin,flush=True) # close 'double' program 

時間:16.029137s

私にとって、遅い理由は、PythonプロセスとC++ progra私はそれを加速する方法を見つけていません。サブプロセスや他のライブラリを使用して、この通信を高速化するソリューションはありますか?

私はWindowsでPython 3.5.2を使用しています。

+2

C++コードをPythonにboost :: pythonのようなモジュールとして公開してみませんか? –

+2

a)ちょうどプロセスの開始と終了にかなりの時間がかかります。b)あなたの計算が実際にはC++でかなり高速かどうかはわかりません。あなたはそれをプロファイリングしましたか? – user45891

+1

モジュールアプローチに加えて、[scipy.weave](https://docs.scipy.org/doc/scipy/reference/weave.html)や[cffi](http:// cffi。 readthedocs.io/en/latest/index.html)(あなたのユースケースに適しているかもしれません)。 – UnholySheep

答えて

1

ちょうど推測ですが、std::endlは新しい行文字を書き込むだけでなく、出力ストリームをフラッシュするという事実のためかもしれません。フラッシュミネは、最も時間がかかる部分です。したがって、あなただけの

std::cout << 2 * a << "\n"; //Unix style line break 

または

std::cout << 2 * a << "\r\n"; //Windows style line break 

書く場合、それは速いかもしれません(注:テストされていないこの作品または暗黙のフラッシュが実際にそこにあることが要求されているかどうかを)

+0

私はそれをテストしましたが、std :: endlと比較して通信を高速化することはできません。 '\ r \ n'を指定すると、 "* doublea * \ n"と "\ n"という2つの出力行が出力されます。 – Aral

3

問題があります通信そのものではなく、むしろ大規模なコンテキストの切り替え。あなたはC++コードでは非常に小さな "タスク"を行いますが、そのような "タスク"ごとに、Pythonコードはパイプにデータを書き込み、フラッシュし、スリープ状態になり、C++パートが起動し、入力を解析し、結果を計算し、出力し、フラッシュして眠りにつく。その後、Pythonコードが起動します。

スリープ状態になり、目を覚ます(関連するコンテキスト切り替え)ことは無料ではありません。 "タスク"(入力に2を掛ける)のサイズでは、このオーバーヘッドはほとんどの時間を消費します。

バッチでC++プログラムに作業を提供するか、より大きなタスクを持つことによって、その問題を「修正」できます。または両方。

たとえば、数値が100万であるが、10個の数値のバッチを使用して同じジョブは、パイプが各書き込み後にフラッシュされると、ボックスで2倍速く実行されます。コード:

for i in range(1,int(1e5)): 
    for j in range(1, 10): 
     print(i*10 + j, file=process.stdin, flush=True) 
    for j in range(1, 10): 
     output = int(process.stdout.readline()) 

フラッシュが一度だけ10の数値ごとに行われている場合、それは前の例よりも1.5倍高速(または元のコードよりも3倍速く)実行:

for i in range(1,int(1e5)): 
    for j in range(1, 10): 
     print(i*10 + j, file=process.stdin) 
    process.stdin.flush() 
    for j in range(1, 10): 
     output = int(process.stdout.readline()) 

"の場合タスク "が大きければ、コンテキストスイッチの料金は同じです。しかし、それはタスクのサイズに比べて大きくはありません。たとえば、コンテキストスイッチに0.1秒かかっているとしましょう(現実にはこれよりも小さくなりますが、これは単なる例です)。タスクが例えば1msで行われる乗算である場合(再び、単なる例として)、タスクと比較したコンテキストスイッチオーバーヘッドは10000%である。しかし、タスクが重く、1秒かかるとオーバーヘッドはわずか10%です。相対値の1000倍の差。

+0

私は単純なタスクの例を2倍にして問題を説明しましたが、私がスピードアップしたい仕事は実際にはbです残念ながら、残念ながら、タスクのPython実装と比較して、サブプロセスで呼び出されるC++コードでリアルタイムゲインを得るには十分ではありません。私のPC上では、あなたの両方のコードは5秒で実行されます。これは私の3倍です。それにもかかわらず、実際の問題では、次の計算を計算する前に答えが必要なので、そのような実装はできません。 – Aral

+0

@Aralこの場合、おそらく*あなたのPythonコード用のC/C++拡張モジュールを実装したいと思うでしょう - あなたがしなければならない計算が本当に重く、それらをPythonで実装することは非常に遅くなるだろうと仮定します。 https://docs.python.org/3/extending/extending.html – dvk

+0

@aralを参照してください。きめ細かいタスクでIPCを使用することは、言語に関係なくパフォーマンスの向上には必ずしも悪い考えであり、ipcのstdinは最も遅く、最悪の方法が可能です。拡張機能を使用していても、処理中に、(自動)データ型の変換、スタック変換などに悩まされます。メモリマップされたファイルが役立つかもしれませんが、パイソンアルゴリズムのプロファイリングと最適化の方法をすでに使い果たしているかどうかを尋ねてください。 – Abel

関連する問題