我有一段代码可以执行以下操作:
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
的等效parfor
代码,并在fun
的每次迭代中调用parfor
:
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
速度非常快,双核处理器大约需要1.5秒,而Python
程序大约需要33-34秒。为什么会这样?
编辑:很多答案都表明我应该对随机数生成进行矢量化。实际上它的工作方式是,生成的随机数取决于最新的a和b 2D阵列。我只是简单地调用rand()
来保持程序简单易读。在我的程序的实际情况中,总是通过查看(i,j)单元的某些水平和垂直相邻单元来生成随机数。因此无法对其进行矢量化。
答案 0 :(得分:2)
您是否在非并行上下文中对fun
的两种实现进行了基准测试?一个人可能会快得多。特别是,Python fun
中的那些嵌套循环看起来可能会在Matlab中转换为更快的矢量化解决方案,或者可能会被Matlab的JIT优化。
将两种实施方法都放在剖析器中,以了解他们在哪里花费时间。将两个实现转换为非并行并首先对它们进行分析,以确保它们在引入并行化内容的复杂性之前在性能上具有相同性。
最后一次检查 - 您是否正在使用本地工作池设置Matlab的并行计算工具箱,对,而不是挂钩到远程计算机或获取其他一些资源? Matlab方面有多少工人?
答案 1 :(得分:1)
我对你的Python代码进行了一些测试,虽然没有multiprocessing
部分,并通过进行以下更改实现了大约25倍的加速:
ndarray.tolist()
所需的时间,所以这实际上可能是一个可行的选择,只要我认为阵列不是很大。rand = np.random.rand
和更晚u = rand()
,因为在Python中,本地命名空间中的查找更快,这在紧密循环中很重要,例如random.random
而不是np.random.rand
(也绑定到函数的本地名称)。range
。xrange
(此列表从最重要的加速到非常小的增益排序)
当然还有并行计算方面。使用multiprocessing
所有在进程之间传递的Python对象都是“pickle”的,这意味着它们必须在进程之间复制时进行序列化和反序列化。 MATLAB还可以在进程(或线程?)之间复制数据,但可能会以较少浪费的方式进行复制。接下来,设置multiprocessing.Pool
也需要一段时间,这对您的MATLAB基准测试可能不公平,但我不确定。
基于你的时间和我的时间,我会说Python和MATLAB对于这个特定的任务可能同样快。但是,不幸的是,你必须跳过一些箍来获得Python的速度。如果你有可能的话,使用Numba的@autojit
能力可能会很有趣。
答案 2 :(得分:0)
鉴于可用的信息,你可能会从使用Cython中受益,让你也可以使用一些parallelism。根据您需要的随机数,您可以使用GSL生成它们。
无需使用multiprocessing
,因为fun
可以很容易地进行矢量化,从而产生大幅加速(超过50倍)。
我不确定为什么matlab不会像numpy那样难受,但它的JIT可能会保存它。 Python不喜欢在内部循环中进行两次.
查找,也不喜欢在那里调用昂贵的函数。
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
答案 3 :(得分: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目前没有对这些类型的循环进行任何优化(我知道)但是至少有一个python实现pypy确实有一个JIT。不幸的是,pypy目前只有有限的numpy支持。
更新
看起来你有一个迭代算法,至少在我的经验中,那些是最难用numpy / cpython进行优化的。考虑使用cython,这个tutorial也可能有用,可以编写嵌套循环。其他人可能有其他建议,但这是我能想到的最好的建议。