Python multiprocessing.Pool: почему добавление дополнительных процессов не улучшает производительность в системе с гиперпоточностью?

Я экспериментирую с многопроцессорным модулем Python 3 и имею следующий код, который читает файл, содержащий число в каждой строке, и печатает факторизацию каждого числа:

import multiprocessing
import sys

NUM_PROCESSES = 4
CHUNK_SIZE = 20

def factor(n):
    #Return a list of factors of n
    factors = []
    #factor out 2's
    while n % 2 == 0:
        factors.append(2)
        n //= 2
    factor = 3
    while n > 1:
        if n % factor == 0:
            factors.append(factor)
            n //= factor
        else:
            factor += 2
    return factors

def process_line(line):
    #process an input file line
    number = int(line)
    factorization = '*'.join(str(x) for x in factor(number))
    return f'{number} = {factorization}\n'

if __name__ == '__main__':
    with open('input.txt') as f:
        with multiprocessing.Pool(NUM_PROCESSES) as pool:
            processed = pool.imap(process_line, f, CHUNK_SIZE)
            sys.stdout.writelines(processed)

Я запускаю это в системе Linux с 2 физическими ядрами с гиперпоточностью, всего 4 виртуальных ядра. Я протестировал этот сценарий на файле, содержащем 10 000 случайных чисел от 2 до 1 000 000, и измерил его производительность с помощью команды time.

Когда NUM_PROCESSES равно 1, я получаю результат:

real    0m26.997s
user    0m26.979s
sys     0m0.077s

Когда NUM_PROCESSES равно 2, я получаю:

real    0m13.477s
user    0m26.809s
sys     0m0.048s

Пока это то, что я ожидал - добавление еще одного процесса сокращает время выполнения почти вдвое, в то время как общее время процессора остается прежним. Но когда NUM_PROCESSES равно 4, я получаю:

real    0m14.598s
user    0m56.703s
sys     0m0.059s

Время выполнения не только не уменьшилось, но и увеличилось на 1 секунду, хотя время процессора увеличилось вдвое. По сути, это означает, что каждый виртуальный ЦП работал на половине скорости физического ЦП, поэтому от работы на всех 4 виртуальных ЦП не было никакого выигрыша в производительности. Изменение CHUNK_SIZE, по-видимому, не оказывает существенного влияния на производительность, даже если я установил его на 1 или 2500. Использование map() вместо imap() также ничего не меняет.

Насколько я понимаю, виртуальные ядра с поддержкой Hyper-Threading не дают таких же преимуществ в производительности, как дополнительные физические ядра, но они все же должны давать некоторое улучшение, верно? Почему тогда производительность скрипта не улучшилась?


person turtletuna    schedule 19.10.2018    source источник
comment
Вам нужно оставить хотя бы один поток для запуска системы. Поэтому попробуйте с NUM_PROCESSES = 3.   -  person Denis Rasulev    schedule 19.10.2018
comment
Эта статья может показаться интересной, она многое объясняет: medium.com/ @bfortuner/   -  person Denis Rasulev    schedule 19.10.2018
comment
Если я использую 3 процесса, я получаю реальные 0m13.809s, пользовательские 0m41.019s, так что все равно никаких улучшений. Задача, которую я выполняю, требует интенсивного использования ЦП, она вообще не выполняет операций ввода-вывода. Вот почему я считаю, что чем больше процессов, тем выше производительность.   -  person turtletuna    schedule 19.10.2018
comment
Гиперпоточность добавляет примерно 20-30% производительности в хороший день. Для некоторых рабочих нагрузок преимущество гиперпоточности в производительности равно нулю или отрицательно.   -  person Dietrich Epp    schedule 19.10.2018


Ответы (1)


(извините, я пока не могу добавить комментарий) Можете ли вы показать вывод lscpu? Создание слишком большого количества процессов/потоков приведет к переключению контекста. Когда вы запускаете это и используете htop, все ли ядра загружены на 100%?

person anon    schedule 19.10.2018