...

Comprendre et optimiser le CPU Context Switching sous une charge élevée en mode d'hébergement

Sous forte charge, c'est la décision Commutation de contexte en mode d'hébergement, si le temps de l'unité centrale est consacré à un travail réel ou s'il est gaspillé dans le changement entre les threads. Je montre concrètement comment reconnaître les symptômes, trouver les causes et réduire les coûts de changement, afin que les applications web, les boutiques et les API réagissent de manière fiable et réduisent les coûts. Latence produire.

Points centraux

Les points clés suivants constituent le fil rouge de l'analyse et de l'optimisation dans le quotidien de l'hébergement.

  • Frais de changement augmentent avec les threads et entraînent rapidement une latence.
  • Symptômes se manifestent sous forme de gigue, de 503 et de valeurs cs remarquables.
  • Ordonnateur Linux et les priorités contrôlent l'équité et le temps de réaction.
  • Tuning comprend le nombre d'utilisateurs, la mise en cache, les limites et l'architecture.
  • Suivi avec cs, RPS et codes d'erreur évite de voler à l'aveuglette.

Ce que coûte vraiment la commutation de contexte dans l'hébergement

Chaque changement sauvegarde les registres, les pointeurs de pile, les compteurs de programme et recharge les états, ce qui, sous des serveurs web, PHP-FPM, des bases de données et des files d'attente fonctionnant en parallèle, peut entraîner des problèmes. Overhead est généré. Si le parallélisme augmente, les tranches de temps rétrécissent, les lignes de cache s'invalident plus souvent et le CPU passe beaucoup de temps dans le planificateur plutôt que dans la logique de l'application. Je constate souvent dans les logs que les requêtes par seconde augmentent à peine, alors que les cs/s s'envolent - un signe clair de gaspillage. temps CPU. Les configurations de partage et de conteneurs aggravent la situation, car de nombreux voisins génèrent des interruptions, des E/S et des processus supplémentaires. Si l'on fait monter en puissance les travailleurs sans frein, on déclenche des tempêtes de changement et on le paie par des temps de réaction fluctuants et des coûts plus élevés.

En pratique, je calcule grossièrement l'overhead : si un changement de contexte se situe par exemple entre 2 et 5 µs et que le système génère 150.000 cs/s, 0,3 à 0,75 seconde CPU par seconde disparaissent - donc une partie significative d'un cœur. Avec 500.000 cs/s, nous parlons rapidement de plusieurs cœurs qui font presque exclusivement de la gestion. Ce calcul approximatif permet de rendre les coûts cachés plus tangibles.

Voir aussi SMT/hyper-threading influence la perception : deux threads logiques se partagent les caches et les unités d'exécution. Si le nombre de threads actifs par cœur physique dépasse durablement deux, ils se font davantage concurrence pour les mêmes ressources - l'ordonnanceur change plus souvent, tandis que la progression réelle par thread diminue. C'est pourquoi je ne règle pas les workers en fonction de la logique, mais en fonction de l'activité. noyaux physiques et de regarder de manière ciblée les taux d'échec de la mémoire cache lorsque des pics de latence se produisent.

Reconnaître les symptômes : Quand le système ralentit

Je vérifie d'abord les temps de réponse fluctuants qui surviennent malgré une utilisation de 60-80 % du CPU et qui sont considérés comme Jitter sont perceptibles. Les erreurs 503 récurrentes indiquent souvent que les limites des processus ou des travailleurs sont épuisées et que les threads s'affrontent au lieu de travailler proprement. Des outils comme vmstat, pidstat -w et sar -w montrent les cs/s ainsi que les changements volontaires et forcés par processus, ce qui me permet d'identifier rapidement les fauteurs de troubles bruyants. Si les cs/s augmentent nettement sans augmentation proportionnelle des requêtes par seconde, cela signifie que trop d'administration tourne en rond, alors que la charge utile réelle est trop faible. Dans les environnements partagés, des limites d'utilisation équitables pour les processus, les minutes CPU et les E/S interviennent également, ce qui rend les goulots d'étranglement plus rapidement perceptibles et, à long terme, permet de réduire les coûts. Performance coûts [3][4].

En complément, j'utilise PSI (Information sur l'écurie de pression) via /proc/pressure/cpu : si les valeurs de coupe 10s/60s/300s indiquent une pression persistante du CPU, le travail s'accumule dans les queues d'exécution - même avec une charge totale modérée. Dans les environnements cgroup, un throttle_count croissant indique un étranglement du quota CFS, ce qui renforce les changements forcés et la gigue. Si des pics de ksoftirqd se produisent en parallèle, les interruptions de réseau ou de stockage sont souvent à l'origine des changements.

