sas*_*hab 13 python iteration parallel-processing pi montecarlo
我使用多处理模块在Python中编写了简单的monte-carloπ计算程序.它工作正常,但是当我为每个工作者传递1E + 10次迭代时,会出现一些问题,结果是错误的.我无法理解问题是什么,因为1E + 9迭代的一切都很好!
import sys
from multiprocessing import Pool
from random import random
def calculate_pi(iters):
""" Worker function """
points = 0 # points inside circle
for i in iters:
x = random()
y = random()
if x ** 2 + y ** 2 <= 1:
points += 1
return points
if __name__ == "__main__":
if len(sys.argv) != 3:
print "Usage: python pi.py workers_number iterations_per_worker"
exit()
procs = int(sys.argv[1])
iters = float(sys.argv[2]) # 1E+8 is cool
p = Pool(processes=procs)
total = iters * procs
total_in = 0
for points in p.map(calculate_pi, [xrange(int(iters))] * procs):
total_in += points
print "Total: ", total, "In: ", total_in
print "Pi: ", 4.0 * total_in / total
Run Code Online (Sandbox Code Playgroud)
aba*_*ert 14
问题似乎是多处理有一个限制,它可以传递给xrange内的子进程的最大int.这是一个快速测试:
import sys
from multiprocessing import Pool
def doit(n):
print n
if __name__ == "__main__":
procs = int(sys.argv[1])
iters = int(float(sys.argv[2]))
p = Pool(processes=procs)
for points in p.map(doit, [xrange(int(iters))] * procs):
pass
Run Code Online (Sandbox Code Playgroud)
现在:
$ ./multitest.py 2 1E8
xrange(100000000)
xrange(100000000)
$ ./multitest.py 2 1E9
xrange(1000000000)
xrange(1000000000)
$ ./multitest.py 2 1E10
xrange(1410065408)
xrange(1410065408)
Run Code Online (Sandbox Code Playgroud)
这是多处理的一个更普遍的问题的一部分:它依赖于标准的Python pickle,有一些小的(并没有很好记录)扩展来传递值.每当出现问题时,首先要检查的是价值是按照预期的方式到达的.
事实上,你可以通过玩pickle,甚至没有触摸来看到这个问题multiprocessing(由于那些小的扩展,但通常情况并非总是如此):
>>> pickle.dumps(xrange(int(1E9)))
'c__builtin__\nxrange\np0\n(I0\nI1000000000\nI1\ntp1\nRp2\n.'
>>> pickle.dumps(xrange(int(1E10)))
'c__builtin__\nxrange\np0\n(I0\nI1410065408\nI1\ntp1\nRp2\n.'
Run Code Online (Sandbox Code Playgroud)
即使没有学习pickle协议的所有细节,显而易见的是,I1000000000在第一种情况下,1E9作为int,而下一种情况的等效块大约是1.41E9,而不是1E10,作为int.你可以试验一下
尝试的一个明显的解决方案是传递int(iters)而不是xrange(int(iters)),并从其参数calculate_pi创建xrange.(注意:在某些情况下,像这样的明显转换可能会损害性能,可能会很糟糕.但在这种情况下,如果有任何事情可能会稍微好一点 - 一个更简单的对象可以通过,并且你正在xrange构建并行化- 当然差别在于如此微小,它可能无关紧要.只要确保在盲目改造前思考.)
快速测试显示现在可以使用:
import sys
from multiprocessing import Pool
def doit(n):
print xrange(n)
if __name__ == "__main__":
procs = int(sys.argv[1])
iters = int(float(sys.argv[2]))
p = Pool(processes=procs)
for points in p.map(doit, [iters] * procs):
pass
Run Code Online (Sandbox Code Playgroud)
然后:
$ ./multitest.py 2 1E10
xrange(10000000000)
xrange(10000000000)
Run Code Online (Sandbox Code Playgroud)
但是,您仍将遇到更大的限制:
$ ./multitest.py 2 1E100
OverflowError: Python int too large to convert to C long
Run Code Online (Sandbox Code Playgroud)
同样,这是同样的基本问题.解决这个问题的一种方法是将arg作为字符串一直向下传递,并在子进程内执行int(float(a)).
作为旁注:我之所以做iters = int(float(sys.argv[2]))而不仅仅是iters = float(sys.argv[2])后来使用的原因int(iters)是为了避免以后意外地使用浮点iters值(因为OP的版本在计算中total也是如此total_in / total).
请记住,如果你得到足够大的数字,你会遇到C双重类型的限制:1E23通常是99999999999999991611392,而不是100000000000000000000000.
| 归档时间: |
|
| 查看次数: |
1182 次 |
| 最近记录: |