Combien de threads sont trop nombreux?

J’écris un serveur et je twig chaque action dans un thread lorsque la requête est reçue. Je le fais parce que presque chaque requête fait une requête de firebase database. J’utilise une bibliothèque de pools de threads pour réduire la construction / destruction de threads.

Ma question est la suivante: quel est le bon sharepoint coupure pour les fils d’E / S comme ceux-ci? Je sais que ce serait juste une estimation approximative, mais parlons-nous des centaines? milliers?


MODIFIER:

Merci à tous pour vos réponses, il semble que je vais juste devoir le tester pour découvrir mon plafond de nombre de threads. La question est la suivante: comment savoir si j’ai atteint ce plafond? Que dois-je mesurer exactement?

Certains diraient que deux fils sont trop nombreux – je ne suis pas tout à fait dans ce camp 🙂

Voici mon conseil: mesurez, ne devinez pas. Une suggestion est de le rendre configurable et de le configurer initialement à 100, puis de libérer votre logiciel et de surveiller ce qui se passe.

Si votre utilisation de threads atteint son maximum à 3, alors 100 est trop. Si elle rest à 100 pour la plupart de la journée, montez-la à 200 et voyez ce qui se passe.

Vous pourriez en fait avoir votre code lui-même surveillant l’utilisation et ajuster la configuration pour la prochaine fois qu’il démarre, mais c’est probablement exagéré.


Pour clarification et élaboration:

Je ne préconise pas le déploiement de votre propre sous-système de pool de threads, utilisez bien celui que vous avez. Mais comme vous vous posiez des questions sur un bon sharepoint coupure pour les threads, je suppose que votre implémentation de pool de threads a la capacité de limiter le nombre maximum de threads créés (ce qui est une bonne chose).

J’ai écrit un code de regroupement de connexions de threads et de bases de données et ils présentent les fonctionnalités suivantes (essentielles aux performances, à mon avis):

  • un nombre minimum de threads actifs.
  • un nombre maximum de threads.
  • arrêter les threads qui n’ont pas été utilisés depuis un certain temps.

Le premier définit une ligne de base pour des performances minimales en termes de client de pool de threads (ce nombre de threads est toujours disponible). La seconde définit une ressortingction sur l’utilisation des ressources par les threads actifs. La troisième vous ramène à la ligne de base en temps de silence afin de minimiser l’utilisation des ressources.

Vous devez équilibrer l’utilisation des ressources en ayant des threads inutilisés (A) avec l’utilisation des ressources pour ne pas avoir assez de threads pour effectuer le travail (B).

(A) est généralement l’utilisation de la mémoire (stacks, etc.), car un thread qui ne travaille pas n’utilisera pas beaucoup de CPU. (B) sera généralement un retard dans le traitement des demandes au fur et à mesure qu’elles arrivent, car vous devez attendre qu’un thread devienne disponible.

C’est pourquoi vous mesurez. Au fur et à mesure que vous déclarez, la grande majorité de vos threads attendront une réponse de la firebase database pour ne pas fonctionner. Deux facteurs influent sur le nombre de threads à autoriser.

Le premier est le nombre de connexions DB disponibles. Cela peut être une limite ssortingcte à moins que vous ne puissiez l’augmenter au niveau du SGBD – je suppose que votre SGBD peut prendre un nombre illimité de connexions dans ce cas (même si vous devriez idéalement le mesurer).

Ensuite, le nombre de threads que vous devriez avoir dépend de votre utilisation historique. Le minimum que vous devriez avoir est le nombre minimum que vous avez déjà eu en cours d’exécution + A%, avec un minimum absolu de (par exemple, et le rendre configurable comme A) 5.

Le nombre maximum de threads doit être votre maximum historique + B%.

Vous devez également surveiller les changements de comportement. Si, pour une raison quelconque, votre utilisation atteint 100% de la disponibilité pendant une période significative (de sorte que cela affecte les performances des clients), vous devez augmenter le maximum autorisé jusqu’à ce qu’il soit à nouveau supérieur de B%.


En réponse à la question “que dois-je mesurer exactement?” question:

Ce que vous devez mesurer en particulier, c’est le nombre maximum de threads en utilisation simultanée (par exemple, en attente d’un retour de l’appel DB) sous charge. Ajoutez ensuite un facteur de sécurité de 10% par exemple (souligné, car d’autres affiches semblent prendre mes exemples comme des recommandations fixes).