Autres remarques : Nombre de runnables par cœur élevé en permanence (>2) dans top/htop, 95e/99e centiles très dispersés dans APM, et processus qui ont été identifiés dans pidstat avec de nombreux involuntary-de changement d'état. Ensemble, ils permettent de déterminer clairement si je dois plutôt adresser une attente IO (volontaire) ou un retrait de CPU (forcé).

Bien évaluer les ordonnanceurs Linux

Le planificateur préemptif Linux planifie les processus de manière équitable via le CFS et réagit aux priorités, aux valeurs de Nice ainsi qu'aux interruptions d'E/S et de réseau, ce qui a une influence directe sur Temps de réaction a. Dans les piles d'hébergement avec de nombreuses tâches éphémères, les tranches de temps se rétrécissent et imposent des changements de contexte plus fréquents lorsque les configurations lancent des processus de manière débridée. Je préfère des priorités claires pour les travailleurs de la base de données et du web, afin que les chemins importants ne se perdent pas dans les files d'attente. Ceux qui souhaitent approfondir la question trouveront des options et des alternatives dans l'article suivant CFS et alternatives, qui permet de mieux voir les effets de bord dans l'hébergement. Il reste décisif de ne pas surcharger CFS avec trop de processus actifs, car l'équité en cas de densité élevée est la Latence et de gaspiller le débit.

Je tiens également compte des granularités de l'ordonnanceur : sched_min_granularity_ns et sched_wakeup_granularity_ns influencent la vitesse à laquelle les threads se déplacent les uns par rapport aux autres. Des tranches de temps trop petites augmentent le taux de changement, des tranches trop grandes favorisent la latence pour les charges de travail interactives. Sur les noyaux partagés ou en conteneurs, je reste généralement sur les valeurs par défaut et régule la charge par le biais du nombre de worker ; je réserve le réglage du noyau aux hôtes spécialisés.

Avec Affinité avec le CPU et l'affinité IRQ, je réduis le trafic croisé : le fait d'épingler les workers web et les threads DB sur des groupes de base différents, tout en distribuant de manière ciblée les interruptions NIC (RPS/XPS), réduit le faux partage de cache. Je tiens également compte des remarques de NUMA (mémoire locale) : si les threads sont migrés via des sockets, les latences et les changements de contexte augmentent. Cela aide numactl-politiques et éviter les migrations de threads inutiles.

Mesure et seuils : les chiffres qui comptent vraiment

Je n'évalue jamais le Context Switching de manière isolée, mais toujours avec la charge utile, les codes d'erreur et le nombre de processus, afin que Tendances deviennent visibles. Une comparaison propre avant/après chaque modification permet d'éviter les erreurs d'interprétation. Comme point de départ, des cs/s de l'ordre de quelques milliers ne sont souvent pas considérés comme critiques, alors que des sauts par rapport aux requêtes par seconde donnent l'alerte. Les changements volontaires dans les processus à forte charge d'E/S sont normaux, les changements forcés dans les tâches à la demande de l'unité centrale sont considérés comme un signal d'alarme. Le tableau suivant classe les principales métriques et montre les indications typiques que j'utilise au quotidien pour Goulots d'étranglement de saisir.

Métriques Outil Remarque Valeur indicative/interprétation
cs/s (total) vmstat, sar -w Taux de changement de l'ensemble du système En forte hausse sans augmentation de la RPS = overhead administratif
voluntary/involuntary pidstat -w Distinction entre l'attente E/S et le retrait de temps De nombreux changements forcés dans les tâches de CPU-bound sont critiques
Processus runnable top/htop, Load Longueur du serpent au cœur du CPU Haut en permanence = trop de worker/threads
HTTP 5xx/503 Journaux d'accès/d'erreurs Limites, délais, backpressure Pics de charge = limite Worker ou DB atteinte
RPS/TPM APM/NGINX/DB Charge utile par rapport à cs cs augmente plus vite que RPS = inefficacité

Quelques heuristiques ont fait leurs preuves : La longueur de la file d'attente par noyau est idéalement proche de 1, 2-3 est acceptable pendant une courte période, mais au-delà, la latence se disperse. Des cs/s à cinq ou six chiffres sont possibles sur les grands hôtes, mais doivent être adaptés à la charge utile. Calcul approximatif des coûts : cs/s × 2-5 µs montre combien de secondes de CPU disparaissent dans la gestion - un indicateur précoce avant que les utilisateurs ne le ressentent.

