Почему многопроцессорность использует только одно ядро ​​после импорта numpy?

Я не уверен, что это больше похоже на проблему с ОС, но я подумал, что попрошу здесь, если у кого-то есть представление о конце Python.

Я пытаюсь параллелизовать цикл с процессором for с использованием joblib, но я считаю, что вместо каждого рабочего процесса, назначенного другому ядру, я получаю все, что им назначено то же самое ядро ​​и отсутствие производительности.

Здесь очень тривиальный пример...

from joblib import Parallel,delayed
import numpy as np

def testfunc(data):
    # some very boneheaded CPU work
    for nn in xrange(1000):
        for ii in data[0,:]:
            for jj in data[1,:]:
                ii*jj

def run(niter=10):
    data = (np.random.randn(2,100) for ii in xrange(niter))
    pool = Parallel(n_jobs=-1,verbose=1,pre_dispatch='all')
    results = pool(delayed(testfunc)(dd) for dd in data)

if __name__ == '__main__':
    run()

... и вот что я вижу в htop, пока работает этот script:

htop

Я запускаю Ubuntu 12.10 (3.5.0-26) на ноутбуке с 4 ядрами. Ясно, что joblib.Parallel порождает отдельные процессы для разных работников, но есть ли способ, который я могу сделать, чтобы эти процессы выполнялись на разных ядрах?

Ответ 1

После еще нескольких поисковых запросов я нашел ответ здесь.

Оказывается, что некоторые модули Python (numpy, scipy, tables, pandas, skimage...) путаются с привязкой ядра к импорту. Насколько я могу судить, эта проблема, по-видимому, вызвана тем, что они связаны с многопоточными библиотеками OpenBLAS.

Обходной путь заключается в reset сродстве задачи с использованием

os.system("taskset -p 0xff %d" % os.getpid())

С помощью этой строки, вставленной после импорта модуля, мой пример теперь работает на всех ядрах:

htop_workaround

До сих пор мой опыт заключался в том, что это, по-видимому, не оказывает негативного влияния на производительность numpy, хотя это, вероятно, связано с машинами и задачами.

Обновление:

Существует также два способа отключить поведение функции сброса аффинации CPU самого OpenBLAS. Во время выполнения вы можете использовать переменную среды OPENBLAS_MAIN_FREE (или GOTOBLAS_MAIN_FREE), например

OPENBLAS_MAIN_FREE=1 python myscript.py

Или, альтернативно, если вы компилируете OpenBLAS из исходного кода, вы можете навсегда отключить его во время сборки, отредактировав Makefile.rule, чтобы содержать строку

NO_AFFINITY=1

Ответ 2

Python 3 теперь предоставляет методы , чтобы напрямую установить сродство

>>> import os
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}
>>> os.sched_setaffinity(0, {1, 3})
>>> os.sched_getaffinity(0)
{1, 3}
>>> x = {i for i in range(10)}
>>> x
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> os.sched_setaffinity(0, x)
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}

Ответ 3

Это, по-видимому, общая проблема с Python на Ubuntu и не относится к joblib:

Я бы предложил экспериментировать с аффинностью процессора (taskset).