Pourquoi le multitraitement n'utilise-t-il qu'un seul cœur après l'importation de numpy?

127

Je ne sais pas si cela compte davantage comme un problème de système d'exploitation, mais j'ai pensé que je demanderais ici au cas où quelqu'un aurait un aperçu de la fin de Python.

J'ai essayé de paralléliser une forboucle lourde en CPU joblib, mais je trouve qu'au lieu d'attribuer chaque processus de travail à un noyau différent, je me retrouve tous affectés 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 __name__ == '__main__':
    run()

... et voici ce que je vois htoppendant l'exécution de ce script:

htop

J'exécute Ubuntu 12.10 (3.5.0-26) sur un ordinateur portable avec 4 cœurs. Il joblib.Parallelest clair que des processus séparés sont créés pour les différents travailleurs, mais y a-t-il un moyen de faire exécuter ces processus sur différents cœurs?

ali_m
la source
stackoverflow.com/questions/15168014/… - pas de réponses là-bas, j'ai peur, mais cela ressemble au même problème.
NPE
Est-ce toujours un problème? J'essaie de recréer cela avec Python 3.7 et d'importer numpy avec multiprocessing.Pool (), et il utilise tous les threads (comme il se doit). Je veux juste m'assurer que cela a été corrigé.
Jared Nielsen

Réponses:

148

Après quelques recherches sur Google, j'ai trouvé la réponse ici .

Il se trouve que certains modules Python ( numpy, scipy, tables, pandas, skimage...) pagaille avec une affinité de base à 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 la tâche à l'aide de

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

Avec cette ligne collée après l'importation du module, mon exemple fonctionne maintenant sur tous les cœurs:

htop_workaround

Mon expérience jusqu'à présent a été que cela ne semble pas avoir d'effet négatif sur numpyles performances de, bien que cela soit probablement spécifique à la machine et à la tâche.

Mettre à jour:

Il existe également deux façons de désactiver le comportement de réinitialisation d'affinité du processeur d'OpenBLAS lui-même. 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 de la source, vous pouvez le désactiver définitivement au moment de la construction en éditant le Makefile.rulepour contenir la ligne

NO_AFFINITY=1
ali_m
la source
Merci, votre solution a résolu le problème. Une question, j'ai le même code mais fonctionne différemment sur une machine différente. Les deux machines sont Ubuntu 12.04 LTS, python 2.7, mais une seule a ce problème. Avez-vous une idée pourquoi?
iampat
Les deux machines ont OpenBLAS (construit avec OpenMPI).
iampat
2
Ancien fil, mais au cas où quelqu'un d'autre trouverait ce problème, j'ai eu le problème exact et il était en effet lié aux bibliothèques OpenBLAS. Voir ici pour deux solutions de contournement possibles et une discussion connexe.
Gabriel
2
Une autre façon de définir l'affinité du processeur consiste à utiliserpsutil .
Ioannis Filippidis
2
@JHG C'est un problème avec OpenBLAS plutôt que Python, donc je ne vois aucune raison pour laquelle la version Python ferait une différence
ali_m
27

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}
WoJ
la source
1
Erreur> AttributeError: le module 'os' n'a pas d'attribut 'sched_getaffinity', Python 3.6
Paddy
4
@Paddy De la documentation liée: Ils ne sont disponibles que sur certaines plates-formes Unix.
BlackJack
2
J'ai le même problème mais j'ai intégré cette même ligne en haut os.system ("taskset -p 0xff% d"% os.getpid ()) mais il n'utilise pas tous les cpu
rajeshcis
12

Cela semble être un problème courant avec Python sur Ubuntu, et n'est pas spécifique à joblib:

Je suggérerais d'expérimenter l'affinité CPU ( taskset).

NPE
la source
Python on UbuntuCela implique qu'il fonctionne sans problème sur Windows et d'autres systèmes d'exploitation. C'est ça?
Mât du