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


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

Я пытался распараллелить CPU-heavy 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 пока этот скрипт работает:

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

3 111

3 ответа:

после некоторых погуглив я нашел ответ!--13-->здесь.

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

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

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

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

htop_workaround

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

обновление:

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

OPENBLAS_MAIN_FREE=1 python myscript.py

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

NO_AFFINITY=1

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}