multiprocessingでmap

Pythonのビルトイン関数であるmapは通常次のように使用されます.

>>> def f(x):
...     return x * x
... 
>>> map(f, range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

多くの場合mapに渡す関数は副作用が少なく,map処理は並列可能性が高いと考えられます.Python2.6以降で提供されているmultiprocessingモジュールを使用すれば,mapを容易に並列的に実行することができます.次のようなシンプルなプログラムで逐次処理と並列処理の速度を比較してみましょう.

#!/usr/bin/env python2.6
from multiprocessing import Pool
from time import time

def evaluate(n):
    if n%15 == 0:
        return 'Fizz Buzz'
    if n%3 == 0:
        return 'Fizz'
    if n%5 == 0:
        return 'Buzz'
    return n

limit = 1000000

#リスト内包
single_start = time()
single_result = [evaluate(i) for i in xrange(1,limit+1)]
single_end = time()

#multiprocessingのmap
multi_start = time()
num_processes = 2
pool = Pool(num_processes)
multi_result = pool.map(evaluate, xrange(1,limit+1))
multi_end = time()

print single_end-single_start, multi_end-multi_start

私の環境では前者が1.42秒,後者が0.70秒と理想的な結果が得られました.リスト内包ほどシンプルな記述とはいきませんが,これだけの簡潔さで並列処理を実現できるのは非常に魅力的です.なおかつマルチプロセスなのでGILの問題を回避して比較的容易にパフォーマンスを向上することができる点も素晴らしいと思います.os.forkの利用できないWindows環境でも使用できるのも特徴です.