问题似乎是多处理对其可以传递给 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
Now:
$ ./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 pickling,以及一些次要的(且没有详细记录的)扩展来传递值。每当出现问题时,首先要检查的是这些值是否按照您的预期方式到达。
其实你玩一下就可以看出这个问题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
第一种情况是 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
Then:
$ ./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 double 类型的限制:1E23
通常为 99999999999999991611392,而不是 100000000000000000000000。