web-dev-qa-db-ja.com

OpenMPおよびPython

共有メモリマシン用のOpenMPのコーディング(CおよびFORTRANの両方で)の経験があり、行列の加算、乗算などの単純なタスクを実行します(LAPACKと競合する方法を参照してください)。私はOpenMPを十分に知っているので、ドキュメントを見なくても簡単なタスクを実行できます。

最近、私はプロジェクトのためにPythonに移行しましたが、絶対的な基本を超えたPythonの経験はありません。

私の質問は:

PythonでOpenMPを使用するeasiestの方法は何ですか?簡単に言うと、プログラマー側での労力が最も少ないものを意味します(システム時間の追加を犠牲にしても)。

OpenMPを使用する理由は、シリアルコードをいくつかの!$OMPsが散らばっています。 roughの並列化を達成するのに必要な時間は非常に短いです。この機能をPythonで複製する方法はありますか?

SOをブラウジングすると、次のことがわかります。

  • C拡張
  • StackLess Python

もっとありますか?どちらが私の質問に最適ですか?

52
user1132648

GILにより、CPythonでCPUを集中的に使用するタスクにスレッドを使用しても意味がありません。マルチプロセッシング( example )が必要か、計算中にGILを解放するC拡張を使用します(例:numpy関数の一部 example )。

Cythonで複数のスレッドを使用するC拡張機能、 example を簡単に作成できます。

26
jfs

シトン

CythonOpenMPをサポートしています:Cythonでは、prange(並列範囲)演算子を使用してOpenMPを追加できます-fopenmpコンパイラディレクティブをsetup.pyに追加します。

Prangeスタンザで作業する場合、with nogil:を使用してGILが存在するブロックを指定することにより、グローバルインタープリターロック(GIL)を無効にするため、実行は並行して実行されます無効。

_cython_np.pyx_をコンパイルするには、以下に示すようにsetup.pyスクリプトを変更する必要があります。コンパイル中に-fopenmpを引数として使用するようにCコンパイラに通知するように指示します-OpenMPを有効にし、OpenMPライブラリとリンクします。 setup.py

Cythonのprange,を使用すると、さまざまなスケジューリングアプローチを選択できます。 staticの場合、ワークロードは利用可能なCPUに均等に分散されます。ただし、一部の計算領域は時間がかかり、他の領域は安価であるため、CythonにCPU全体でstaticを使用して均等にワークチャンクをスケジュールするように依頼すると、一部の領域は他の領域よりも早く完了し、それらのスレッドはアイドル状態になります。 dynamicおよびguidedスケジュールオプションはどちらも、CPUが実行されるように実行時に小さなチャンクで動的に作業を割り当てることにより、この問題を軽減しようとします。ワークロードの計算時間が変動する場合、より均等に分散されます。したがって、コードの正しい選択は、ワークロードの性質によって異なります。

ヌンバ

NumbaのプレミアムバージョンであるNumbaProには、OpenMPを操作するためのprange並列化演算子の実験的なサポートがあります。

ピトラーン

Pythran(Pythonのサブセット用のPython-to-C++コンパイラー)は、Python 2.7のみを使用して実行します。並列セクションを指定します。 pragma ompディレクティブを使用する(上記のCythonのOpenMPサポートと非常に似ています)。例:

pragma omp

PyPy

JIT PythonコンパイラPyPyはマルチプロセッシングモジュール(以下を参照)をサポートし、PyPy-STMと呼ばれるプロジェクトを持っています " 複数の独立したCPUを実行できるPyPy同じプロセス内の空腹のスレッドを並行して "。

サイドノート:マルチプロセッシング

OpenMPは、複数のコアへの低レベルインターフェイスです。 multiprocessing.をご覧ください。multiprocessingモジュールは、より高いレベルで動作し、Pythonデータ構造を共有しますが、OpenMPはCプリミティブオブジェクト(整数など)で動作しますCにコンパイルしたら、OpenMPを使用するのは理にかなっています。コードをコンパイルしている場合、コンパイルしていない場合(たとえば、効率的なnumpyコードで、多くのコアで実行したい場合)、multiprocessingに固執するのがおそらく正しいアプローチです。

19
boardrider

私の知る限り、PythonのOpenMPパッケージはありません(ある場合はどうなるかわかりません)。スレッドを直接制御したい場合は、ただし、他の人が指摘しているように、GIL(Global Interpreter Lock)は、マルチスレッド化をPythonでパフォーマンスを少し向上させます... *。GILは、一度に1つのスレッドのみがインタープリターにアクセスできることを意味します。

代わりにNumPy/SciPyを見ることをお勧めします。 NumPyを使用すると、1回の操作で配列と行列を操作するMatlab風のコードを作成できます。並列処理機能もいくつかあります。 SciPy Wiki をご覧ください。

探し始める他の場所:

* OK、それは無意味ではありませんが、時間をPythonコード(popenなどを介して呼び出される外部プロセスによって)の外で消費しない限り、スレッドはありません便利以外のものは買わない。

13
Nathan

GILをリリースしてOpenMPを使用する場合は、Cythonをご覧ください。いくつかの一般的なタスクに対して単純な並列処理を提供します。 Cython documentation で詳細を読むことができます。

10
btel

たぶんあなたの応答はCythonにあります:

「Cythonはcython.parallelモジュールを通じてネイティブの並列化をサポートしています。この種の並列化を使用するには、GILをリリースする必要があります(GILのリリースを参照)。 Cythonドキュメント

8
Guilherme

pymp と呼ばれるパッケージがあります。これは、著者がOpenMPに似た機能をPythonにもたらすパッケージとして説明しています。私はそれを使ってみましたが、異なるユースケースで:ファイル処理。動いた。使い方はとても簡単だと思います。以下は、GitHubページから抜粋したサンプルです。

import pymp
ex_array = pymp.shared.array((100,), dtype='uint8')
with pymp.Parallel(4) as p:
    for index in p.range(0, 100):
        ex_array[index] = 1
        # The parallel print function takes care of asynchronous output.
        p.print('Yay! {} done!'.format(index))
3
DannyCju

http://archive.euroscipy.org/talk/6857 "NumPy配列上の並列ループに焦点を当てたCythonのOpenMP機能を紹介します。ソースコードの例は、PythonからOpenMPを使用する方法を示しています。OpenMPでの並列アルゴリズムの結果他の並列化戦略と比較して、さまざまなデータサイズでどのような高速化を達成できるかを示します。」

import numpy
import cython
from cython cimport parallel

@cython.boundscheck(False)
@cython.wraparound(False)
def func(object[double, ndim=2] buf1 not None,
        object[double, ndim=2] buf2 not None,
        object[double, ndim=2] output=None,
        int num_threads=2):
    cdef unsigned int x, y, inner, outer
    if buf1.shape != buf2.shape:
        raise TypeError('Arrays have different shapes: %s, %s' % (buf1.shape,
            buf2.shape))
    if output is None:
        output = numpy.empty_like(buf1)
    outer = buf1.shape[0]
    inner = buf1.shape[1]
    with nogil, cython.boundscheck(False), cython.wraparound(False):
        for x in parallel.prange(outer, schedule='static',
                num_threads=num_threads):
            for y in xrange(inner):
                output[x, y] = ((buf1[x, y] + buf2[x, y]) * 2 +
                    buf1[x, y] * buf2[x, y])
return output
0