web-dev-qa-db-fra.com

Combien de fils, c'est trop?

J'écris un serveur et je branche chaque action de dans un thread lorsque la demande est entrante. Je le fais parce que presque chaque requête effectue une requête dans la base de données. J'utilise une bibliothèque de threadpool pour réduire la construction/destruction des threads.

Ma question est cependant - quel est un bon point de coupure pour les threads I/O comme ceux-ci? Je sais que ce ne serait qu'une estimation approximative, mais parlons-nous de centaines? milliers?


MODIFIER:

Merci à tous pour vos réponses, il semble que je vais juste devoir le tester pour connaître mon plafond de comptage de threads. La question est cependant: comment savoir si j'ai atteint ce plafond? Que dois-je mesurer exactement?

283
ryeguy

Certaines personnes diraient que deux fils, c'est trop - je ne suis pas tout à fait dans ce camp :-)

Voici mon conseil: mesure, ne devine pas. Une suggestion est de le rendre configurable et de le définir au départ à 100, puis de relâcher ton logiciel dans la nature et de surveiller ce qui se passe.

Si l'utilisation de votre fil atteint 3, 100, c'est trop. S'il reste à 100% pendant la majeure partie de la journée, augmentez-le à 200 et voyez ce qui se passe.

Votre pourrait avez votre code lui-même pour surveiller l'utilisation et ajuster la configuration pour le prochain démarrage, mais c'est probablement trop.


Pour clarification et élaboration:

Je ne préconise pas de lancer votre propre sous-système de regroupement de threads, utilisez certainement celui que vous avez. Mais, étant donné que vous demandiez un bon point de coupure pour les threads, je suppose que l'implémentation de votre pool de threads permet de limiter le nombre maximal de threads créés (ce qui est une bonne chose).

J'ai écrit le code de mise en pool des connexions de base de données et de threads, qui présente les fonctionnalités suivantes (que je considère essentielles pour la performance):

  • un nombre minimum de threads actifs.
  • un nombre maximal de threads.
  • fermer les threads qui n'ont pas été utilisés pendant un moment.

Le premier définit une base de référence pour des performances minimales en termes de client du pool de threads (ce nombre de threads est toujours disponible pour utilisation). La seconde définit une restriction sur l'utilisation des ressources par les threads actifs. La troisième vous ramène à la base de référence pendant les périodes creuses afin de minimiser l'utilisation des ressources.

Vous devez équilibrer l'utilisation des ressources des threads inutilisés (A) par l'utilisation des ressources du fait de ne pas avoir suffisamment de threads pour effectuer le travail (B).

(A) correspond généralement à l'utilisation de la mémoire (piles, etc.) puisqu'un thread ne faisant aucun travail n'utilisera pas une grande partie du processeur. (B) retardera généralement le traitement des demandes au fur et à mesure de leur arrivée car vous devez attendre qu'un thread soit disponible.

C'est pourquoi vous mesurez. Comme vous l'avez dit, la grande majorité de vos threads attendront une réponse de la base de données pour ne pas s'exécuter. Deux facteurs affectent le nombre de threads à autoriser.

Le premier est le nombre de connexions à la base de données disponibles. Cela peut être une limite difficile à moins que vous ne puissiez l'augmenter au niveau du SGBD - je vais supposer que votre SGBD peut prendre un nombre illimité de connexions dans ce cas (bien que vous devriez idéalement le mesurer également).

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

Le nombre maximal de threads doit correspondre à votre historique maximum + B%.

