Warum verwendet Multiprocessing nach dem Import von numpy nur einen einzigen Kern?

127

Ich bin mir nicht sicher, ob dies mehr als Betriebssystemproblem zählt, aber ich dachte, ich würde hier fragen, falls jemand einen Einblick in das Python-Ende der Dinge hat.

Ich habe versucht, eine CPU-schwere forSchleife mithilfe von zu parallelisieren joblib, aber ich stelle fest, dass nicht jeder Arbeitsprozess einem anderen Kern zugewiesen wird, sondern alle demselben Kern zugewiesen werden und kein Leistungsgewinn erzielt wird.

Hier ist ein sehr triviales Beispiel ...

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()

... und hier ist, was ich sehe, htopwährend dieses Skript ausgeführt wird:

htop

Ich verwende Ubuntu 12.10 (3.5.0-26) auf einem Laptop mit 4 Kernen. Es joblib.Parallelist klar, dass separate Prozesse für die verschiedenen Worker erzeugt werden. Gibt es jedoch eine Möglichkeit, diese Prozesse auf verschiedenen Kernen auszuführen?

ali_m
quelle
stackoverflow.com/questions/15168014/… - keine Antworten, fürchte ich, aber es klingt wie das gleiche Problem.
NPE
Ist das noch ein Problem? Ich versuche dies mit Python 3.7 neu zu erstellen und numpy mit multiprocessing.Pool () zu importieren, und es werden alle Threads verwendet (wie es sollte). Ich möchte nur sicherstellen, dass dies behoben wurde.
Jared Nielsen

Antworten:

148

Nach etwas mehr googeln fand ich die Antwort hier .

Es stellt sich heraus , dass bestimmte Python - Module ( numpy, scipy, tables, pandas, skimage...) mess mit Kern Affinität zu importieren. Soweit ich das beurteilen kann, scheint dieses Problem speziell dadurch verursacht zu werden, dass sie mit Multithread-OpenBLAS-Bibliotheken verknüpft sind.

Eine Problemumgehung besteht darin, die Aufgabenaffinität mithilfe von zurückzusetzen

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

Nachdem diese Zeile nach dem Import des Moduls eingefügt wurde, wird mein Beispiel jetzt auf allen Kernen ausgeführt:

htop_workaround

Bisher habe ich die Erfahrung gemacht, dass dies keine negativen Auswirkungen auf numpydie Leistung zu haben scheint , obwohl dies wahrscheinlich maschinen- und aufgabenspezifisch ist.

Aktualisieren:

Es gibt auch zwei Möglichkeiten, das Verhalten zum Zurücksetzen der CPU-Affinität von OpenBLAS selbst zu deaktivieren. Zur Laufzeit können Sie beispielsweise die Umgebungsvariable OPENBLAS_MAIN_FREE(oder GOTOBLAS_MAIN_FREE) verwenden

OPENBLAS_MAIN_FREE=1 python myscript.py

Wenn Sie OpenBLAS aus dem Quellcode kompilieren, können Sie es alternativ beim Erstellen dauerhaft deaktivieren, indem Sie das bearbeiten Makefile.rule, um die Zeile zu enthalten

NO_AFFINITY=1
ali_m
quelle
Vielen Dank, Ihre Lösung hat das Problem gelöst. Eine Frage, ich habe den gleichen Code, laufe aber auf zwei verschiedenen Maschinen unterschiedlich. Beide Maschinen sind Ubuntu 12.04 LTS, Python 2.7, aber nur eine hat dieses Problem. Hast du eine Idee warum?
Iampat
Beide Maschinen haben OpenBLAS (Build mit OpenMPI).
Iampat
2
Alter Thread, aber falls jemand anderes dieses Problem findet, hatte ich das genaue Problem und es hing tatsächlich mit den OpenBLAS-Bibliotheken zusammen. Sehen Sie hier für zwei mögliche Abhilfen und einige damit verbundene Diskussion.
Gabriel
2
Eine andere Möglichkeit, die CPU-Affinität einzustellen, ist die Verwendungpsutil .
Ioannis Filippidis
2
@JHG Es ist ein Problem mit OpenBLAS und nicht mit Python, daher kann ich keinen Grund erkennen, warum die Python-Version einen Unterschied machen würde
ali_m
27

Python 3 stellt jetzt die Methoden zum direkten Festlegen der Affinität bereit

>>> 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}
WoJ
quelle
1
Fehler> AttributeError: Modul 'os' hat kein Attribut 'sched_getaffinity', Python 3.6
Paddy
4
@Paddy Aus der verknüpften Dokumentation: Sie sind nur auf einigen Unix-Plattformen verfügbar.
BlackJack
2
Ich habe das gleiche Problem, aber ich habe die gleiche Zeile im oberen Betriebssystem integriert ("Taskset -p 0xff% d"% os.getpid ()), aber es werden nicht alle
CPUs
12

Dies scheint ein häufiges Problem mit Python unter Ubuntu zu sein und ist nicht spezifisch für joblib:

Ich würde vorschlagen, mit der CPU-Affinität zu experimentieren ( taskset).

NPE
quelle
Python on UbuntuDies bedeutet, dass es unter Windows und anderen Betriebssystemen problemlos funktioniert. Ist es?
Mast