4

次のことを行うコードがあります。

for each file (already read in the RAM):
    call a function and obtain a result
add the results up and disply

各ファイルは並行して分析できます。各ファイルを解析する関数は次のとおりです。

# Complexity = 1000*19*19 units of work
def fun(args):
    (a, b, p) = args
    for itr in range(1000):
        for i in range(19):
            for j in range(19):
                # The following random number generated depends on
                # latest values in (i-1, j), (i+1, j), (i, j-1) & (i, j+1)
                # cells of latest a and b arrays
                u = np.random.rand();
                if (u < p):
                    a[i, j] += -1
                else:
                    b[i, j] += 1
    return a+b

multiprocessing並列処理を実現するためにパッケージを使用しています:

import numpy as np
import time
from multiprocessing import Pool, cpu_count

if __name__ == '__main__':
    t = time.time()
    pool = Pool(processes=cpu_count())
    args = [None]*100
    for i in range(100):
        a = np.random.randint(2, size=(19, 19))
        b = np.random.randint(2, size=(19, 19))
        p = np.random.rand()
        args[i] = (a, b, p)
    result = pool.map(fun, args)
    for i in range(2, 100):
        result[0] += result[i]
    print result[0]
    print time.time() - t

の各反復で使用および呼び出しを行う同等のMATLABコードを作成しました。parforfunparfor

tic
args = cell(100, 1);
r = cell(100, 1);
parfor i = 1:100
   a = randi(2, 19, 19);
   b = randi(2, 19, 19);
   p = rand();
   args{i}.a = a;
   args{i}.b = b;
   args{i}.p = p;
   r{i} = fun(args{i});
end

for i = 2:100
    r{1} = r{1} + r{i};
end
disp(r{1});
toc

の実装funは次のとおりです。

function [ ret ] = fun( args )
a = args.a;
b = args.b;
p = args.p;

for itr = 1:1000
    for i = 1:19
        for j = 1:19
            u = rand();
            if (u < p)
                a(i, j) = a(i, j) + -1;
            else
                b(i, j) = b(i, j) + 1;
            end
        end
    end
end
ret = a + b;
end

MATLABプログラムが約 33 ~ 34 秒かかるのに対し、デュアル コア プロセッサでは約 1.5 秒かかりますPython。これはなぜですか?

編集:多くの回答が、乱数生成をベクトル化する必要があることを示唆しています。実際には、生成される乱数は最新の a および b 2D 配列に依存します。プログラムをシンプルで読みやすくするために、簡単なrand()呼び出しを行いました。私のプログラムの実際では、(i, j) セルの特定の水平方向および垂直方向に隣接するセルを見て、常に乱数が生成されます。したがって、それをベクトル化することはできません。

4

4 に答える 4

2

fun非並列コンテキストで の両方の実装をベンチマークしましたか? はるかに高速な場合があります。特に、Python のネストされたループは、funMatlab でより高速なベクトル化されたソリューションになるか、Matlab の JIT によって最適化される可能性があるように見えます。

両方の実装をプロファイラーに貼り付けて、どこに時間を費やしているかを確認します。両方の実装を非並列に変換し、まずそれらをプロファイルして、並列化の複雑さを導入する前にパフォーマンスが同等であることを確認します。

最後にもう 1 つチェックします。Matlab の Parallel Computation Toolbox をワーカーのローカル プールでセットアップしていて、リモート マシンに接続したり、他のリソースを取得したりしていませんか? Matlab 側のワーカーは何人ですか?

于 2013-04-22T22:37:55.190 に答える
0

利用可能な情報を考えると、Cython を使用することでおそらくメリットが得られます。これにより、いくつかの並列処理も使用できるようになります。必要な乱数に応じて、たとえば GSL を使用して乱数を生成できます。

元の質問

を使用する必要はありません。簡単にベクトル化できるmultiprocessingため、大幅な速度向上 (50 倍以上) が得られます。fun

なぜ matlab が numpy ほど大きな打撃を受けないのかはわかりませんが、その JIT がそれを救っているのかもしれません。Python は.、内側のループで 2 つのルックアップを実行したり、そこで高価な関数を呼び出したりすることを好みません。

def fun_fast(args):
     a, b, p = args
     for i in xrange(19):
         for j in xrange(19):
             u = np.random.rand(1000)
             msk = u < p
             msk_sum = msk.sum()
             a[i, j] -= msk_sum
             b[i, j] += msk.size - msk_sum
     return a + b
于 2013-04-22T23:07:12.067 に答える
0

このバージョンの楽しみを試して、スピードアップが得られるかどうかを確認してください。

def fun(args):
    a, b, p = args
    n = 1000
    u = np.random.random((n, 19, 19))
    msk = u < p
    msk_sum = msk.sum(0)
    a -= msk_sum
    b += (n - msk_sum)

    return a + b

これは、numpy を使用してこの種の関数を実装するより効率的な方法です。

これらの種類の入れ子になったループは、matlab や python などのインタープリター言語で非常に高いオーバーヘッドを持つ可能性がありますが、JIT が matlab で少なくとも部分的に補償されていると思われるため、ベクトル化された実装とループの実装のパフォーマンスの違いは小さくなります。Cpython には現在、これらの種類のループ (私が知っている) に対する最適化はありませんが、少なくとも 1 つの Python 実装pypyには JIT があります。残念ながら、現在 pypy は限定的な numpy サポートしかありません。

アップデート:

反復アルゴリズムを使用しているようですが、少なくとも私の経験では、それらは numpy/cpython で最適化するのが最も困難です。cythonの使用を検討してください。ネストされたループを作成するには、このチュートリアルも役立つ場合があります。他の人は他の提案をするかもしれませんが、それは私が考えることができる最高のものです.

于 2013-04-22T23:39:11.910 に答える