Dlaczego wieloprocesorowość używa tylko jednego rdzenia po zaimportowaniu numpy?

127

Nie jestem pewien, czy liczy się to bardziej jako problem z systemem operacyjnym, ale pomyślałem, że zapytałbym tutaj, gdyby ktoś miał jakiś wgląd w Python.

Próbowałem zsynchronizować forpętlę obciążającą procesor joblib, ale stwierdziłem, że zamiast przypisywać każdy proces roboczy do innego rdzenia, w końcu wszystkie są przypisane do tego samego rdzenia i nie ma wzrostu wydajności.

Oto bardzo trywialny przykład ...

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

... a oto, co widzę htoppodczas działania tego skryptu:

htop

Używam Ubuntu 12.10 (3.5.0-26) na laptopie z 4 rdzeniami. Jasne joblib.Paralleljest, że powstają oddzielne procesy dla różnych procesów roboczych, ale czy istnieje sposób, aby te procesy były wykonywane na różnych rdzeniach?

ali_m
źródło
stackoverflow.com/questions/15168014/… - obawiam się, że brak odpowiedzi, ale brzmi to jak ten sam problem.
NPE
Czy to nadal problem? Próbuję odtworzyć to w Pythonie 3.7 i zaimportować numpy z multiprocessing.Pool (), i używa wszystkich wątków (tak jak powinno). Chcę się tylko upewnić, że zostało to naprawione.
Jared Nielsen

Odpowiedzi:

148

Po kilku kolejnych googlach znalazłem odpowiedź tutaj .

Okazuje się, że niektóre moduły Pythona ( numpy, scipy, tables, pandas, skimage...) bałagan z rdzeniem powinowactwa na import. O ile wiem, problem ten wydaje się być spowodowany przez ich łączenie z wielowątkowymi bibliotekami OpenBLAS.

Obejściem problemu jest zresetowanie koligacji zadania za pomocą

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

Po wklejeniu tej linii po zaimportowaniu modułu mój przykład działa teraz na wszystkich rdzeniach:

htop_workaround

Z mojego dotychczasowego doświadczenia wynika, że ​​nie wydaje się to mieć żadnego negatywnego wpływu na numpywydajność, chociaż jest to prawdopodobnie zależne od maszyny i zadania.

Aktualizacja:

Istnieją również dwa sposoby, aby wyłączyć resetowanie powinowactwa procesora samego OpenBLAS. W czasie wykonywania możesz na przykład użyć zmiennej środowiskowej OPENBLAS_MAIN_FREE(lub GOTOBLAS_MAIN_FREE)

OPENBLAS_MAIN_FREE=1 python myscript.py

Lub alternatywnie, jeśli kompilujesz OpenBLAS ze źródła, możesz go trwale wyłączyć w czasie kompilacji, edytując tak, Makefile.ruleaby zawierał wiersz

NO_AFFINITY=1
ali_m
źródło
Dziękuję, twoje rozwiązanie rozwiązało problem. Jedno pytanie, mam ten sam kod, ale działam inaczej na holowaniu różnych maszyn. Obie maszyny to Ubuntu 12.04 LTS, python 2.7, ale tylko jeden ma ten problem. Czy masz pojęcie, dlaczego?
iampat
Obie maszyny mają OpenBLAS (kompilacja z OpenMPI).
iampat
2
Stary wątek, ale na wypadek, gdyby ktoś inny znalazł ten problem, miałem dokładny problem i rzeczywiście był on związany z bibliotekami OpenBLAS. Zobacz tutaj dwa możliwe obejścia i kilka powiązanych dyskusji.
Gabriel
2
Innym sposobem ustawienia powinowactwa procesora jest użyciepsutil .
Ioannis Filippidis
2
@JHG Jest to problem z OpenBLAS, a nie z Pythonem, więc nie widzę żadnego powodu, dla którego wersja Pythona miałaby coś zmienić
ali_m
27

Python 3 udostępnia teraz metody do bezpośredniego ustawiania koligacji

>>> 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
źródło
1
Błąd> AttributeError: moduł „os” nie ma atrybutu „schedule_getaffinity”, Python 3.6
Paddy
4
@Paddy Z połączonej dokumentacji: są one dostępne tylko na niektórych platformach Unix.
BlackJack
2
Mam ten sam problem ale zintegrowałem ten sam wiersz na górze os.system ("taskset -p 0xff% d"% os.getpid ()) ale nie używa całego procesora
rajeshcis
12

Wydaje się, że jest to częsty problem z Pythonem w systemie Ubuntu i nie dotyczy joblib:

Sugerowałbym eksperymentowanie z koligacją procesora (taskset ).

NPE
źródło
Python on UbuntuOznacza to, że działa bez problemów w systemie Windows i innym systemie operacyjnym. Czy to jest?
Maszt