J'ai récemment écrit un serveur http en C++, et c'est à la lecture de plusieurs articles, dont celui qui ma motivé à écrire cet article : http://pl.atyp.us/content/tech/servers.html, que je me suis rendu compte de la faute impardonnable que je faisais. Je pari que beaucoup de gens, je veux dire particulièrement les débutants, font aussi cette erreur grave. Les threads ne sont pas des êtres humain.
Pour comprendre mettons nous en situation. Prenons par exemple la fabrication d'une voiture. Si vous regardez une usine qui fabrique des voitures, vous vous apercevrez que sa construction est divisée en plusieurs étapes, que chaque étape doit se faire dans un ordre précis, et qu'au final, une voiture en sort à l'autre bout. On peut aisément comparer ce processus au traitement d'une requête par un serveur. Observons maintenant les travailleurs. Ceux ci sont affectés à une tâche bien précise, faisant un travail vital pour toute la chaîne, mais bien défini. Regardons pourquoi le concepteur de cette chaîne, sûrement un peu tayloriste, à choisit cette façon de faire. Les raisons sont simples, il adapte la chaîne de production à sa main d'oeuvre.
Les travailleurs sont affectés à une tâche pour minimiser les temps de déplacement, pour maximiser la concentration, pour que l'optimisation d'un geste maintes fois répété soit maximale, et pour d'autre raisons directement liées aux performances, au rendement.
Pour un serveur, on identifie un processus, que l'on divise ensuite en stage. Prenons par exemple l'écoute sur un port donné, et le traitement d'une requête. Vous ne voyez peut-être pas encore où je veux en venir, mais si vous deviez mettre des threads dans tout ça, vous devriez repenser encore: les threads ne sont pas humains. Ils ne sont pas déconcentrés, le temps de déplacement d'une tâche à une autre est nul (appel de fonction), le thread n'apprend rien, il est directement au maximum de ses performances. Il n'y a de fait plus aucun intérêt diviser le travail entre plusieurs threads. Si nous devions procéder ainsi, nous devrions perdre du temps pour que toute la chaîne soit synchronisée (des locks), utiliser des files d'attentes pour les tâches à faire, nous devrions passer des données entre plusieurs threads (context switches), la complexité augmente, les performances baissent. Non, dans le cas d'un serveur, je pense qu'il faut minimiser le nombre de thread (par exemple le nombre de coeur) et concevoir l'application comme s'il n'y avait aucun thread. Bien entendu, on reste capable d'augmenter facilement le nombre de thread, tout simplement en multipliant les usines. Si un seul thread peut écouter sur un ou plusieurs port et traiter toute la requête, deux pourront le faire sans problème, avec peu ou pas d'interaction entre les deux threads. Les performances seront multipliées par deux, la simplicité est conservée.
Bien entendu, il existe des cas où dans une même chaîne de traitement, plusieurs actions pourraient avantageusement être parallélisées. Ces cas sont rares, et si vraiment vous voulez y réfléchir, pensez à la complexité induite. On passe 10% du temps à écrire du code, 90% à le corriger.
0 commentaires:
Enregistrer un commentaire