Je complète cette vue par des centiles (p95/p99) et la relation „cs par requête“. Si cette métrique reste stable ou baisse après un réglage, la mesure a été efficace. Si elle augmente, cela signifie souvent que des threads supplémentaires ont été créés sans que le chemin critique ne soit délesté.

Les causes au quotidien et comment les éliminer

Les pools PHP-FPM qui débordent, les consommateurs de files d'attente trop nombreux et les exécutions Cron inutiles font grimper les processus et génèrent des erreurs. Tempête de changement. Les plug-ins lourds des CMS empilent les requêtes de base de données et les tâches d'arrière-plan, qui fonctionnent immédiatement plus calmement grâce à la mise en cache ou à la suppression des extensions obsolètes [1][3]. En l'absence de cache de pages et d'objets, chaque requête doit passer par la chaîne dynamique complète et déclenche d'autres threads [6]. Je mise sur des index propres, des requêtes légères et je limite les travailleurs parallèles afin que les cœurs de l'unité centrale calculent plus longtemps dans le même contexte. Ainsi, les chemins des noyaux restent planifiables, les latences diminuent et les cs/s se rapprochent à nouveau de la réalité. charge utile.

A cela s'ajoutent les particularités du langage et de l'exécution : Des tâches CPU bloquantes dans Node.js obstruent la boucle d'événements ; le transfert dans des threads de travail ou des files d'attente permet de résoudre ce problème. Sur les services basés sur JVM, les pics de GC peuvent mettre les threads en pause, ce qui provoque l'engorgement des workers en aval et augmente les taux de changement - le réglage des tailles de tas et des stratégies de pause est payant. En PHP, les logs lents FPM révèlent des aberrations qui sont souvent en corrélation avec des opérations IO coûteuses ou des plugins défectueux.

Un autre modèle : un parallélisme excessif dans les tâches par lots. Au lieu de labourer 100 threads en parallèle à travers la même table, je mets à l'échelle via le sharding/partitions ou je limite la concurrency et je prolonge le temps d'exécution de manière minimale - le temps total diminue néanmoins parce que l'overhead diminue et que les hotspots dans la DB et le cache ne forcent pas constamment des changements de contexte.

Configuration du serveur : travailleurs, pools et limites

Je dimensionne PHP-FPM de manière à ce que la somme des workers actifs corresponde à peu près au nombre de cœurs physiques, plutôt que de lancer sans frein des processus qui ne font que Conflits causent des problèmes. Apache/Nginx obtient des limites réalistes de worker et de connexion, afin que les files d'attente lissent la charge au lieu d'inonder le planificateur. Les bases de données telles que MySQL ou PostgreSQL fonctionnent mieux lorsque les connexions maximales correspondent à la capacité de la RAM et du processeur et que les longues transactions sont évitées. Je suis heureux de donner des conseils pratiques sur la réduction des coûts de changement via l'article "Les coûts de changement". Réglage de l'overhead du CPU qui garde un œil sur le nombre de travailleurs, de pools et de backpressure. Ceux qui gèrent des projets professionnels bénéficient généralement de tarifs performants et de limites équitables - par exemple chez webhoster.de - et sont gagnants. Temps de réaction.

Ajustement fin dans la pratique :

  • PHP-FPM : pm = static/ondemand selon le profil de trafic ; pm.max_children ~ noyaux, pm.max_requests pour la prévention des fuites, process_idle_timeout contre les coûts de fonctionnement à vide. Trop de processus à l'arrêt augmentent les commutateurs sans utilité.
  • Nginx : worker_processes auto, utile worker_connections, keepalive_requests et les keepalive en amont réduisent les changements de connexion et de déconnexion. reuseport répartit la charge de manière plus équitable sur les travailleurs.
  • Apache : MPM event bat prefork dans les charges de travail mixtes ; des limites sévères sur les connexions simultanées protègent contre les débordements.
  • DB : Modéré max_connections, le pooling de connexions et les transactions courtes. Dans MySQL, les pools de threads aident, dans PostgreSQL, le proxy/pooling permet d'éviter les débordements de processus.
  • système : ulimit -n et augmenter les limites de systemd de manière appropriée, mais les backlogs (par ex. net.core.somaxconn) ne tournent pas à l'infini - les files d'attente lissent, elles ne remplacent pas la capacité.

Architecture et mise à l'échelle sans embouteillage