En outre, cela devrait être fait dans l’environnement de production pour le réglage. Vous pouvez obtenir une estimation au préalable, mais vous ne savez jamais quelle production vous lancera (ce qui explique pourquoi toutes ces choses devraient être configurables au moment de l’exécution). Ceci est pour attraper une situation telle que le doublement inattendu des appels clients entrant.

Cette question a été discutée de manière approfondie et je n’ai pas eu l’occasion de lire toutes les réponses. Mais voici quelques points à prendre en compte en examinant la limite supérieure du nombre de threads simultanés pouvant coexister pacifiquement dans un système donné.

  1. Taille de la stack de threads: sous Linux, la taille de la stack de threads par défaut est de 8 Mo (vous pouvez utiliser ulimit -a pour la découvrir).
  2. Max Mémoire virtuelle prise en charge par une variante de système d’exploitation donnée. Le kernel Linux 2.4 prend en charge un espace d’adressage mémoire de 2 Go. avec le kernel 2.6, je suis un peu plus gros (3 Go)
  3. [1] montre les calculs pour le nombre maximum de threads par Max VM donnée pris en charge. Pour 2.4, il s’agit d’environ 255 threads. pour 2.6 le nombre est un peu plus grand.
  4. Quel planificateur de kernel vous avez. En comparant le planificateur Linux 2.4 avec le 2.6, le dernier vous offre une planification O (1) sans dépendance du nombre de tâches existant dans un système alors que la première est plus un O (n). Ainsi, les capacités SMP du calendrier du kernel jouent également un rôle important dans le nombre maximal de threads durables dans un système.

Vous pouvez maintenant ajuster la taille de votre stack pour incorporer plus de threads, mais vous devez alors prendre en compte les surcharges de la gestion des threads (création / destruction et planification). Vous pouvez imposer l’affinité au processeur à un processus donné, ainsi qu’à un thread donné, afin de les lier à des processeurs spécifiques pour éviter les frais de migration de threads entre les processeurs et éviter les problèmes de trésorerie.

Notez que l’on peut créer des milliers de threads à sa guise, mais quand Linux est à court de VM, il commence simplement à tuer les processus (donc les threads) de manière aléatoire. Cela permet d’éviter que le profil de l’utilitaire ne soit maximisé. (La fonction d’utilitaire indique l’utilité du système pour un nombre donné de ressources. Avec des ressources constantes dans ce cas, CPU Cycles et Memory, la courbe d’utilitaire s’aplatit avec un nombre croissant de tâches).

Je suis sûr que le planificateur du kernel Windows fait aussi quelque chose de ce genre pour gérer la surutilisation des ressources

[1] http://adywicaksono.wordpress.com/2007/07/10/i-can-not-create-more-than-255-threads-on-linux-what-is-the-solutions/

Si vos threads effectuent des tâches nécessitant beaucoup de ressources (CPU / Disk), alors vous verrez rarement des avantages au-delà de un ou deux, et trop de gens tueront les performances très rapidement.

Le meilleur exemple est que vos derniers threads seront bloqués pendant que les premiers seront terminés, ou certains auront des blocs à faible surcharge sur les ressources à faible contention. Le pire est que vous commencez à détruire le cache / disque / réseau et que votre débit global passe à travers le sol.

Une bonne solution consiste à placer des requêtes dans un pool qui sont ensuite envoyées aux threads de travail depuis un pool de threads (et oui, éviter la création / destruction de threads en continu est une excellente première étape).

Le nombre de threads actifs dans ce pool peut ensuite être modifié et mis à l’échelle en fonction des résultats de votre profiling, du matériel sur lequel vous exécutez et d’autres éléments susceptibles de se produire sur l’ordinateur.

Une chose à garder à l’esprit est que python (au moins la version basée sur C) utilise ce que l’on appelle un verrou d’interpréteur global qui peut avoir un impact énorme sur les performances des machines multi-core.

Si vous avez vraiment besoin du meilleur de python multithread, vous pouvez envisager d’utiliser Jython ou quelque chose du genre.

Comme Pax l’a bien dit, mesurez, ne devinez pas . Ce que j’ai fait pour DNSwitness et les résultats ont été surprenants: le nombre idéal de threads était beaucoup plus élevé que ce que je pensais, quelque chose comme 15 000 threads pour obtenir les résultats les plus rapides.

Bien sûr, cela dépend de beaucoup de choses, c’est pourquoi vous devez vous mesurer.

