web-dev-qa-db-ja.com

これらのネストされたforループをpython

多次元配列(result)があり、ネストされたループで埋める必要があります。関数fun()は、複雑で時間のかかる関数です。配列要素を並列に埋めたいので、システムのすべての処理能力を使用できます。コードは次のとおりです。

import numpy as np


def fun(x, y, z):
    # time-consuming computation...
    # ...

    return output


dim1 = 10
dim2 = 20
dim3 = 30

result = np.zeros([dim1, dim2, dim3])

for i in xrange(dim1):
    for j in xrange(dim2):
        for k in xrange(dim3):
            result[i, j, k] = fun(i, j, k)

私の質問は、「このコードを並列化できるかどうか?はいの場合、どのように?」です。

私はWindows1064ビットとpython 2.7を使用しています。

可能であれば、私のコードを変更してソリューションを提供してください。ありがとう!

9
amir masoud

完全並列実行を利用して、より一般的なソリューションが必要な場合は、次のようなものを使用してみませんか。

_>>> import multiprocess as mp
>>> p = mp.Pool()
>>> 
>>> # a time consuming function taking x,y,z,...
>>> def fun(*args):
...   import time
...   time.sleep(.1)
...   return sum(*args)
... 
>>> dim1, dim2, dim3 = 10, 20, 30
>>> import itertools
>>> input = ((i,j,k) for i,j,k in itertools.combinations_with_replacement(xrange(dim3), 3) if i < dim1 and j < dim2)
>>> results = p.map(fun, input)
>>> p.close()
>>> p.join()
>>>
>>> results[:2]
[0, 1]
>>> results[-2:]
[56, 57]
_

multiprocessの代わりにmultiprocessingを使用していることに注意してください。ただし、これはインタプリタで機能するためだけのものです。

私は_numpy.array_を使用しませんでしたが、必要に応じて... _p.map_からの出力を直接_numpy.array_にダンプしてから、shapeを変更することができます。属性をshape = (dim1, dim2, dim3)にするか、次のようにします。

_>>> input = ((i,j,k) for i,j,k in itertools.combinations_with_replacement(xrange(dim3), 3) if i < dim1 and j < dim2)
>>> import numpy as np
>>> results = np.empty(dim1*dim2*dim3)
>>> res = p.imap(fun, input)
>>> for i,r in enumerate(res):
...   results[i] = r
... 
>>> results.shape = (dim1,dim2,dim3)
_
3
Mike McKerns

これは、異なるkインデックスに対してfun(i, j, k)を並行して実行するコードのバージョンです。これは、 https://docs.python.org/2/library/multiprocessing.html を使用して、さまざまなプロセスでfunを実行することによって実行されます。

import numpy as np
from multiprocessing import Pool


def fun(x, y, z):
    # time-consuming computation...
    # ...

    return output


def fun_wrapper(indices):
    fun(*indices)

if __name__ == '__main__':
    dim1 = 10
    dim2 = 20
    dim3 = 30

    result = np.zeros([dim1, dim2, dim3])

    pool = Pool(processes=8)
    for i in xrange(dim1):
        for j in xrange(dim2):
            result[i, j] = pool.map(fun_wrapper, [(i, j, k) for k in xrange(dim3)])

これは最も洗練されたソリューションではありませんが、最初から始めることができます。また、funに時間のかかる計算が含まれている場合にのみ、速度が向上します。

1

簡単なアプローチは、配列をセクションに分割し、これらのセクション全体で動作するスレッドを作成することです。たとえば、(0,0,0)から(5,10,15)までの1つのセクションと、(5,10,16)から(10,20,30)までの別のセクション。

threading moduleを使用して、次のようなことを行うことができます

import numpy as np
import threading as t


def fun(x, y, z):
    # time-consuming computation...
    # ...

    return output


dim1 = 10
dim2 = 20
dim3 = 30

result = np.zeros([dim1, dim2, dim3])
#b - beginning index, e - end index
def work(ib,jb,kb,ie,je,ke):
    for i in xrange(ib,ie):
        for j in xrange(jb,je):
            for k in xrange(kb,ke):
                result[i, j, k] = fun(i, j, k)

 threads = list()
 threads.append(t.Thread(target=work, args(0,0,0,dim1/2,dim2/2,dim3/2))
 threads.append(t.Thread(target=work, args(dim1/2,dim2/2,dim3/2 +1,dim1, dim2, dim3))

 for thread in threads:
     thread.start()

いくつかのアルゴリズムを使用してこれらのセクションを定義し、スレッドの数を動的に決定できます。それがあなたを助けるか、少なくともあなたにいくつかのアイデアを与えることを願っています。

0
J. Maria