Je ne suis pas sûr que cela compte davantage comme un problème de système d’exploitation, mais j’avais l’idée de poser la question ici au cas où quelqu'un aurait un aperçu de la fin Python).
J'ai essayé de paralléliser une boucle for
avec beaucoup de ressources CPU en utilisant joblib
, mais je constate qu'au lieu d'attribuer chaque processus de travail à un noyau différent, tous les affecté au même noyau et sans gain de performance.
Voici un exemple très trivial ...
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 __== '__main__':
run()
... et voici ce que je vois dans htop
pendant l'exécution de ce script:
J'utilise Ubuntu 12.10 (3.5.0-26) sur un ordinateur portable à 4 cœurs. Clairement joblib.Parallel
génère des processus distincts pour les différents travailleurs, mais y a-t-il moyen de faire en sorte que ces processus s'exécutent sur des cœurs différents?
Après quelques recherches supplémentaires, j'ai trouvé la réponse ici .
Il s'avère que certains Python modules (numpy
, scipy
, tables
, pandas
, skimage
...) avec une affinité essentielle à l’importation: pour autant que je sache, ce problème semble être spécifiquement causé par leur liaison avec des bibliothèques OpenBLAS multithread.
Une solution de contournement consiste à réinitialiser l'affinité de tâche à l'aide de
os.system("taskset -p 0xff %d" % os.getpid())
Avec cette ligne collée après les importations du module, mon exemple s’exécute maintenant sur tous les cœurs:
Mon expérience jusqu’à présent montre que cela ne semble pas avoir d’effet négatif sur les performances de numpy
, bien que cela soit probablement lié à une machine et à une tâche.
Il existe également deux méthodes pour désactiver le comportement de OpenBLAS lui-même en matière de réinitialisation de l'affinité du processeur. Au moment de l'exécution, vous pouvez utiliser la variable d'environnement OPENBLAS_MAIN_FREE
(ou GOTOBLAS_MAIN_FREE
), par exemple
OPENBLAS_MAIN_FREE=1 python myscript.py
Ou bien, si vous compilez OpenBLAS à partir du source, vous pouvez le désactiver définitivement au moment de la construction en éditant le fichier Makefile.rule
pour contenir la ligne
NO_AFFINITY=1
Python 3 expose maintenant les méthodes pour définir directement l'affinité
>>> 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}
Cela semble être un problème commun avec Python sur Ubuntu et n'est pas spécifique à joblib
:
Je suggérerais d’expérimenter avec une affinité CPU ( taskset
).