Au lieu de pousser une instance au maximum, je répartis les requêtes horizontalement entre plusieurs serveurs ou conteneurs, ce qui Taux de changement par hôte est sensiblement réduit. Les microservices avec des files d'attente asynchrones découplent les étapes de travail, de sorte que les tâches de longue durée ne se disputent pas simultanément le temps de l'unité centrale. La limitation de débit à la périphérie empêche les requêtes d'affluer, ce qui épuiserait les travailleurs et provoquerait des 503. Backpressure dans les files d'attente veille à ce que les producteurs n'engagent que la quantité de travail que les consommateurs traitent réellement. Avec des limites claires, l'ordonnanceur reste plus prévisible et les Latence est plus uniforme.

Pour la planification de la taille, j'utilise la loi de Little (L = λ - W) : la concordance autorisée par niveau résulte du taux d'arrivée et du temps de réponse souhaité. Je fixe des limites supérieures de manière à ce que chaque niveau (web, app, DB, file d'attente) reste stable de manière autonome. J'évite ainsi que des optimisations à un niveau ne conduisent qu'à des tempêtes de changement au niveau suivant.

Dans les environnements de conteneurs et d'orchestration, je tiens compte de l'utilisation de l'unité centrale et de l'espace de stockage.requests et -limitsDes quotas trop serrés ralentissent les threads de manière cyclique, ce qui augmente le nombre de changements forcés. Je fixe des limites au-dessus des bursts typiques et je mets à l'échelle horizontalement avant que les limites de quota CFS ne frappent toutes les minutes. L'autoscaling devrait évaluer les centiles (et pas seulement les moyennes) et les longueurs de file d'attente.

Interruptions, E/S et effets de réseau

De nombreux changements de contexte sont dus à des interruptions provenant du réseau et du stockage, qui nécessitent un travail supplémentaire du noyau et des Softirqs déclencher des attaques. Des taux PPS élevés, des handshakes TLS et des petits paquets augmentent la pression, c'est pourquoi j'utilise le batching, le keep-alive et des buffer utiles. NVMe aide à réduire la latence, mais sans discipline de file d'attente, les E/S rapides ne font qu'augmenter les changements de contexte entre les threads en attente et les threads en cours. Si je réduis les effets de type Nagle et que j'utilise des options de socket efficaces, le nombre de changements inutiles diminue sensiblement. Ceux qui souhaitent approfondir les thèmes des pilotes et des IRQ trouveront des informations pratiques dans l'article suivant Gestion des interruptions, qui a analysé les relations entre l'affinité de l'IRQ, la charge du CPU et l'utilisation de l'IRQ. Débit explique.

Je fais également attention à la répartition des queues de NIC sur les noyaux (RPS/XPS), à une coalescence d'interruption adaptée et à des MTU raisonnables. De nombreuses connexions courtes (par ex. l'absence de keep-alives) multiplient les handshake et les changements de contexte, alors que la résumption de session et l'utilisation de connexion empêchent justement cela. Du côté du stockage, je réduis les pics de synchronisation grâce au write-combining, à des intervalles de flux courts uniquement là où c'est techniquement nécessaire et à la backpressure dans les chemins du producteur.

Pour les configurations "busy edge", il vaut la peine de choisir des paramètres TLS et des concepts HTTP/2/3 de manière à ce que le multiplexage et le réemploi soient efficaces. L'objectif reste identique : moins de cycles de vie de connexion par requête, donc moins de transitions de noyau et des taux de changement plus faibles.

Surveillance et exploitation : contrôler au lieu de réagir

Je définis des alarmes non seulement pour le CPU, la RAM et les E/S, mais aussi pour cs/s, le nombre de processus et le temps de réponse, afin que Anomalies sont visibles très tôt. Les tests de charge avant les campagnes ou les versions révèlent des nombres de travailleurs, des temporisateurs et des limites de base de données inappropriés avant que les utilisateurs ne le remarquent. Je déploie les changements par étapes et compare les métriques pour que les améliorations restent mesurables de manière fiable [2][3][6]. Je complète les APM, les logs et les statistiques du noyau par des métriques commerciales telles que la durée du checkout ou la latence de l'API, afin que la technique et l'utilité se rejoignent. En vérifiant régulièrement, on reconnaît à temps les modèles et on maintient les Temps de réaction constante.

