Python で並列処理(初めての人向け)

  • Pythonで並列計算してみたいけど,どうすればいいかわからない
  • 調べたらいろいろ出てくるけど,いまいちピンとこない
  • とりあえず簡単なプログラムで動作確認して,早く実装したい

そんな方のための投稿になればなーと思います.

いつものことですが,上の状態にあった自分が「そこそこ」使えるようになるまでのメモをまとめたものになります.早く実装できることを目標としているため,なるべく詳しい説明を省いて平易に説明することを心掛けたつもりです...







一番簡単な形:引数が一つで,その変数を並列させて処理

前置きが長いのは苦手なので,早速簡単な例を挙げてその簡単な説明をしてみます.Pythonで並列化を試みる際に一番最初に見た方がよいと思うのは次のページです.Pythonの並列化の記事をいくつか見てもよくわからなかったのですが,このページで最初の手がかりがつかめました.

pythonの並列計算(CPUの数だけ並列させる) – Blog of an immature researcher

ヤボですが,コメントだけ加えるかたちで引用させていただきたいと思います.皆様もとりあえず深く考えず次のコードを書いて実行させてみてください.

コードと実行結果

######## 並列計算を使えるように #########
from multiprocessing import Pool

##### 並列計算させる関数(処理):引数1つ ###
##### この場合は,引数の二乗を返す関数 ###
def nijou(x):
    print( x*x )

###### 並列計算させてみる #########
if __name__ == "__main__":
    p = Pool(4)
    p.map( nijou, range(10) )#nijouに0,1,..のそれぞれを与えて並列演算
0
1
4
9
16
25
36
49
64
81

簡単な解説

並列計算のライブラリ:multiprocessing

Pythonの標準のライブラリには,既に並列計算用のライブラリがあります.それが”multiprocessing”というものです.また,今回はその中の”Pool”という機能を使うので,コードの最初の行でそれを使えるようにインポートしています.次のリンクはmultiprocessingの公式ドキュメントです.

17.2. multiprocessing — プロセスベースの並列処理 – Python 公式ドキュメント

関数の定義

最初に習う例として複雑なのはよくないので,まずは引数が一つの例を扱います.nijou(x) という関数は(扱いやすさを重視して),xに入れた数を二乗にして返してくれる関数になっています.

main を明示的に書こう

multiprocessing の機能は,必ずメインの関数に”if __name__ == “__main__”:”を明記しておかなければなりません.うっかり忘れると永遠とエラーを出されるので気を付けましょう.試しに”if __name__ == “__main__”:”を取り除いてインデントを正しく治すと,次のような実行結果になります.

RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.

このmainを明示的に書くことについては,こちらの質問で学びました.

where to put freeze_support() in a Python script? – Stack Overflow

並列計算の準備:Pool

main関数の最初の行には p = Pool(4) と書いてありますが,この行で「4並列で計算する準備をします」といった感じになります.左側の p という文字は何でもよいです.それ以降はこのpが並列計算を表す文字になります.右にある Pool というものは並列計算をする場所をのようなものです.より正確には「hoge.Pool(4)」は「並列計算のプロセスを4つ準備して,4つのまとまりにhogeという名前を付けます」といった感じになります.

並列計算の実行:map

「p.map」で始まる行で並列計算の実行を行っています.「p.map( nijou, range(10) )」と書くことで「pという名前で用意したプロセスの集りに並列計算を実行させます.並列計算で実行するのはnijouという関数で,関数に渡す変数はrange(10),つまり0から9までの数字をnijouという関数に渡します」という意味になっています.

今の例ですと,関数の引数は一つですので,それぞれの並列化した関数(プロセス)に0,1,2,3…を渡しています.今の例ですと,nijouという関数が4つ作成されて,その4つの関数に0から9までの数を分け与えていることになっています.mapの引数の最初は並列計算させる関数.2つ目はその関数たちに渡す引数です.

引数が複数の関数の並列化

ラッパー

エミネムじゃないよ.

先ほどの並列化のやり方だと,関数に渡せる引数は並列化させる変数の1つしか考えられません.並列化させたい関数が複数の変数を持つような場合はひと工夫必要です.その発想として「複数の変数をラップに包んで,あたかも一つの変数のように見せかける」というのがあります.ということでこのようなラップの役割をしてくれる関数をラッパーといいます.ラップでいい気もするけどなぁ...

ラッパーを使って,複数の引数を持つ関数を並列化

ではその具体的方法を見てみましょう.

今回は並列化させる関数の例として「引数をaとbの2つをとり,その二つを掛け算した値を返す」という掛け算関数を扱いたいと思います.では具体例を見てみましょう.ちょっと長くなってしまいますがこれは並列化のためには仕方ないので,根気よく見ていきましょう.

####### 並列計算を使えるように ############
from multiprocessing import Pool

####### aとbを掛け算するだけの関数 #########
def kakezan(a, b):
    return a*b

####### ラッパー ######
def wrapper_kakezan(args):
    return kakezan(*args)

####### 並列処理実行 ########
if __name__ == "__main__":
    tutumimono = [[i, 3] for i in range(10)]
    p = Pool(processes=2)
    print( p.map(wrapper_kakezan, tutumimono) )
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

mainの文から読んでいきましょう.

まず tutumimono という名前で「リストのリスト」を作成しています.tutumimono の要素の一つとして tutumimono[0] は [0,3] というリストを表しています.このゼロと3という数字のペアを kakezan の引数として渡すことを考えています.このようにして引数となる複数の変数をまずはリストとしてまとまています.

そのまとまりをラッパーという名前を付けた wrapper_kakezen に渡します.この関数は,並列計算をするPoolが一つの引数しか取れないため, 単一変数を受け取るための緩衝材のようなものです.つまり,並列計算している実態はkakezanという関数ではなくこのwrapper_kakezanという単一引数を持つ関数です.そしてこのwrapper_kakezanは一つの引数として受け取った後に,すぐにkakezan関数に二つの変数として渡しています.例えば,tutumimono[4] は [4,3] ですが,これを受け取った並列させた関数の一つは4と3の掛け算をしてreturnしてくれます.

また,実行結果を見て理解していただけるかと思いますが map が返してくれるのは「計算結果をリストにしたもの」になります.

ついでに:自分のパソコンの並列計算能力の把握

最初に書くべきだったかもしれないのですが,並列計算を実際回す際には自分のコンピューターがどれだけの並列計算能力があるのかを把握しましょう.こちらは今回の投稿のメインの内容ではないので,簡単に説明している記事を紹介するにとどめたいと思います.

Windows の場合は次の記事を見ましょう.リソースモニターで現在の使用状況なども確認できます.

Windows標準「リソースモニター」でより高度にCPUやメモリを監視してみよう – パソコン実践講座 道すがら講堂

Linux の場合次の投稿を確認しましょう.

LinuxでCPUのコア数と物理プロセッサ数を確認する方法

このブログについて

IAtLeX です.ブログをはじめてさほど時間がたっていないので,未熟な内容が多々あるかと思いますが,それも時間が解決してくれるはず...Python系の記事を着々と充実させていきたいです.投稿主についてはこちらを参照してください.

このブログについて - http://iatlex.com/about_blog/

コメント

  1. Shigeaki より:

    とても分かりやすかったです。ありがとうございます。

  2. ftakamura より:

    参考になりました 良い記事ですね ありがとうございます

  3. yoddy より:

    大変助かりました。おかげで実装できました。

    1. IAtLeX より:

      お役に立ててよかったです。コメントありがとうございます。

IAtLeX へ返信する コメントをキャンセル

*