IPC是否放慢了速度?

时间:2016-06-06 21:09:19

标签: python performance multiprocessing

我知道使用多处理模块时会有开销,但这似乎是一个很大的数额,IPC的水平应该是我可以收集的相当低。

假设我在1-1000之间生成一个大的随机数列表,并且想要获得仅有素数的列表。此代码仅用于测试CPU密集型任务的多处理。忽略素性测试的整体效率低下。

大部分代码可能如下所示:

from random import SystemRandom
from math import sqrt
from timeit import default_timer as time
from multiprocessing import Pool, Process, Manager, cpu_count

rdev = SystemRandom()
NUM_CNT = 0x5000
nums = [rdev.randint(0, 1000) for _ in range(NUM_CNT)]
primes = []


def chunk(l, n):
    i = int(len(l)/float(n))
    for j in range(0, n-1):
        yield l[j*i:j*i+i]
    yield l[n*i-i:]


def is_prime(n):
    if n <= 2: return True
    if not n % 2: return False
    for i in range(3, int(sqrt(n)) + 1, 2):
        if n % i == 0:
            return False
    return True

在我看来,我应该能够在多个进程中分割它。我有8个逻辑核心,所以我应该能够使用cpu_count()作为进程数。

串行:

def serial():
    global primes
    primes = []
    for num in nums:
        if is_prime(num):
            primes.append(num)  # primes now contain all the values

以下尺寸的NUM_CNT对应于速度:

  • 0x500 = 0.00100秒。
  • 0x5000 = 0.01723秒。
  • 0x50000 = 0.27573秒。
  • 0x500000 = 4.31746秒。

这是我选择进行多处理的方式。它使用chunk()函数将nums拆分为cpu_count()(大致相等)部分。它将每个块传递给一个新进程,该进程遍历它们,然后将其分配给共享dict变量的条目。当我将值分配给共享变量时,应该真正发生IPC。为什么会发生呢?

def loop(ret, id, numbers):
    l_primes = []
    for num in numbers:
        if is_prime(num):
            l_primes.append(num)
    ret[id] = l_primes


def parallel():
    man = Manager()
    ret = man.dict()
    num_procs = cpu_count()
    procs = []
    for i, l in enumerate(chunk(nums, num_procs)):
        p = Process(target=loop, args=(ret, i, l))
        p.daemon = True
        p.start()
        procs.append(p)
    [proc.join() for proc in procs]
    return sum(ret.values(), [])

同样,我预计会有一些开销,但时间似乎比串行版本快得多。

  • 0x500 = 0.37199秒。
  • 0x5000 = 0.91906秒。
  • 0x50000 = 8.38845秒。
  • 0x500000 = 119.37617秒。

是什么导致它这样做?是IPC吗?初始设置让我期待一些开销,但这只是一个疯狂的数量。

修改

以下是我如何计算函数的执行时间:

if __name__ == '__main__':
    print(hex(NUM_CNT))
    for func in (serial, parallel):
        t1 = time()
        vals = func()
        t2 = time()
        if vals is None:  # serial has no return value
            print(len(primes))
        else:  # but parallel does
            print(len(vals))
        print("Took {:.05f} sec.".format(t2 - t1))

每次都使用相同的数字列表。

示例输出:

0x5000
3442
Took 0.01828 sec.
3442
Took 0.93016 sec.

1 个答案:

答案 0 :(得分:0)

嗯。你如何衡量时间?在我的计算机上,并行版本比串行版本快得多。

我使用time.time()进行测试:如果我们假设tttime.time()的别名。

serial()
t2 = int(round(tt() * 1000))
print(t2 - t1)
parallel()
t3 = int(round(tt() * 1000))
print(t3-t2)

我得到了0x500000作为输入:

  • 5519ms for serial version
  • 3351ms for parallel version

我相信你的错误是由并行中包含数字生成过程引起的,而不是在序列中包含

在我的计算机上,随机数的生成需要45秒(这是一个非常缓慢的过程)。因此,它可以解释您的两个值之间的差异,因为我不认为我的计算机使用了非常不同的架构。