Python Multiprocessing Pool.map () на Mac запускает процессы последовательно, а не параллельно - PullRequest
1 голос
/ 22 мая 2019

Я писал сценарий для сортировки и фильтрации большого набора данных и разделения работы по нескольким ядрам ЦП (используя несколько процессов), однако Python, похоже, запускает каждый процесс поочередно, выполняя их последовательно, а не параллельно .

Я удалил код обратно, чтобы он по сути ничего не делал полезного (он генерирует список случайных чисел и просто удаляет их все), и проблема сохраняется. Это проблема с Python на Mac?

Я использую Python 3.7.1 на OS X 10.13.6.

Это полный код:

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 скрипт выполняется быстрее, но не потому, что он выполняет параллельные процессы, а потому, что удаление всех записей в двух списках элементов по 250 КБ происходит быстрее, чем удаление всех записей в одном списке элементов по 500 КБ ( вывод, когда num_processes = 2 - удвоенная продолжительность последнего запуска, когда num_processes = 1, но размер списка уменьшен до 250 тыс. записей, что также составляет примерно одну четверть времени для первого запуска).

Насколько я понимаю, при запуске нового процесса, используя Pool.map(), каждый процесс получает полную копию своего фрагмента списка unsorted_sub_lists, что означает, что несколько процессов не блокируют попытки доступа к оригиналу unsorted_sub_lists список одновременно. Python не передается по ссылке на новый процесс. Я могу напечатать список unsorted_sub_lists в конце сценария, и исходное содержимое все еще там, поэтому я предполагаю, что мое понимание верно?

1 Ответ

1 голос
/ 22 мая 2019

В случае n процессов переменная unsorted_sub_lists имеет n элементов.Поэтому, когда вы передаете chunksize=list_chunk_size_per_core, где list_chunk_size_per_core равно 250 КБ, вы разбиваете список длины 2 на фрагменты максимальной длины 250 КБ, по сути дублируя работу над каждым процессом.Попробуйте либо исправить значение unsorted_sub_lists на длину 500 КБ, либо просто удалить параметр chunksize в вызове Pool.map

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...