Mesures complètes dans Combien de fils d’exécution? .

J’ai écrit un certain nombre d’applications fortement multithread. J’autorise généralement le nombre de threads potentiels à être spécifié par un fichier de configuration. Lorsque je me suis mis au point pour des clients spécifiques, j’ai fixé le nombre suffisamment élevé pour que mon utilisation de tous les cœurs de processeur soit assez élevée, mais pas trop élevée pour que je rencontre des problèmes de mémoire (systèmes d’exploitation 32 bits au temps).

En d’autres termes, une fois que vous avez atteint un goulot d’étranglement, que ce soit le processeur, le débit de la firebase database, le débit du disque, etc., l’ajout de threads n’augmentera pas les performances globales. Mais jusqu’à ce que vous atteigniez ce point, ajoutez plus de threads!

Notez que cela suppose que les systèmes en question sont dédiés à votre application et que vous n’avez pas à jouer correctement (évitez d’affamer) d’autres applications.

La réponse “big iron” est généralement un thread par ressource limitée – processeur (lié au processeur), arm (lié aux E / S), etc. – mais cela ne fonctionne que si vous pouvez acheminer le travail vers le thread approprié pour la ressource. être consulté.

Si ce n’est pas possible, considérez que vous avez des ressources fongibles (CPU) et des ressources non fongibles (armes). Pour les processeurs, il n’est pas essentiel d’affecter chaque thread à un CPU spécifique (bien qu’il aide à la gestion du cache), mais pour les armes, si vous ne pouvez pas atsortingbuer un thread au arm, vous entrez dans la théorie des files d’attente. occupé. En règle générale, je pense que si vous ne pouvez pas acheminer les demandes en fonction du arm utilisé, le fait d’avoir 2 ou 3 fils par arm sera à peu près correct.

Une complication survient lorsque l’unité de travail transmise au thread n’exécute pas une unité de travail raisonnablement atomique. Par exemple, vous pouvez avoir le thread à un moment donné d’accéder au disque, à un autre moment, attendre sur un réseau. Cela augmente le nombre de «fissures» où des threads supplémentaires peuvent entrer et effectuer un travail utile, mais cela augmente également la possibilité pour les threads supplémentaires de polluer les caches les uns des autres, etc., et de ralentir le système.

Bien sûr, vous devez peser tout cela contre le “poids” d’un fil. Malheureusement, la plupart des systèmes ont des threads très lourds (et ce qu’ils appellent des “threads légers” ne sont pas du tout des threads), il est donc préférable de faire preuve de prudence.

Ce que j’ai vu en pratique, c’est que des différences très subtiles peuvent faire une énorme différence dans le nombre de threads optimaux. En particulier, les problèmes de cache et les conflits de locking peuvent considérablement limiter la quantité de simultanéité pratique.

Une chose à considérer est le nombre de cœurs sur la machine qui exécutera le code. Cela représente une limite ssortingcte au nombre de threads pouvant être exécutés à un moment donné. Toutefois, si, comme dans votre cas, les threads attendent fréquemment qu’une firebase database exécute une requête, vous souhaiterez probablement ajuster vos threads en fonction du nombre de requêtes simultanées que la firebase database peut traiter.

Je pense que c’est un peu une esquive à votre question, mais pourquoi ne pas les transformer en processus? Ma compréhension de la mise en réseau (à partir des jours flous du passé, je ne code pas du tout les réseaux) était que chaque connexion entrante peut être traitée comme un processus séparé, car si quelqu’un fait quelque chose de méchant dans votre processus, Nuke le programme entier.

ryeguy, je suis en train de développer une application similaire et mon numéro de threads est fixé à 15. Malheureusement, si je l’augmente à 20, il se bloque. Donc, oui, je pense que la meilleure façon de gérer cela est de mesurer si oui ou non votre configuration actuelle permet plus ou moins qu’un nombre X de threads.

Dans la plupart des cas, vous devez autoriser le pool de threads à gérer cela. Si vous publiez du code ou donnez plus de détails, il peut être plus facile de voir s’il ya une raison pour laquelle le comportement par défaut du pool de threads ne serait pas optimal.

Vous pouvez trouver plus d’informations sur la façon dont cela devrait fonctionner ici: http://en.wikipedia.org/wiki/Thread_pool_pattern

Autant de threads que de cœurs de processeur sont ce que j’ai souvent entendu.