7
votes

Filetage en python vs multiprocession de Linux

basé sur ce Question J'ai supposé que la création Nouveau processus Strong> devrait être presque aussi rapide que strong> créer nouveau thread fort> sous Linux. Cependant, peu de test montrait un résultat très différent. Voici mon code: xxx pré>

tests: p> xxx pré>

donc, les processus sont presque 40 fois plus lents forts> à créer! Pourquoi cela se passe-t-il? Est-ce spécifique à Python ou à ces bibliothèques? Ou ai-je juste mal interprété la réponse ci-dessus? p>


upd 1. strong> pour le rendre plus clair. Je comprends que ce morceau de code n'introduit pas de concurrence. L'objectif ici est de tester le temps nécessaire pour créer un processus et un fil. Pour utiliser la concurrence réelle avec Python, on peut utiliser quelque chose comme ceci: p> xxx pré>

qui fonctionne vraiment beaucoup plus vite que la version filetée. p>


upd 2. strong> J'ai ajouté la version avec OS.FORK () code>: p>

$ time python test_fork.py 

real    0m3.919s
user    0m0.040s
sys     0m0.208s

$ time python test_multiprocessing.py 

real    0m1.088s
user    0m0.128s
sys     0m0.292s

$ time python test_threadings.py

real    0m0.134s
user    0m0.112s
sys     0m0.048s


3 commentaires

Eh bien, la question que vous avez liée à comparer le coût de l'appelant Fork (2) vs. code> pthread_create (3) , alors que votre code fait un peu plus. Que diriez-vous de comparer OS.FORK () avec thread.start_new_thread () ?


@Aya: Je n'ai pas pu trouver de type rejoindre dans thread Module pour créer un test similaire, mais même par rapport à une version de filetage de haut niveau avec OS.FORK () est toujours beaucoup plus lent. En fait, c'est le plus lent (bien que des conditions supplémentaires puissent affecter les performances). Voir ma mise à jour.


Vous devez utiliser un mutex pour attendre le fil si vous utilisez le module de thread à bas niveau , qui correspond à la manière dont le niveau de threading de niveau supérieur est implémente rejoindre () . Mais, si vous essayez simplement de mesurer le temps nécessaire pour créer le nouveau processus / thread, vous ne devriez pas appeler joindre () . Voir aussi ma réponse ci-dessous.


3 Réponses :


2
votes

Oui, c'est vrai. Démarrer un nouveau processus (appelé processus lourd) est coûteux.

Un aperçu ...

Le système d'exploitation doit (dans l'affaire Linux) Fourche le premier processus, configurez la comptabilisation du nouveau processus, configurez la nouvelle pile, effectuez le commutateur de contexte, copiez une mémoire qui est modifiée et déchire tout cela. lorsque le nouveau processus revient.

Le fil alloue simplement une nouvelle structure de pile et de thread, le contexte change-t-il et retourne lorsque le travail est terminé.

... c'est pourquoi nous utilisons des threads.


2 commentaires

Vous l'avez en arrière. Un processus n'est qu'un processus. Un thread est un processus léger :) Je suppose que vous pouvez appeler un processus un fil lourd, mais je ne pense pas que personne ne le fait. Qu'est-ce qu'un processus de poids lourd?


@ Shatang soupir. Si vous ne savez pas si vous ne savez pas, alors au moins, vous pourriez le faire Google. Essayez Googling "Processus de poids lourd" et voir si Quelqu'un fait cela .



1
votes

Dans mon expérience, il existe une différence significative entre la création d'un thread (avec pthread_create ) et le destination d'un processus.

Par exemple, j'ai créé un test C similaire à votre test de python avec le code de thread ceci: xxx

et processus de force de traitement comme ceci: xxx

sur mon système, le code de recherche a pris environ 8 fois plus Longue à exécuter.

Cependant, il convient de noter que la mise en œuvre de Python est encore plus lente - pour moi, c'était environ 16 fois plus lent. Je soupçonne que c'est parce que, en plus des frais généraux réguliers de la création d'un nouveau processus, il existe également plus de surcharge de python associée au nouveau processus.


