web-dev-qa-db-fra.com

Multiprocessing: utiliser uniquement les cœurs physiques?

J'ai une fonction foo qui consomme beaucoup de mémoire et dont je voudrais exécuter plusieurs instances en parallèle.

Supposons que j'ai un processeur avec 4 cœurs physiques, chacun avec deux cœurs logiques.

Mon système a suffisamment de mémoire pour accueillir 4 instances de foo en parallèle mais pas 8. De plus, comme 4 de ces 8 cœurs sont logiques de toute façon, je ne m'attends pas non plus à ce que l'utilisation des 8 cœurs fournisse beaucoup de gains ci-dessus et au-delà de l'utilisation des 4 physiques seulement.

Je veux donc exécuter foo sur les 4 cœurs physiques niquement. En d'autres termes, je voudrais m'assurer que l'exécution de multiprocessing.Pool(4) (4 étant le nombre maximal d'exécutions simultanées de la fonction que je peux gérer sur cette machine en raison de limitations de mémoire) distribue le travail aux quatre cœurs physiques ( et non, par exemple, à un combo de deux cœurs physiques et de leurs deux descendants logiques).

Comment faire ça en python?

Éditer:

J'ai déjà utilisé un exemple de code de multiprocessing mais je suis indépendant de la bibliothèque, donc pour éviter toute confusion, j'ai supprimé cela.

19
user189035

J'ai trouvé une solution qui n'implique pas de changer le code source d'un module python. Il utilise l'approche suggérée ici . On peut vérifier que seuls les cœurs physiques sont actif après avoir exécuté ce script en faisant:

lscpu

dans le bash renvoie:

CPU(s):                8
On-line CPU(s) list:   0,2,4,6
Off-line CPU(s) list:  1,3,5,7
Thread(s) per core:    1

