13

I wrote simple monte-carlo π calculation program in Python, using multiprocessing module. It works just fine, but when I pass 1E+10 iterations for each worker, some problem occur, and the result is wrong. I cant understand what is the problem, because everything is fine on 1E+9 iterations!

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
4

1 に答える 1

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

今:

$ ./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、最初のケースの は int としての 1E9 であり、次のケースの同等のチャンクは int としての 1E10 ではなく約 1.41E9 であることは明らかです。あなたは実験することができます

試してみるべき明らかな解決策の 1 つは、int(iters)の代わりにを渡し、その引数から を作成することです。(注: 場合によっては、このような明らかな変換によってパフォーマンスが大幅に低下する可能性があります。しかし、この場合は、オブジェクトを渡すのがより単純であり、構築を並列化しているため、どちらかというとわずかに優れている可能性があります。もちろん、違いは次のとおりです。小さいので問題ないかもしれませんが、やみくもに変身する前によく考えてください。)xrange(int(iters))calculate_pixrangexrange

簡単なテストで、これが機能することがわかりました。

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

繰り返しますが、それは同じ基本的な問題です。これを解決する 1 つの方法は、arg を文字列としてずっと下に渡し、サブプロセス内で int(float(a)) を実行することです。

補足として:私が単に後で使用するiters = int(float(sys.argv[2]))代わりに行っている理由は、後で float値を誤って使用しないようにするためです (OP のバージョンのように、コンピューティングで、したがって)。iters = float(sys.argv[2])int(iters)iterstotaltotal_in / total

そして、十分に大きな数になると、C の double 型の制限に遭遇することに注意してください1E23。通常は、10000000000000000000000 ではなく、99999999999999991611392 です。

于 2012-09-24T18:06:02.140 に答える