Mac上的Python Multiprocessing Pool.map()串行而非并行运行进程

时间:2019-05-22 17:29:22

标签: python macos multiprocessing

我一直在编写脚本来对大型数据集进行排序和过滤,并在多个CPU内核上拆分工作(通过使用多个进程),但是,Python似乎一次启动了每个进程,而不是并行运行它们

我已经剥离了代码,以至于它本质上没有任何用处(它会生成一个随机数列表,并将其全部删除),问题仍然存在。这是Mac上的Python问题吗?

我正在OS X 10.13.6上运行Python 3.7.1。

这是完整的代码:

import math
import multiprocessing
import os
import random
import sys
import timeit


def delete_all(passed_nums):

    print("Started process: {}, {}".format(multiprocessing.current_process(), os.getpid()))
    while (len(passed_nums) > 0):
        passed_nums.remove(passed_nums[0])
    print("Finished process: {}, {}".format(multiprocessing.current_process(), os.getpid()))

    return passed_nums


def chunksl(l, n):

    i = [l[i:i + n] for i in range(0, len(l), n)]
    return i


def main():

    rnd_nums = random.sample(range(1, 1000000), 500000)
    num_processes = 1
    Pool = multiprocessing.Pool(num_processes)

    list_chunk_size_per_core = int(math.ceil(len(rnd_nums)/float(num_processes)))

    unsorted_sub_lists = list(chunksl(rnd_nums, list_chunk_size_per_core))

    print("Number of CPUs:  {}".format(num_processes))
    print("Chunk size per CPU: {}".format(list_chunk_size_per_core))
    print("Number of chunks: {}".format(len(unsorted_sub_lists)))

    start_time = timeit.default_timer()
    sorted_sub_lists = Pool.map(delete_all, unsorted_sub_lists, list_chunk_size_per_core)
    end_time = timeit.default_timer()
    print('Duration: {}'.format(end_time - start_time))

    return True


if __name__ == '__main__':
    sys.exit(main())

这是num_processes = 1的输出:

Number of CPUs:  1
Chunk size per CPU: 500000
Number of chunks: 1
Started process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1617
Finished process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1617
Duration: 23.922029328999997

这是num_processes = 2的输出:

Number of CPUs:  2
Chunk size per CPU: 250000
Number of chunks: 2
Started process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1630
Finished process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1630
Started process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1630
Finished process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1630
Duration: 11.938197925

最后,这是num_processes = 1的输出,但是将列表的大小减少到250,000个条目,而不是500,000个:

Number of CPUs:  1
Chunk size per CPU: 250000
Number of chunks: 1
Started process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1639
Finished process: <ForkProcess(ForkPoolWorker-1, started daemon)>, 1639
Duration: 5.904828338

可以看出,当num_processes = 2脚本运行得更快时,不是因为它并行运行进程,而是因为删除两个250k项目列表中的所有条目比删除一个500k项目列表中的所有条目要快。 (num_processes = 2时的输出是num_processes = 1时上一次运行的持续时间的两倍,但列表大小减少到250k条目,这也大约是第一次运行时的四分之一)。 / p>

我的理解是,在启动一个新进程时,每个进程都使用Pool.map()接收其列表unsorted_sub_lists的完整副本,这意味着多个进程不会阻止尝试访问该进程。同时显示原始unsorted_sub_lists列表。 Python并没有通过引用新过程。我可以在脚本末尾打印列表unsorted_sub_lists,并且原始内容仍然存在,所以我认为我的理解是正确的吗?

1 个答案:

答案 0 :(得分:1)

对于n进程,变量unsorted_sub_lists具有n元素。因此,当您传递chunksize=list_chunk_size_per_core时,其中list_chunk_size_per_core为250k时,您正在将长度为2的列表分块为最大长度为250k的块,这实际上是在每个进程上重复工作。尝试将unsorted_sub_lists的长度固定为500k,或者只是在chunksize调用中删除Pool.map参数