web-dev-qa-db-fra.com

Sur quels cœurs de processeur mes processus Python sont-ils exécutés?

La mise en place

J'ai écrit un logiciel assez complexe en Python (sur un PC Windows). Mon logiciel démarre en gros deux interpréteurs Python. Le premier shell démarre (je suppose) lorsque vous double-cliquez sur le fichier main.py. Dans ce shell, les autres threads sont démarrés de la manière suivante:

    # Start TCP_thread
    TCP_thread = threading.Thread(name = 'TCP_loop', target = TCP_loop, args = (TCPsock,))
    TCP_thread.start()

    # Start UDP_thread
    UDP_thread = threading.Thread(name = 'UDP_loop', target = UDP_loop, args = (UDPsock,))
    TCP_thread.start()

Le Main_thread commence un TCP_thread et un UDP_thread. Bien qu'il s'agisse de threads distincts, ils s'exécutent tous dans un même shell Python.

Le Main_thread lance également un sous-processus. Cela se fait de la manière suivante:

p = subprocess.Popen(['python', mySubprocessPath], Shell=True)

D'après la documentation Python, je comprends que ce sous-processus exécute simultanément (!)} Dans une session/interpréteur Python distinct. Le Main_threadin de ce sous-processus est entièrement dédié à mon interface graphique. L'interface graphique démarre un TCP_thread pour toutes ses communications.

Je sais que les choses se compliquent un peu. Par conséquent, j'ai résumé toute la configuration dans cette figure:

 enter image description here


J'ai plusieurs questions concernant cette configuration. Je vais les énumérer ici:

Question 1 [Résolu]

Est-il vrai qu'un interpréteur Python utilise un seul cœur de processeur à la fois pour exécuter tous les threads? En d'autres termes, le Python interpreter session 1 (de la figure) exécutera-t-il les 3 threads (Main_thread, TCP_thread et UDP_thread) sur un cœur de processeur?

Réponse: oui, c'est vrai. Le verrou d'interprète global (GIL) garantit que tous les threads s'exécutent sur un seul cœur de processeur à la fois.

Question 2 [Pas encore résolu]

Ai-je un moyen de savoir quel est le cœur du processeur?

Question 3 [partiellement résolu]

Pour cette question, nous oublions le threads mais nous nous concentrons sur le mécanisme sous-processus en Python. Le démarrage d'un nouveau sous-processus implique le démarrage d'un nouvel interpréteur Python instance. Est-ce correct?

Réponse: Oui, c'est exact. Au début, il y avait une certaine confusion à savoir si le code suivant créerait une nouvelle instance d'interpréteur Python:

    p = subprocess.Popen(['python', mySubprocessPath], Shell = True)

Le problème a été clarifié. Ce code démarre en effet une nouvelle instance d'interpréteur Python.

Python sera-t-il assez intelligent pour faire en sorte que cette instance d'interpréteur Python distinct s'exécute sur un cœur de processeur différent? Existe-t-il un moyen de savoir laquelle, éventuellement avec des déclarations d'impression sporadiques?

Question 4 [Nouvelle question]

La discussion de la communauté a soulevé une nouvelle question. Il y a apparemment deux approches pour créer un nouveau processus (dans une nouvelle instance d'interpréteur Python):

    # Approach 1(a)
    p = subprocess.Popen(['python', mySubprocessPath], Shell = True)

    # Approach 1(b) (J.F. Sebastian)
    p = subprocess.Popen([sys.executable, mySubprocessPath])

    # Approach 2
    p = multiprocessing.Process(target=foo, args=(q,))

La seconde approche présente l'inconvénient évident de ne cibler qu'une fonction, alors que je dois ouvrir un nouveau script Python. Quoi qu'il en soit, les deux approches sont-elles similaires dans ce qu'elles réalisent?

31
K.Mulier

Puisque vous utilisez le module threading qui est construit sur thread. Comme le suggère la documentation, il utilise «l'implémentation du thread POSIX» pthread de votre système d'exploitation. 

  1. Les threads sont gérés par le système d'exploitation au lieu de l'interpréteur Python. La réponse dépendra donc de la bibliothèque pthread de votre système. Cependant, CPython utilise GIL pour empêcher plusieurs threads d’exécuter simultanément des bytecodes Python. Ils seront donc séquencés. Mais ils peuvent toujours être séparés en différents cœurs, ce qui dépend de vos bibliothèques pthread.
  2. Utilisez simplement un débogueur et attachez-le à votre python.exe. Par exemple, la commande GDB .
  3. Semblable à la question 1, le nouveau processus est géré par votre système d'exploitation et s'exécute probablement sur un noyau différent. Utilisez le débogueur ou tout moniteur de processus pour le voir. Pour plus de détails, allez à la CreatProcess() documentation page .
3
gdlmx

1, 2: Vous avez trois vrais threads, mais dans CPython, ils sont limités par GIL. Par conséquent, en supposant qu’ils utilisent du python pur, le code indique l’utilisation du processeur comme si un seul cœur était utilisé.

3: Comme dit gdlmx, il appartient à OS de choisir un noyau sur lequel exécuter un thread, Mais si vous avez vraiment besoin de contrôle, vous pouvez définir l'affinité de processus ou de thread à l'aide de l'API native Via ctypes. Puisque vous êtes sur Windows, ce serait comme ça:

# This will run your subprocess on core#0 only
p = subprocess.Popen(['python', mySubprocessPath], Shell = True)
cpu_mask = 1
ctypes.windll.kernel32.SetProcessAffinityMask(p._handle, cpu_mask)

J'utilise ici Popen._handle privé pour simplicty. La manière propre serait OpenProcess(p.tid) etc. 

Et oui, subprocess exécute python comme tout le reste dans un autre nouveau processus. 

1
robyschek