python prime crunching:处理池比较慢?

时间:2011-08-26 19:31:29

标签: python multiprocessing pool

所以我过去几天一直在搞乱python的多处理库,我真的很喜欢处理池。它易于实现,我可以想象很多用途。我之前已经完成了几个我已经听过的项目以熟悉它,并且最近完成了一个蛮力游戏的程序。

Anywho,我正在执行一个执行时间计算,将100万到200万单线程和处理池之间的所有素数相加。现在,对于hangman cruncher来说,将游戏放入处理池可以将执行时间提高大约8倍(i7有8个核心),但是当研究出这些素数时,它实际上增加了处理时间几乎是因子4.

谁能告诉我为什么会这样?以下是有兴趣查看或测试它的人的代码:

#!/user/bin/python.exe
import math
from multiprocessing import Pool

global primes
primes = []

def log(result):
    global primes

    if result:
        primes.append(result[1])

def isPrime( n ):
    if n < 2:
        return False
    if n == 2:
        return True, n

    max = int(math.ceil(math.sqrt(n)))
    i = 2
    while i <= max:
        if n % i == 0:
            return False
        i += 1
    return True, n


def main():

   global primes

   #pool = Pool()

   for i in range(1000000, 2000000):
       #pool.apply_async(isPrime,(i,), callback = log)
       temp = isPrime(i)
       log(temp)

   #pool.close()
   #pool.join()

   print sum(primes)

   return

if __name__ == "__main__":
    main()

它当前将在单个线程中运行,以运行处理池,取消注释池语句并注释掉main for循环中的其他行。

1 个答案:

答案 0 :(得分:14)

使用multiprocessing的最有效方法是将工作划分为n个相等大小的块,其中n为池的大小,应该大约是系统中的核心数。这样做的原因是启动子进程并在它们之间进行通信的工作量非常大。如果工作量与工作块数相比较小,那么IPC的开销就会变得很大。

在您的情况下,您要求多处理单独处理每个素数。处理问题的一个更好的方法是为每个worker传递一系列值(可能只是一个起始值和结束值),并让它返回它找到的范围内的所有素数。

在识别大型素数的情况下,完成的工作随着起始值而增长,因此您可能不希望将总范围划分为n个块,而是将n * k相等的块,k一些合理的,较小的数字,比如10 - 100.这样,当一些工人在别人面前完成时,还有更多工作要做,并且可以在所有工人之间有效平衡。

编辑:这是一个改进的示例,用于显示该解决方案的外观。我已经尽可能少地改变了,所以你可以比较苹果和苹果。

#!/user/bin/python.exe
import math
from multiprocessing import Pool

global primes
primes = set()

def log(result):
    global primes

    if result:
        # since the result is a batch of primes, we have to use 
        # update instead of add (or for a list, extend instead of append)
        primes.update(result)

def isPrime( n ):
    if n < 2:
        return False
    if n == 2:
        return True, n

    max = int(math.ceil(math.sqrt(n)))
    i = 2
    while i <= max:
        if n % i == 0:
            return False
        i += 1
    return True, n

def isPrimeWorker(start, stop):
    """
    find a batch of primes
    """
    primes = set()
    for i in xrange(start, stop):
        if isPrime(i):
            primes.add(i)

    return primes



def main():

    global primes

    pool = Pool()

    # pick an arbitrary chunk size, this will give us 100 different 
    # chunks, but another value might be optimal
    step = 10000

    # use xrange instead of range, we don't actually need a list, just
    # the values in that range.
    for i in xrange(1000000, 2000000, step):
        # call the *worker* function with start and stop values.
        pool.apply_async(isPrimeWorker,(i, i+step,), callback = log)

    pool.close()
    pool.join()

    print sum(primes)

    return

if __name__ == "__main__":
    main()