问题似乎是多处理对它可以传递给 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
现在:
$ ./multitest.py 2 1E8
xrange(100000000)
xrange(100000000)
$ ./multitest.py 2 1E9
xrange(1000000000)
xrange(1000000000)
$ ./multitest.py 2 1E10
xrange(1410065408)
xrange(1410065408)
这是多处理的一个更普遍的问题的一部分:它依赖于标准的 Python 酸洗,并带有一些小的(并且没有很好的文档记录)扩展来传递值。每当出现问题时,首先要检查的是值是否以您期望的方式到达。
事实上,你可以通过玩 来看到这个问题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.'
即使不了解 pickle 协议的所有细节,很明显I1000000000
第一种情况下的 int 是 1E9,而下一种情况的等效块大约是 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
然后:
$ ./multitest.py 2 1E10
xrange(10000000000)
xrange(10000000000)
但是,您仍然会遇到更大的限制:
$ ./multitest.py 2 1E100
OverflowError: Python int too large to convert to C long
同样,这是同样的基本问题。解决此问题的一种方法是将 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。