Многопроцессорность Python медленнее, чем одиночная

Sort.py

import random
import time

def time_analysis(func):
    def do_func(*args, **kwargs):
        print('[INFO] \'{}\' analysis started (N = {}).'.format(func.__name__, len(args[0])))
        start_time = time.clock()
        result = func(*args, **kwargs)
        end_time = time.clock()
        total_time = end_time - start_time
        print('[INFO] \'{}\' took {} seconds (N = {}).'.format(func.__name__, total_time, len(args[0])))
        return result

    return do_func


@time_analysis
def bubble_sort(num_list):
    num_len = len(num_list)
    for i in range(num_len - 1):
        for j in range(num_len - i - 1):
            if num_list[j] > num_list[j + 1]:
                num_list[j], num_list[j + 1] = num_list[j + 1], num_list[j]
    return num_list

if __name__ == '__main__':
    N = 30000
    random_list = list(range(N))
    random.shuffle(random_list)
    bubble_sort(random_list)

    random_list = list(range(N))
    random.shuffle(random_list)
    bubble_sort(random_list)

Parallel.py

from multiprocessing import Pool, cpu_count
from Sort import *

def bubble_sort_parallel(*args, **kwargs):
    return bubble_sort(*args, **kwargs)

if __name__ == '__main__':
    N = 30000
    random_list = list(range(N))
    random.shuffle(random_list)
    pool.apply_async(bubble_sort_parallel, (random_list,))
    random_list = list(range(N))
    random.shuffle(random_list)
    pool.apply_async(bubble_sort_parallel, (random_list,))

    pool.close()
    pool.join()

Один поток занял всего 2 секунды, а многопроцессорность - 8 секунд.

N = 300 000. Одиночный поток занял всего 200 секунд, а многопроцессорность - 1400 секунд.

Почему использование многопроцессорной обработки медленнее, чем однопоточная?

Как я мог улучшить производительность?

Платформа: Linux, pypy2.7-5.10.0, 4 ядра на моем компьютере

Многопроцессорность: [Рисунок многопроцессорности] [https://i.stack.imgur.com/QksXf.png]

Одиночный поток: [Рисунок одного потока] [https://i.stack.imgur.com/9HYw7.png]

Когда я запускаю Sort.py самостоятельно, я получаю TypeError: object of type 'NoneType' has no len(). Это предназначено? Как вы оцениваете сроки однопоточного подхода?

Kevin 04.05.2018 16:54

Вы также учли тот факт, что массивы, которые вы передаете в многопроцессорную версию, отличаются от обычной версии? Это может повлиять на производительность. Кроме того, сколько ядер у вас на машине?

Aneesh Durg 04.05.2018 16:56

Я заметил, что random.shuffle возвращает None, поэтому это может повлиять на вашу среду выполнения: вы фактически не передаете список своим функциям сортировки.

Kevin 04.05.2018 16:57

Я предполагаю, что вы используете BubbleSort исключительно для того, чтобы что-то запустить, экспериментируя с многопроцессорностью. Для фактических целей сортировки BubbleSort полезен только для демонстрации своей неполноценности почти всем другим (нормальным) алгоритмам сортировки. ;)

PM 2Ring 04.05.2018 17:08

Извините, я ввел неправильный код. Я поправил.

Kevin 04.05.2018 17:11

BubbleSort - это не производительность, но почему многопроцессорность медленнее, чем однопоточная?

Kevin 04.05.2018 17:15

Если вы увеличите размер списка до 300 000, останется ли разница между параллельным и последовательным выполнением?

djkern 04.05.2018 17:43

Я пробовал 300 тысяч. Параллельный процесс занял 1400 секунд, а последовательный инструмент - всего 200 секунд.

Kevin 04.05.2018 17:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
8
606
2

Ответы 2

Я пробовал с N=15000. На моем компьютере он работал почти одинаково, то есть непараллельная версия отсортировала один массив за 26 секунд. и параллельная версия отсортировала два массива за 28 секунд. Ставил pool = Pool(2). Вы пытались увеличить N, возможно, ваши результаты будут сопоставимы для больших значений N в вашей среде.

p.s. Вы также должны иметь в виду, что для создания процессов также требуются ресурсы и используются некоторые инструменты синхронизации.

Я надеюсь, что это уже было вам ясно: Pool.apply_async позволяет вам передавать работу другим процессам в пуле; он не выполняет автоматическое распараллеливание отдельной функции. Другими словами, обе версии выполняют каждую сортировку в одном потоке на одном ядре. Параллельная версия должна отправлять два вызова сортировки на два ядра, но вы измеряете время выполнения каждой сортировки, а не выполнение всей программы, поэтому вы не обнаружите какой-либо экономии из-за перекрытия двух вызовов сортировки. (Кроме того, в настоящее время ваш код не включает создание объекта пула, поэтому я просто предполагая, который вы использовали processes=N для N> 2 - хотя, опять же, это не имеет значения, поскольку вы не измеряете общий время выполнения, а скорее время выполнения каждого вида.)

(Если нет, см. https://docs.python.org/2/library/multiprocessing.html#using-a-pool-of-workers а также https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.pool)

Это, однако, не объясняет, почему просто отправка работы другому процессу приводит к более медленному времени выполнения. (Стоит отметить, что на моем MacBook Pro нет разницы во времени выполнения между простой версией и «параллельной» версией.) Причина медленности - связь между процессами. Вы просите его передать большой список через свой канал IPC, и он явно неэффективен при этом. Вы можете продемонстрировать это сами: переместите создание и перемешивание списка в bubble_sort_parallel и сделайте аргумент для предоставленной функции в pool.apply_async пустым списком. На моем компьютере это делает среду выполнения идентичной базовой версии.

Другие вопросы по теме