Python 大迭代次数失败

标签 python parallel-processing iteration montecarlo pi

我写的很简单monte-carlo π calculation Python 程序,使用多处理模块。 它工作得很好,但是当我为每个 worker 传递 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

最佳答案

问题似乎是多处理对它可以传递给 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 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 协议(protocol)的所有细节,也应该很明显,第一种情况下的 I1000000000 是 1E9 作为 int,而下一种情况的等效 block 约为 1.41E9 ,而不是 1E10,作为一个整数。你可以试试

一个显而易见的解决方案是传递 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 double 类型的限制:1E23 通常是 99999999999999991611392,而不是 100000000000000000000000。

关于Python 大迭代次数失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12569977/

相关文章:

parallel-processing - 在集群上部署 optaplanner

c - 如何逃离一个循环的任务组?

python - Pep8 和多行 while 条件

multithreading - HDF5读取和fit_generator多处理错误

python - 倒带迭代

python - 在 Python 中查找大型文本文件中的字符串

c# - 重复遍历列表

python - 在 webapp2/GAE 中上传文件

python - 使用遗传算法演化多层感知器

python - 如何实现红噪声?