Python大量迭代失败
我用Python编写了一个简单的程序,使用了多处理模块。 它工作得很好,但是当我为每个工作人员通过1E+10次迭代时,就会出现一些问题,结果是错误的。我不明白是什么问题,因为在1E+9迭代中一切都很好Python大量迭代失败,python,parallel-processing,iteration,montecarlo,pi,Python,Parallel Processing,Iteration,Montecarlo,Pi,我用Python编写了一个简单的程序,使用了多处理模块。 它工作得很好,但是当我为每个工作人员通过1E+10次迭代时,就会出现一些问题,结果是错误的。我不明白是什么问题,因为在1E+9迭代中一切都很好 import sys from multiprocessing import Pool from random import random def calculate_pi(iters): """ Worker function """ points = 0 # point
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
导入系统
来自多处理导入池
从随机导入随机
def计算π(iters):
“工作函数”
点=0#圆内点
对于iters中的i:
x=随机()
y=随机()
如果x**2+y**2的话,问题似乎是多处理对它可以传递给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
现在:
这是多处理的一个更一般的问题的一部分:它依赖于标准的Python pickle,使用一些较小的(并且没有很好的文档记录)扩展来传递值。无论何时出现问题,首先要检查的是值是否按预期的方式到达
事实上,您可以通过玩pickle
,甚至不用碰多处理
(由于这些较小的扩展,情况并不总是这样,但通常是这样):
即使不了解pickle协议的所有细节,第一种情况下的i100000000
显然是1E9作为int,而下一种情况下的等效块大约是1.41E9,而不是1E10作为int。您可以进行实验
一个显而易见的解决方案是传递int(iters)
而不是xrange(int(iters))
,并让计算π
从其参数创建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 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)
是为了避免以后意外使用floatiters
值(与OP的版本一样,在计算total
时,因此total\u in/total
)
请记住,如果你得到足够大的数字,你会遇到C双精度类型的限制:
1E23
通常是9999999999999991611392,而不是10000000000000000。你得到的结果是什么?@AmirRachumπ就像~0.4个迭代总数是正确的。不管过程是什么,都会发生这种情况吗代码>是吗?你在过程中使用什么类型的值?为什么iters是浮点而不是int?比如说,10000.0001次迭代意味着什么?@MattiLyra没有除以过程的数值。所以它不应该。我稍后会测试它。@abarnert for exponent.1E+NUM很有用。那么为什么100000000次迭代会给你PI估计值正确,但1410065408次迭代不正确?@MattiLyra:首先,如果你正在进行1410065408次迭代,但你认为你在进行100000000次迭代,那么你最终将导致除法错误。但我还没有测试或真正想清楚。在python 2.7.3中,我得到了>>pickle.dumps(xrange(int(1E10)))溢出错误:Python int太大,无法在pyhton 3.2.3中转换为C long
,我得到pickle.dumps(range(int(1E10)),protocol=0)b'C\uuuuuuu内置\uuuuuuuunxrange\np0\n(L0L\nl1000000000l\nL1L\ntp1\nRp2\n.
@XavierCombelle:这不仅仅是关于2.7.3和3.2.3;它还包括32位和64位、不同的平台和编译器等。如果您的范围内的所有数字恰好都适用于您的实现,您可以忽略这个问题(除非您以后可能需要更大的范围或更高的可移植性);否则,您必须处理它。这个问题将跟踪到python核心开发中
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