[On peut exécuter le script lié ci-dessus de l'intérieur python ]. Dans tous les cas, après avoir exécuté le script ci-dessus, en tapant ces commandes en python:

import multiprocessing
multiprocessing.cpu_count()

renvoie 4.

2
user189035

Je sais que le sujet est assez ancien maintenant, mais comme il apparaît toujours comme la première réponse lors de la saisie de "noyau logique multiprocesseur" dans google ... J'ai l'impression que je dois donner une réponse supplémentaire car je peux voir que ce serait possible pour les gens en 2018 (ou même plus tard ..) pour se confondre facilement ici (certaines réponses sont en effet un peu déroutantes)

Je ne vois pas de meilleur endroit qu'ici pour avertir les lecteurs de certaines des réponses ci-dessus, donc désolé de ramener le sujet à la vie.

-> POUR COMPTER LES CPU (LOGIQUE/PHYSIQUE) UTILISER LE MODULE PSUTIL

Pour un 4 core physique/8 thread i7 pour ex, il reviendra

import psutil 
psutil.cpu_count(logical = False)

4

psutil.cpu_count(logical = True)

8

Aussi simple que cela.

Là, vous n'aurez pas à vous soucier du système d'exploitation, de la plate-forme, du matériel lui-même ou quoi que ce soit. Je suis convaincu que c'est bien mieux que multiprocessing.cpu_count () qui peut parfois donner des résultats étranges, du moins d'après ma propre expérience.

-> POUR UTILISER N CORPS PHYSIQUE (à votre choix) UTILISEZ LE MODULE DE MULTIPROCESSATION DÉCRIT PAR YUGI

Il suffit de compter le nombre de processus physiques dont vous disposez, de lancer un multiprocessing.Pool de 4 travailleurs.

Ou vous pouvez également essayer d'utiliser la fonction joblib.Parallel ()

joblib en 2018 ne fait pas partie de la distribution standard de python, mais n'est qu'un wrapper du module de multitraitement qui a été décrit par Yugi.

-> LA PLUPART DU TEMPS, N'UTILISEZ PAS PLUS DE CŒURS QUE DISPONIBLE (à moins que vous n'ayez testé un code très spécifique et prouvé qu'il en valait la peine)

Nous pouvons entendre ici et là (également de la part de certaines personnes qui répondent ici) que "l'OS prendra soin correctement si vous utilisez plus de core que disponible". C'est absolument faux à 100%. Si vous utilisez plus de core que disponible, vous serez confronté à d'énormes baisses de performances. Parce que le planificateur du système d'exploitation fera de son mieux pour travailler sur chaque tâche avec la même attention, en passant régulièrement de l'un à l'autre, et selon le système d'exploitation, il peut consacrer jusqu'à 100% de son temps de travail à simplement basculer entre les processus, ce qui être désastreux.

Ne vous fiez pas seulement à moi: essayez-le, comparez-le, vous verrez à quel point c'est clair.

EST-IL POSSIBLE DE DÉCIDER SI LE CODE SERA EXÉCUTÉ SUR LA LOGIQUE OR NOYAU PHYSIQUE?

Si vous posez cette question, cela signifie que vous ne comprenez pas la façon dont les cœurs physiques et logiques sont conçus, alors vous devriez peut-être vérifier un peu plus sur l'architecture d'un processeur.

Si vous voulez exécuter sur le noyau 3 plutôt que sur le noyau 1 par exemple, je suppose qu'il existe en effet des solutions, mais disponibles uniquement si vous savez comment coder le noyau et le planificateur d'un système d'exploitation, ce qui, je pense, n'est pas le cas si vous êtes poser cette question.

Si vous lancez 4 processus gourmands en CPU sur un processeur logique 4 physiques/8, le planificateur attribuera chacun de vos processus à 1 noyau physique distinct (et 4 noyaux logiques resteront non/mal utilisés). Mais sur un processus à 4 threads logique/8, si les unités de traitement sont (0,1) (1,2) (2,3) (4,5) (5,6) (6,7), alors cela ne fait pas différence si le processus est exécuté sur 0 ou 1: c'est la même unité de traitement.

D'après mes connaissances au moins (mais un expert pourrait confirmer/infirmer, peut-être que cela diffère également des spécifications matérielles très spécifiques) je pense qu'il n'y a pas ou très peu de différence entre exécuter un code sur 0 ou 1. Dans l'unité de traitement (0,1 ), Je ne suis pas sûr que 0 soit la logique alors que 1 est le physique, ou vice-versa. D'après ma compréhension (ce qui peut être faux), les deux sont des processeurs de la même unité de traitement, et ils partagent juste leur mémoire cache/accès au matériel (RAM incluse), et 0 n'est pas plus une unité physique que 1.

Plus que cela, vous devriez laisser le système d'exploitation décider. Parce que le planificateur du système d'exploitation peut tirer parti d'un turbo boost matériel de noyau logique qui existe sur certaines plates-formes (ex i7, i5, i3 ...), autre chose que vous n'avez pas sous tension, et qui pourrait vraiment vous être utile.

Si vous lancez 5 tâches gourmandes en CPU sur un noyau logique 4 physiques/8, le comportement sera chaotique, presque imprévisible, principalement dépendant de votre matériel et de votre système d'exploitation. L'ordonnanceur fera de son mieux. Presque à chaque fois, vous devrez faire face à de très mauvaises performances.

Supposons un instant que nous parlons toujours d'une architecture classique 4(8): parce que l'ordonnanceur fait de son mieux (et change donc souvent les attributions), selon le processus que vous exécutez , il pourrait être encore pire de lancer sur 5 cœurs logiques que sur 8 cœurs logiques (où au moins il sait que tout sera utilisé à 100% de toute façon, donc perdu pour perdu il n'essaiera pas beaucoup de l'éviter, ne changera pas trop souvent et ne perdra donc pas trop de temps en changeant).

Cependant, il est sûr à 99% (mais vérifiez-le sur votre matériel) que presque tous les programmes de multitraitement fonctionneront plus lentement si vous utilisez plus de cœur physique que disponible.

Beaucoup de choses peuvent intervenir ... Le programme, le matériel, l'état du système d'exploitation, le planificateur qu'il utilise, le fruit que vous avez mangé ce matin, le nom de votre sœur ... Au cas où vous doutez de quelque chose, il suffit de le comparer, il n'y a pas d'autre moyen simple de savoir si vous perdez ou non des performances. Parfois, l'informatique peut être vraiment bizarre.

-> LA PLUPART DU TEMPS, DES NOYAUX LOGIQUES SUPPLÉMENTAIRES SONT INDIQUÉS INUTILES EN PYTHON (mais pas toujours)

Il y a 2 façons principales de faire des tâches vraiment parallèles en python.

  • multiprocessing (ne peut pas tirer parti des cœurs logiques)
  • multithreading (peut tirer parti des cœurs logiques)

Par exemple pour exécuter 4 tâches en parallèle

-> le multiprocessing créera 4 interprètes différents python. Pour chacun d'eux, vous devez démarrer un interprète python, définissez les droits de lecture/écriture, définissez l'environnement, allouer beaucoup de mémoire, etc. Disons-le tel quel: vous allez démarrer une toute nouvelle instance de programme à partir de 0. Cela peut prendre un temps considérable, vous devez donc être sûr que ce nouveau programme fonctionnera assez longtemps pour que cela en vaille la peine.

Si votre programme a suffisamment de travail (disons, quelques secondes de travail au moins), alors parce que le système d'exploitation alloue des processus consommant du processeur sur différents cœurs physiques, il fonctionne et vous pouvez gagner beaucoup de performances, ce qui est génial. Et comme le système d'exploitation permet presque toujours aux processus de communiquer entre eux (bien qu'il soit lent), ils peuvent même échanger (un peu de) données.

-> le multithreading est différent. Au sein de votre interprète python, il ne fera que créer une petite quantité de mémoire que de nombreux CPU seront disponibles pour partager et y travailler en même temps. Il est beaucoup plus rapide de générer (où la génération d'un nouveau processus sur un ancien ordinateur peut parfois prendre plusieurs secondes, la génération d'un thread se fait dans une fraction de temps ridiculement petite.) Vous ne créez pas de nouveaux processus, mais des "threads" beaucoup plus légers.

Les threads peuvent partager la mémoire entre les threads très rapidement, car ils fonctionnent littéralement ensemble sur la même mémoire (alors qu'elle doit être copiée/échangée lorsque vous travaillez avec différents processus).

MAIS: POURQUOI NE POUVEZ PAS WE UTILISER LE MULTI-LECTURE DANS LA PLUPART DES SITUATIONS? CELA A l'air très pratique?

Il y a une très GRANDE limitation en python: une seule ligne python peut être exécutée à la fois dans un interpréteur python, appelé GIL (Global Interpreter) La plupart du temps, vous perdrez même des performances en utilisant le multithreading, car différents threads devront attendre pour accéder à la même ressource. Le multithreading est toujours inutile et même pire si votre code est en python pur.

-> POURQUOI NE DOIS-JE PAS UTILISER DE NOYAUX LOGIQUES LORS DE L'UTILISATION DU MULTI-TRAITEMENT?

Les cœurs logiques n'ont pas leur propre accès à la mémoire. Ils ne peuvent travailler que sur l'accès mémoire et sur le cache de son processeur physique d'hébergement. Par exemple, il est très probable (et souvent utilisé en effet) que le cœur logique et le cœur physique d'une même unité de traitement utilisent tous les deux la même fonction C/C++ sur différents emplacements de la mémoire cache en même temps. Rendre le traitement extrêmement rapide en effet.

Mais ... ce sont des fonctions C/C++! Python est un gros wrapper C/C++, qui a besoin de beaucoup plus de mémoire et de CPU que son code C++ équivalent. Il est très probable en 2018 que, quoi que vous vouliez faire, 2 gros python auront besoin de beaucoup, beaucoup plus de mémoire et de lecture/écriture de cache que ce qu'une seule unité physique + logique peut se permettre, et bien plus que ce que consommerait le code C/C++ véritablement multithread équivalent. encore une fois, entraînerait presque toujours une baisse des performances. N'oubliez pas que chaque variable qui n'est pas disponible dans le cache du processeur mettra x1000 à lire en mémoire. Si votre cache est déjà complètement rempli pour 1 seule python processus, devinez ce qui se passera si vous forcez 2 processus à l'utiliser: ils l'utiliseront un à la fois et basculeront de manière permanente, ce qui provoquera le vidage stupide des données et les relira à chaque fois qu'il commutera. lu ou écrit de mémoire, vous pourriez penser que votre CPU "fonctionne" mais ce n'est pas le cas. Il attend les données! En ne faisant rien .

-> COMMENT POUVEZ-VOUS PROFITER DES CŒURS LOGIQUES ALORS?

Comme je l'ai dit, il n'y a pas de véritable multithreading (donc pas de véritable utilisation des cœurs logiques) dans le python par défaut, à cause du verrouillage de l'interpréteur global. Vous pouvez forcer la suppression du GIL pendant certaines parties du programme, mais je pense qu'il serait judicieux de ne pas y toucher si vous ne savez pas exactement ce que vous faites.

La suppression du GIL a définitivement fait l'objet de nombreuses recherches (voir les projets expérimentaux PyPy ou Cython qui tentent tous les deux de le faire).

Pour l'instant, aucune solution réelle n'existe, car c'est un problème beaucoup plus complexe qu'il n'y paraît.

Il existe, je l'admets, une autre solution qui peut fonctionner: - Codez votre fonction en C - Enveloppez-la dans python avec ctype - Utilisez le module python multithreading pour appelez votre fonction C enveloppée

Cela fonctionnera à 100% et vous pourrez utiliser tous les cœurs logiques, en python, avec multithreading, et pour de vrai. Le GIL ne vous dérangera pas, car vous n'exécuterez pas les vraies fonctions python, mais les fonctions C à la place.

Par exemple, certaines bibliothèques comme Numpy peuvent fonctionner sur tous les threads disponibles, car elles sont codées en C. Mais si vous arrivez à ce point, j'ai toujours pensé qu'il pourrait être judicieux de penser à faire votre programme en C/C++ directement parce qu'il est une considération très éloignée de l'esprit Pythonique d'origine.

** -> N'UTILISEZ PAS TOUJOURS TOUS LES NOYAUX PHYSIQUES DISPONIBLES **

Je vois souvent des gens comme "Ok, j'ai 8 noyaux physiques, donc j'en prendrai 8 pour mon travail". Cela fonctionne souvent, mais s'avère parfois être une mauvaise idée, surtout si votre travail nécessite beaucoup d'E/S.

Essayez avec des cœurs N-1 (encore une fois, en particulier pour les tâches exigeantes en E/S), et vous verrez que 100% du temps, par tâche/moyenne, les tâches individuelles s'exécuteront toujours plus rapidement sur le cœur N-1. En effet, votre ordinateur fait beaucoup de choses différentes: USB, souris, clavier, réseau, disque dur, etc ... Même sur un poste de travail, des tâches périodiques sont effectuées à tout moment en arrière-plan dont vous n'avez aucune idée. Si vous ne laissez pas 1 cœur physique pour gérer ces tâches, votre calcul sera régulièrement interrompu (vidé de la mémoire/replacé en mémoire), ce qui peut également entraîner des problèmes de performances.

Vous pourriez penser "Eh bien, les tâches en arrière-plan n'utiliseront que 5% du temps CPU donc il reste 95%". Mais ce n'est pas le cas.

Le processeur gère une tâche à la fois. Et chaque fois qu'il commute, un temps considérablement élevé est gaspillé pour tout remettre à sa place dans le cache/registres de mémoire. Ensuite, si pour une raison étrange, le planificateur du système d'exploitation effectue cette commutation trop souvent (quelque chose que vous n'avez aucun contrôle), tout ce temps de calcul est perdu pour toujours et vous ne pouvez rien y faire.

Si (et cela arrive parfois) pour une raison inconnue, ce problème de planificateur affecte les performances non pas de 1 mais de 30 tâches, il peut en résulter des situations vraiment intrigantes où le travail sur le noyau physique 29/30 peut être considérablement plus rapide que sur 30/30

PLUS DE CPU IS PAS TOUJOURS LE MEILLEUR

Il est très fréquent, lorsque vous utilisez un pool multiprocessing.Pool, d'utiliser une file d'attente multiprocessing.Queue ou manager, partagée entre les processus, pour permettre une communication de base entre eux. Parfois (je dois l'avoir dit 100 fois mais je le répète), d'une manière dépendante du matériel, cela peut se produire (mais vous devez le comparer pour votre application spécifique, votre implémentation de code et votre matériel) que l'utilisation de plus de CPU pourrait créer un goulot d'étranglement lorsque vous faites communiquer/synchroniser les processus. Dans ces cas spécifiques, il pourrait être intéressant d'exécuter sur un nombre de CPU inférieur, ou même d'essayer de déporter la tâche de synchronisation sur un processeur plus rapide (je parle ici de calculs intensifs scientifiques exécutés sur un cluster bien sûr). Comme le multitraitement est souvent destiné à être utilisé sur des clusters, vous devez noter que les clusters sont souvent sous-synchronisés en fréquence à des fins d'économie d'énergie. De ce fait, les performances monocœur peuvent être vraiment mauvaises (équilibrées par un nombre beaucoup plus élevé de CPU), ce qui aggrave le problème lorsque vous mettez à l'échelle votre code à partir de votre ordinateur local (quelques cœurs, performances monocœur élevées) à un cluster (beaucoup de cœurs, performances monocœur inférieures), car votre goulot d'étranglement de code selon le ratio single_core_perf/nb_cpu, ce qui le rend parfois très ennuyeux

Tout le monde a la tentation d'utiliser autant de CPU que possible. Mais une référence pour ces cas est obligatoire.

Le cas typique (en science des données par exemple) est d'avoir N processus exécutés en parallèle et vous voulez résumer les résultats dans un fichier. Parce que vous ne pouvez pas attendre que le travail soit terminé, vous le faites via un processus d'écriture spécifique. L'écrivain écrira dans le fichier de sortie tout ce qui est poussé dans son multiprocessing.Queue (processus limité à un cœur et à un disque dur). Les N processus remplissent le multiprocessing.Queue.

Il est facile alors d'imaginer que si vous avez 31 CPU écrivant des informations sur un CPU vraiment lent, alors vos performances chuteront (et peut-être que quelque chose plantera si vous surmontez la capacité du système à gérer des données temporaires)

-> Message à emporter

  • Utilisez psutil pour compter les processeurs logiques/physiques, plutôt que multiprocessing.cpu_count () ou quoi que ce soit
  • Le multitraitement ne peut fonctionner que sur le cœur physique (ou au moins le comparer pour prouver que ce n'est pas vrai dans votre cas)
  • Le multithreading fonctionnera sur le noyau logique MAIS vous devrez coder et encapsuler vos fonctions en C, ou supprimer l'interpréteur de verrouillage global (et chaque fois que vous le faites, un chaton meurt atrocement quelque part dans le monde)
  • Si vous essayez d'exécuter le multithreading sur du code python pur, vous aurez d'énormes baisses de performances, vous devriez donc utiliser 99% du temps à la place du multitraitement
  • À moins que vos processus/threads ne subissent de longues pauses que vous pouvez exploiter, n'utilisez jamais plus de core que disponible et testez correctement si vous voulez essayer
  • Si votre tâche est intensive en E/S, vous devez laisser 1 cœur physique pour gérer les E/S, et si vous avez suffisamment de cœur physique, cela en vaudra la peine. Pour les implémentations multiprocesseurs, il doit utiliser le noyau physique N-1. Pour un multithreading bidirectionnel classique, cela signifie utiliser le noyau logique N-2.
  • Si vous avez besoin de plus de performances, essayez PyPy (pas prêt pour la production) ou Cython, ou même pour le coder en C

Dernier point mais non le moindre, et le plus important de tous: si vous êtes vraiment à la recherche de performances, vous devez absolument, toujours, toujours comparer, et ne rien deviner. Le benchmark révèle souvent un comportement très spécifique de plate-forme/matériel/pilote étrange dont vous n'auriez aucune idée.

22
Delevoye Guillaume

Note: Cette approche ne fonctionne pas sur Windows et elle est testée uniquement sur Linux.

en utilisant multiprocessing.Process:

L'affectation d'un noyau physique à chaque processus est assez simple lors de l'utilisation de Process(). Vous pouvez créer une boucle for qui itère à travers chaque cœur et assigne le nouveau processus au nouveau cœur en utilisant taskset -p [mask] [pid]:

import multiprocessing
import os

def foo():
    return

if __name__ == "__main__" :
    for process_idx in range(multiprocessing.cpu_count()):
        p = multiprocessing.Process(target=foo)
        os.system("taskset -p -c %d %d" % (process_idx % multiprocessing.cpu_count(), os.getpid()))
        p.start()

J'ai 32 cœurs sur mon poste de travail, je vais donc mettre des résultats partiels ici:

pid 520811's current affinity list: 0-31
pid 520811's new affinity list: 0
pid 520811's current affinity list: 0
pid 520811's new affinity list: 1
pid 520811's current affinity list: 1
pid 520811's new affinity list: 2
pid 520811's current affinity list: 2
pid 520811's new affinity list: 3
pid 520811's current affinity list: 3
pid 520811's new affinity list: 4
pid 520811's current affinity list: 4
pid 520811's new affinity list: 5
...

Comme vous le voyez, l'affinité précédente et nouvelle de chaque processus ici. Le premier est pour tous les cœurs (0-31) et est ensuite affecté au cœur 0, le deuxième processus est par défaut affecté à core0, puis son affinité est modifiée pour le cœur suivant (1), etc.

en utilisant multiprocessing.Pool:

Attention: Cette approche nécessite de peaufiner le module pool.py Car il n'y a aucun moyen que je sache que vous pouvez extraire le pid de la Pool(). Ces modifications ont également été testées sur python 2.7 Et multiprocessing.__version__ = '0.70a1'.

Dans Pool.py, Recherchez la ligne où la méthode _task_handler_start() est appelée. Dans la ligne suivante, vous pouvez affecter le processus du pool à chaque cœur "physique" en utilisant (j'ai mis le import os Ici pour que le lecteur n'oublie pas de l'importer):

import os
for worker in range(len(self._pool)):
    p = self._pool[worker]
    os.system("taskset -p -c %d %d" % (worker % cpu_count(), p.pid))

et tu as fini. Tester:

import multiprocessing

def foo(i):
    return

if __name__ == "__main__" :
    pool = multiprocessing.Pool(multiprocessing.cpu_count())
    pool.map(foo,'iterable here')

résultat:

pid 524730's current affinity list: 0-31
pid 524730's new affinity list: 0
pid 524731's current affinity list: 0-31
pid 524731's new affinity list: 1
pid 524732's current affinity list: 0-31
pid 524732's new affinity list: 2
pid 524733's current affinity list: 0-31
pid 524733's new affinity list: 3
pid 524734's current affinity list: 0-31
pid 524734's new affinity list: 4
pid 524735's current affinity list: 0-31
pid 524735's new affinity list: 5
...

Notez que cette modification de pool.py Attribue les tâches aux cœurs de manière circulaire. Donc, si vous attribuez plus de tâches que les cpu-cores, vous finirez par en avoir plusieurs sur le même core.

MODIFIER:

Ce que recherche OP, c'est d'avoir une pool() capable de regarder le pool sur des cœurs spécifiques. Pour cela, d'autres ajustements sur multiprocessing sont nécessaires (annulez d'abord les modifications susmentionnées).

Avertissement:

N'essayez pas de copier-coller les définitions de fonction et les appels de fonction. Copiez et collez uniquement la partie qui doit être ajoutée après self._worker_handler.start() (vous le verrez ci-dessous). Notez que mon multiprocessing.__version__ Me dit que la version est '0.70a1', Mais cela n'a pas d'importance tant que vous ajoutez simplement ce que vous devez ajouter:

multiprocessing's pool.py:

ajoutez un argument cores_idx = None à la définition de __init__(). Dans ma version, cela ressemble à ceci après l'avoir ajouté:

def __init__(self, processes=None, initializer=None, initargs=(),
             maxtasksperchild=None,cores_idx=None)

vous devez également ajouter le code suivant après self._worker_handler.start():

if not cores_idx is None:
    import os
    for worker in range(len(self._pool)):
        p = self._pool[worker]
        os.system("taskset -p -c %d %d" % (cores_idx[worker % (len(cores_idx))], p.pid))

multiprocessing's __init__.py:

Ajoutez un argument cores_idx=None À la définition de la fonction Pool() ainsi que de l'autre appel de fonction Pool() dans la partie de retour. Dans ma version, cela ressemble à:

def Pool(processes=None, initializer=None, initargs=(), maxtasksperchild=None,cores_idx=None):
    '''
    Returns a process pool object
    '''
    from multiprocessing.pool import Pool
    return Pool(processes, initializer, initargs, maxtasksperchild,cores_idx)

Et tu as fini. L'exemple suivant exécute un pool de 5 travailleurs sur les cœurs 0 et 2 uniquement:

import multiprocessing


def foo(i):
    return

if __name__ == "__main__":
    pool = multiprocessing.Pool(processes=5,cores_idx=[0,2])
    pool.map(foo,'iterable here')

résultat:

pid 705235's current affinity list: 0-31
pid 705235's new affinity list: 0
pid 705236's current affinity list: 0-31
pid 705236's new affinity list: 2
pid 705237's current affinity list: 0-31
pid 705237's new affinity list: 0
pid 705238's current affinity list: 0-31
pid 705238's new affinity list: 2
pid 705239's current affinity list: 0-31
pid 705239's new affinity list: 0

Bien sûr, vous pouvez toujours avoir la fonctionnalité habituelle de la multiprocessing.Poll() en supprimant l'argument cores_idx.

11
Kennet Celeste