web-dev-qa-db-fra.com

Pourquoi le multitraitement utilise-t-il un seul cœur après avoir importé numpy?

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:

htop

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?

118
ali_m

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:

htop_workaround

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.

Mise à jour:

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
134
ali_m

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}
26
WoJ

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

11
NPE