0 commentaires

5
votes

La question que vous avez liée à la comparaison du coût de l'appelant Fork (2) vs. code> pthread_create (3) , alors que votre code fait un peu plus, par exemple Utilisation de Joindre () Pour attendre que les processus / threads se terminent.

Si, comme vous dites ...

Le but ici est de tester le temps nécessaire pour créer un processus et un fil.

... alors vous ne devriez pas attendre qu'ils se terminent. Vous devriez utiliser des programmes de test plus comme ceux-ci ...

Fork.py xxx

thread.py xxx

... qui donnent les résultats suivants ... xxx

... donc il y a donc Pas beaucoup de différence dans le temps de création des fils et des processus.


8 commentaires

Mais votre Fork.py ne peut-il pas créer de nouveaux threads et sortie, sans attendre les processus d'enfants à compléter?


En outre, vous lancez le prochain thread / processus sans attendre la fin de la fin de la fin, ils fonctionnent donc simultanément, alors qu'il semble être plus correct de les démarrer séquentiellement pour éviter toute gil et toutes ces choses.


@ffriend Eh bien, votre question a dit (l'accent est mis sur le mien) "J'ai supposé que Création de nouveau processus doit être presque aussi rapide que de créer un nouveau thread de Linux", et c'est. L'ensemble du point d'utilisation des threads est pour la concurrence, alors quel serait le point de faire fonctionner séquentiellement? Qu'est-ce que vous essayez exactement d'atteindre ici?


J'essaie de comparer les frais généraux pour avoir exécuté un nouveau fil et un nouveau processus. J'ai souligné la création pour séparer le thread / processus à partir d'autres détails tels que Gil, appels de fonction, etc. Mais bien sûr, le joindre en arrière compte également. Exécution de nombreux threads / processus séquentiellement est juste une autre façon de trouver du temps moyen. Voir ma première mise à jour pour plus de détails.


@ffriend bien, si vous incluez l'heure de la déchirure, les processus prennent un peu plus long que les fils, mais les frais généraux sont toujours dans la gamme de millisecondes, de toute façon. Cependant, dans la pratique, si la quantité de temps nécessaire à la configuration et à démolir un processus / thread est supérieure à la quantité de temps que le processus / thread fonctionne pour, il n'y a pas beaucoup de point dans les utiliser. Sinon, les frais généraux ne sont pas pertinents et le choix entre les deux devraient être basés sur ceux qui sont plus appropriés pour l'objectif réel que vous essayez d'accomplir.


@ffriend également, étant donné que la question liée ne fait que mesurer l'heure de configuration et ignorer le temps de démolition, alors cela expliquerait la différence entre vos résultats et les résultats des exemples de cette réponse. Je pensais que votre question était d'expliquer cette divergence, ce que je pensais avoir eu. Donc, si ce n'est votre question, alors quoi?


Tout comme exemple, envisagez l'application ou le cadre qui crée de nombreux threads / processus tout le temps, quelque chose de similaire à ce que fait Erlang. Si les processus étaient vraiment légers, vous pouvez les utiliser au lieu de threads. Mais si vous obtenez (relativement) de grandes frais générales, il serait préférable de mettre plus d'efforts sur les threads à la place. Bien sûr, il existe des moyens de surmonter tous les problèmes, mais c'est pire de connaître ces détails à l'avance. En outre, n'oubliez pas Gil qui est tout à propos des threads vs. Processus. Quoi qu'il en soit, les réponses et les commentaires clarifiés, alors j'accepte votre réponse comme le plus détaillé. Merci.


@ffriend je vois. Eh bien, la création de threads et de sous-processus a une surcharge assez importante, donc dans la pratique, j'essaierais probablement d'éviter de créer une "application ou un cadre qui crée beaucoup de threads / processus tout le temps" et d'utiliser une piscine modèle à la place. Le gil est un peu douloureux avec python, mais vous pouvez l'éviter à l'aide de sous-processus avec IPC ou à l'aide de threads qui appelle une bibliothèque C (par exemple avec CTYPES ) pour faire la majeure partie du travail.