Я писал сценарий для сортировки и фильтрации большого набора данных и разделения работы по нескольким ядрам ЦП (используя несколько процессов), однако 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
в конце сценария, и исходное содержимое все еще там, поэтому я предполагаю, что мое понимание верно?