18

地理空間データを取得していくつかのステップを実行するアルゴリズムを作成しました。入力データは、大規模なラスター調査エリア (~1 億 5000 万ピクセル) のポリゴンと共変量ラスターのシェープファイルです。手順は次のとおりです。

  1. シェープファイルのポリゴン内からのサンプル ポイント
  2. サンプリング ポイントごとに、共変量ラスターから値を抽出します。
  3. サンプリング ポイントで予測モデルを構築する
  4. ターゲット グリッド ポイントの共変量を抽出する
  5. 予測モデルをターゲット グリッドに適用する
  6. 一連の出力グリッドに予測を書き込む

プロセス全体を数回 (たとえば 100 回) 反復する必要がありますが、現在、連続して処理すると、各反復に 1 時間以上かかります。各反復で最も時間がかかるのはステップ 4 と 5 です。ターゲット グリッドが非常に大きいため、一度に 1 ブロック (たとえば 1000 行) ずつ処理しています。

私は 32 Gb の RAM を備えた 6 コアの CPU を使用しているため、各反復内でオブジェクトを含む Python のmultiprocessingモジュールを使用しPoolて、多数のブロックを同時に処理し (ステップ 4 と 5)、出力 (予測) を書き込んでみました。グローバル出力書き込み関数を呼び出すコールバック関数を使用して、出力グリッドの共通セットに。これは機能しているように見えますが、各ブロックを連続して処理するよりも高速ではありません (実際には、おそらく低速です)。

だから私の質問は、それを行うためのより効率的な方法はありますか? multiprocessing モジュールのQueueクラスに興味がありますが、どのように機能するのかよくわかりません。たとえば、ステップ 4 と 5 を実行するキューを作成してから、ステップ 6 を実行する別のキューに結果を渡す方が効率的かどうか疑問に思っています。

任意のポインタをいただければ幸いです。

4

4 に答える 4

3

Pythonのマルチプロセッシング機能の現在の状態は、CPUバウンド処理には適していません。multiprocessingモジュールを使用してモジュールをより高速に実行する方法はなく、それを使用することが問題ではないことをお伝えすることを恐れていますmultiprocessing

本当の問題は、Pythonが依然としてGlobalInterpreterLock(GIL)のルールに拘束されていることです(スライドを強くお勧めします)。GILの回避に関して、いくつかの刺激的な理論的および実験的進歩がありました。Python 3.2イベントには、いくつかの問題を解決するが、他の問題を紹介する新しいGILが含まれています。

今のところ、1つのプロセス内で多くのスレッドを実行しようとするよりも、単一のシリアルスレッドで多くのPythonプロセスを実行する方が高速です。これにより、スレッド間でGILを取得する問題を回避できます(効果的にGILを増やすことにより)。ただし、これは、Pythonプロセス間のIPCオーバーヘッドが処理の利点を上回らない場合にのみ有益です。

Eli Benderskyは、マルチプロセッシングを使用してCPUバウンドプロセスをより高速に実行しようとした経験について、まともな概要記事を書きました。

PEP 371multiprocessingには、モジュール(以前は非標準のパッケージ化された名前)の導入によりGILを「回避」したいという要望があったことは注目に値しますpyProcessing。ただし、GILは、Pythonインタープリターで依然として大きな役割を果たしているため、CPUバウンドアルゴリズムで適切に機能することはできません。多くの異なる人々がGILの削除/書き換えに取り組んできましたが、Pythonリリースにするのに十分な牽引力はありませんでした。

于 2012-06-20T14:24:49.880 に答える
1

python.org のマルチプロセッシングの例のいくつかは、IMO ではあまり明確ではなく、欠陥のある設計から始めるのは簡単です。プロジェクトを開始するために作成した単純な例を次に示します。

import os, time, random, multiprocessing
def busyfunc(runseconds):
    starttime = int(time.clock())
    while 1:
        for randcount in range(0,100):
            testnum = random.randint(1, 10000000)
            newnum = testnum / 3.256
        newtime = int(time.clock())
        if newtime - starttime > runseconds:
            return

def main(arg):
    print 'arg from init:', arg
    print "I am " + multiprocessing.current_process().name

    busyfunc(15)

if __name__ == '__main__':

    p = multiprocessing.Process(name = "One", target=main, args=('passed_arg1',))
    p.start()

    p = multiprocessing.Process(name = "Two", target=main, args=('passed_arg2',))
    p.start()

    p = multiprocessing.Process(name = "Three", target=main, args=('passed_arg3',))
    p.start()

    time.sleep(5)

これにより、3 つのプロセッサが 15 秒間使用されます。もっと簡単に変更できるはずです。おそらく、これは現在のコードをデバッグし、複数の独立したプロセスを実際に生成していることを確認するのに役立ちます.

RAM の制限によりデータを共有する必要がある場合は、http: //docs.python.org/library/multiprocessing.html#sharing-state-between-processesをお勧めします。

于 2012-06-15T22:31:13.670 に答える
1

Python は実際には集中的な数値処理を行うことを意図していないため、通常、Python プログラムのタイム クリティカルな部分を C/C++ に変換し始め、処理を大幅に高速化します。

また、Python のマルチスレッドはあまり良くありません。Python は、あらゆる種類のものにグローバル セマフォを使用し続けます。そのため、Python が提供するスレッドを使用しても、速度は向上しません。スレッドは、スレッドが通常 IO などを待機するアプリケーションに役立ちます。

C モジュールを作成する場合、データを処理するときにグローバル セマフォを手動で解放できます (もちろん、Python の値にはもうアクセスしないでください)。

C API を使用するには多少の練習が必要ですが、構造が明確であり、たとえば Java ネイティブ API よりもはるかに使いやすいです。

Python ドキュメントの「拡張と埋め込み」を参照してください。

このようにして、時間が重要な部分を C/C++ で作成し、遅い部分をより高速なプログラミングで Python で動作させることができます...

于 2012-06-20T08:32:33.730 に答える
0

コードのどの側面が最も時間がかかっているかを最初に確認することをお勧めします。そのため、それをプロファイリングする必要があります。 cython が必要です。

キューに関しては、データの共有/スレッドの同期に主に使用されますが、私はめったに使用しませんでした。私は常にマルチプロセッシングを使用しています。

私は主に map reduce の哲学に従います。これはシンプルでクリーンですが、 map 関数を適用するときに値を辞書にパックして各プロセスにコピーする必要があるため、大きなオーバーヘッドがあります ...

ファイルをセグメント化し、アルゴリズムをさまざまなセットに適用してみてください。

于 2012-06-11T05:52:53.850 に答える