Vous devriez également surveiller les changements de comportement. Si, pour une raison quelconque, votre utilisation est disponible à 100% pendant un temps considérable (afin d’affecter 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?":

Ce que vous devez mesurer spécifiquement est le nombre maximal de threads utilisés simultanément (par exemple, en attente d'un retour de l'appel de base de données) sous charge. Ajoutez ensuite un facteur de sécurité de 10% pour exemple (souligné, car d'autres afficheurs semblent prendre mes exemples pour recommandations fixes).

De plus, cela devrait être fait dans l'environnement de production pour le réglage. Vous pouvez obtenir un devis à l’avance, mais vous ne savez jamais quelle production vous gênera (c’est pourquoi toutes ces choses devraient être configurables au moment de l’exécution). Cela permet d’attraper une situation telle que le doublement inattendu des appels des clients entrant.

184
paxdiablo

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

  1. Taille de la pile de threads: Sous Linux, la taille de pile de threads par défaut est de 8 Mo (vous pouvez utiliser ulimit -a pour le trouver).
  2. Mémoire virtuelle maximale prise en charge par une variante de système d'exploitation donnée. Le noyau Linux 2.4 prend en charge un espace d'adressage mémoire de 2 Go. avec le noyau 2.6, j'ai un peu plus gros (3 Go)
  3. [1] montre les calculs pour le nombre maximal de threads par Max VM prise en charge donnée. Pour 2.4, il s’agit d’environ 255 threads. pour 2.6 le nombre est un peu plus grand.
  4. Quel planificateur de noyau kindda vous avez. En comparant le planificateur de noyau Linux 2.4 avec la version 2.6, ce dernier vous donne une planification O(1) sans dépendance du nombre de tâches existantes dans un système, tandis que la première est plus une tâche O (n). De même, les capacités SMP de la planification du noyau 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 pile pour intégrer plus de threads, mais vous devez ensuite prendre en compte les frais généraux liés à la gestion des threads (création/destruction et planification). Vous pouvez appliquer l'affinité du processeur à un processus donné ainsi qu'à un thread donné pour les lier à des processeurs spécifiques afin d'éviter les coûts de migration de threads entre les processeurs et d'éviter les problèmes de trésorerie.

Notez que l'on peut créer des milliers de threads à sa guise, mais lorsque Linux est à court de VM, il commence simplement à tuer les processus (donc les threads). Cela permet d'éviter que le profil de l'utilitaire ne soit saturé. (La fonction d'utilitaire indique l'utilitaire à l'échelle du système pour une quantité donnée de ressources. Avec des ressources constantes dans ce cas Cycles du processeur et mémoire, la courbe de l'utilitaire s'aplatit avec un nombre croissant de tâches).

Je suis sûr que le planificateur de noyau Windows fait également 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 /

34
Jay D

Si vos threads effectuent tout type de travail gourmand en ressources (processeur/disque), vous verrez rarement des avantages supérieurs à un ou deux, et un nombre trop important de performances entraînera une perte de performances très rapide.

Le "meilleur des cas" est que vos derniers threads s'arrêteront pendant que les premiers s'achèveront, ou que certains auront des blocs à faible temps système sur des ressources peu conflictuelles. Dans le pire des cas, vous commencez à bricoler le cache/le disque/le réseau et votre débit global est dépassé.

Une bonne solution consiste à placer des demandes dans un pool qui sont ensuite envoyées aux threads de travail à partir d'un pool de threads (et oui, éviter la création/destruction continue de threads est une excellente première étape).

Le nombre de threads actifs dans ce pool peut ensuite être ajusté et mis à l'échelle en fonction des résultats de votre profilage, du matériel sur lequel vous travaillez et d'autres événements susceptibles de se produire sur la machine.

16
Andrew Grant

Il convient de garder à l’esprit que python (au moins la version C) utilise ce qu’on appelle un verrou d’interprète global , qui peut avoir un impact considérable sur les performances multi-core. Machines.

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

9
Chad Okere

Comme Pax l'a dit à juste titre, mesure, ne devine pas . C'est 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 je ne le pensais, environ 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 en Combien de fils d'exécution? .

7
bortzmeyer

J'ai écrit un certain nombre d'applications fortement multithreads. J'autorise généralement le nombre de threads potentiels à spécifier par un fichier de configuration. Lorsque je me suis spécialisé 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 si élevée que j’ai eu des problèmes de mémoire (il s’agissait de systèmes d’exploitation 32 bits). temps).

En d'autres termes, une fois que vous aurez atteint un goulot d'étranglement, que ce soit le processeur, le débit de la base de données, 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 discussions!

Notez que cela suppose que le ou les systèmes en question sont dédiés à votre application et que vous ne devez pas jouer correctement (éviter de mourir de faim) d'autres applications.

4
Matthew Lund

La réponse "big iron" correspond généralement à un thread par ressource limitée - processeur (lié à la CPU), arm (lié aux E/S), etc. - mais cela ne fonctionne que si vous pouvez router le travail vers le bon thread pour que la ressource être consulté.

Lorsque 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'attribuer chaque thread à un processeur spécifique (bien que cela aide à la gestion du cache), mais pour les bras, si vous ne pouvez pas affecter de fil au bras, vous entrez dans la théorie des files d'attente et quel est le nombre optimal pour garder les bras occupé. En règle générale, je pense que si vous ne pouvez pas acheminer les demandes en fonction du bras utilisé, le fait d'avoir 2-3 fils par bras 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 fil à un moment donné 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 faire un travail utile, mais cela augmente également la possibilité pour des threads supplémentaires de polluer les antémémoires, etc., et de gêner 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 souvent pas du tout des threads), il est donc préférable de se tromper.

Ce que j'ai vu dans la pratique, c'est que des différences très subtiles peuvent faire une énorme différence quant au nombre de threads optimaux. En particulier, les problèmes de cache et les conflits de verrous peuvent limiter considérablement la quantité de concurrence simultanée.

3
Hot Licks

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

2
newdayrising

Je pense que votre question est un peu fictive, mais pourquoi ne pas les insérer dans des processus? Ma compréhension de la mise en réseau (depuis les jours brumeux d’antan, je ne code pas du tout les réseaux) était que chaque connexion entrante pouvait être gérée comme un processus séparé, car si quelqu'un faisait quelque chose de méchant dans votre processus, nuke le programme entier.

2
mmr

ryeguy, je suis en train de développer une application similaire et mon nombre 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 votre configuration actuelle autorise plus ou moins qu'un nombre X de threads.

1
hyperboreean