Je formule explicitement des SLO via la latence p95/p99 et je définis des alarmes sur Taux de brûlure (à quelle vitesse un budget d'erreur est consommé). Les tableaux de bord mettent en corrélation cs/s avec RPS, codes d'erreur, longueurs de file d'attente et PSI. Je peux ainsi voir si un bond en cs/s résulte d'une augmentation du travail réel - ou si la plateforme se noie dans le travail administratif. Cette image commune empêche le tuning à l'aveugle.

Dans l'entreprise, j'établis des fenêtres d'observation fixes après les changements (p. ex. 15/60/180 minutes) et je fixe des critères de rollback. Si „cs par requête“ se détériore, je commence par réduire la concordance et par laisser agir la pression arrière avant de serrer d'autres boulons.

Séparer les charges de travail de l'IA et les charges de travail élevées

Les fonctions d'IA sollicitent les cœurs de l'unité centrale plus longtemps par requête et entraînent ainsi des changements de contexte lorsque les requêtes web classiques doivent attendre en parallèle [2]. Je sépare les chemins chargés d'inférence en services propres, j'utilise des files d'attente et je garde le serveur web frontal aussi libre que possible des tâches de longue durée pour Latence de lisser les performances. Des ressources dédiées pour les backends AI empêchent que de courtes requêtes HTML restent dans l'ombre d'appels nécessitant beaucoup de calculs. Les limites de débit et les délais d'attente définissent des corridors clairs pour les chemins gourmands en calcul, afin que la planification soit maintenue. En appliquant strictement cette séparation, on réduit les cs/s sur le serveur web et on s'assure des performances fiables. Temps de réponse.

En pratique, cela signifie : des unités de déploiement et des files d'attente propres pour l'inférence, des limites de concordance strictes par modèle/point final et si possible Streaming au lieu d'un buffering bloquant. Je mesure la taille des lots et le parallélisme - je préfère qu'ils soient stables avec un taux de pointe un peu plus faible plutôt que de flotter avec des coûts de changement élevés.

Tuning-Quickwins en 10 minutes

Je commence par regarder vmstat, pidstat -w et les logs, je compare cs/s avec les requêtes et j'isole les processus avec beaucoup de forçage. Changer. Ensuite, je ramène les workers PHP-FPM et les workers du serveur web au niveau du nombre de noyaux et je vérifie si des files d'attente apparaissent au lieu d'une surcharge. Un cache de pages ou un micro-cache avant les chemins dynamiques allège immédiatement la charge, car moins d'exécution dynamique est nécessaire. Dans la base de données, je réduis les pics par des connexions max modérées et je vérifie les longues transactions qui bloquent trop souvent les cœurs. Pour finir, je teste à nouveau le RPS et le taux de réponse afin de quantifier l'effet et de déterminer les prochaines étapes. Étapes de planifier.

  • Vérification rapide : cs/s vs RPS, latence p95/p99, PSI-CPU. Tout indique l'administration plutôt que le travail ? Réduire la concordance.
  • Principal coupable : pidstat -w par processus, volontaire ou forcé. Ralentir immédiatement le CPU-bound avec de nombreux changements forcés.
  • Web/App : ramener les worker sur les noyaux physiques, activer Keep-Alive, vérifier le keepalive en amont, micro-cache sur les hotpaths.
  • DB : Max-Connections modéré, identifier les transactions longues, vérifier les index, adapter les consommateurs de la file d'attente aux besoins.
  • Réseau/IRQ : vérifier la répartition de l'IRQ, éviter trop de petites connexions, placer la coalescence de manière judicieuse.
  • Comparaison : „cs par requête“ et percentiles avant/après - ne reste que ce qui s'améliore de manière mesurable.

En bref

Efficace Commutation de contexte détermine dans l'hébergement si le temps de l'unité centrale est productif ou s'il se perd dans les frais administratifs. Celui qui reconnaît à temps les symptômes tels que la gigue, les 503 et les cs/s élevés, économise la latence et les coûts. Avec un nombre de travailleurs bien dosé, une mise en cache conséquente, des limites claires et une architecture propre, les processus restent calculables. Le monitoring, les tests de charge et les modifications itératives garantissent que chaque mesure est mesurable et ne déclenche pas de mauvaises surprises. Pour les projets exigeants, je mise sur des tarifs forts avec des limites justes - par exemple chez webhoster.de - afin que les temps de réaction restent constants et que les Expérience utilisateur est vrai.

Derniers articles

Plusieurs serveurs DNS dans deux centres de données pour un hébergement à haute disponibilité
hébergement web

Résolveur DNS Redondance et haute disponibilité dans l'hébergement

Découvre comment fonctionne la redondance du résolveur DNS dans l'hébergement avec plusieurs serveurs de noms et une architecture à haute disponibilité et pourquoi cette stratégie d'hébergement de redondance dns est si importante pour les performances et le référencement.