Mises en garde sur les réacteurs select / poll vs. epoll dans Twisted

Tout ce que j’ai lu et expérimenté (les applications basées sur Tornado) m’amène à penser que ePoll remplace naturellement les réseaux basés sur Select et Poll, en particulier avec Twisted. Ce qui me rend paranoïaque, c’est assez rare qu’une meilleure technique ou méthodologie ne vienne pas avec un prix.

La lecture de quelques dizaines de comparaisons entre epoll et les alternatives montre que epoll est clairement le champion de la vitesse et de l’extensibilité, en particulier du fait qu’elle évolue de manière linéaire, ce qui est fantastique. Cela dit, qu’en est-il de l’utilisation du processeur et de la mémoire, epoll est-il toujours le champion?

Pour un très petit nombre de sockets (varie en fonction de votre matériel, bien sûr, mais nous parlons de quelque chose de l’ordre de 10 ou moins), select peut battre epoll en mémoire et à la vitesse d’exécution. Bien sûr, pour un si petit nombre de sockets, les deux mécanismes sont si rapides que vous ne vous souciez pas vraiment de cette différence dans la grande majorité des cas.

Une clarification, cependant. Les deux sélectionnent et épollent linéairement. La grande différence réside dans le fait que les API faisant face à l’espace utilisateur présentent des complexités basées sur des éléments différents. Le coût d’un appel select correspond approximativement à la valeur du descripteur de fichier numéroté le plus élevé que vous lui transmettez. Si vous sélectionnez une seule fd, 100, alors c’est environ deux fois plus cher que de sélectionner une seule fd, 50. Ajouter plus de fds en dessous du plus haut n’est pas tout à fait gratuit, donc c’est un peu plus compliqué que cela en pratique. est une bonne première approximation pour la plupart des implémentations.

Le coût de epoll est plus proche du nombre de descripteurs de fichiers qui ont effectivement des événements sur eux. Si vous surveillez 200 descripteurs de fichiers, mais que seuls 100 d’entre eux ont des événements, vous ne payez que très grossièrement ces 100 descripteurs de fichiers actifs. C’est là que epoll a tendance à offrir l’un de ses principaux avantages par rapport à la sélection. Si vous avez un millier de clients qui sont pour la plupart inactifs, alors lorsque vous utilisez select, vous en payez toujours les mille. Cependant, avec epoll, c’est comme si vous n’en aviez que quelques-uns, vous ne payez que pour ceux qui sont actifs à un moment donné.

Tout cela signifie que epoll réduira l’utilisation du processeur pour la plupart des charges de travail. En ce qui concerne l’utilisation de la mémoire, c’est un peu difficile. select parvient à représenter toutes les informations nécessaires de manière très compacte (un descripteur par bit). Et la limitation FD_SETSIZE (généralement 1024) du nombre de descripteurs de fichiers que vous pouvez utiliser avec select signifie que vous ne dépenserez jamais plus de 128 octets pour chacun des trois ensembles fd que vous pouvez utiliser avec select (read, write, exception). Par rapport à ces 384 octets maximum, epoll est une sorte de cochon. Chaque descripteur de fichier est représenté par une structure multi-octets. Cependant, en termes absolus, cela ne va toujours pas utiliser beaucoup de mémoire. Vous pouvez représenter un très grand nombre de descripteurs de fichiers en quelques dizaines de kilo-octets (environ 20 000 pour 1 000 descripteurs de fichiers, je pense). Et vous pouvez également inclure le fait que vous devez dépenser les 384 de ces octets avec select si vous voulez seulement surveiller un descripteur de fichier mais sa valeur est 1024, alors qu’avec epoll vous ne dépenserez que 20 octets. Pourtant, tous ces chiffres sont assez petits, donc cela ne fait pas beaucoup de différence.

Et il y a aussi cet autre avantage de epoll, que vous connaissez peut-être déjà, à savoir qu’il n’est pas limité aux descripteurs de fichiers FD_SETSIZE. Vous pouvez l’utiliser pour surveiller autant de descripteurs de fichiers que vous en avez. Et si vous ne possédez qu’un seul descripteur de fichier, mais que sa valeur est supérieure à FD_SETSIZE, epoll fonctionne également avec select , mais select ne le fait pas.

Au hasard, j’ai récemment découvert un léger inconvénient à epoll par rapport à select ou poll . Bien qu’aucune de ces trois API ne prenne en charge les fichiers normaux (c.-à-d. Les fichiers sur un système de fichiers), select and poll présente ce manque de support en rapportant des descripteurs toujours lisibles et toujours inscriptibles. Cela les rend impropres à tout type significatif d’E / S de système de fichiers non bloquant, un programme qui utilise select ou poll et qui rencontre un descripteur de fichier du système de fichiers continuera au moins à fonctionner (ou s’il échoue, il ne le fera pas). être à cause de select ou de poll ), mais peut-être pas avec la meilleure performance.

D’autre part, epoll échouera rapidement avec une erreur (apparemment avec EPERM ) lorsqu’on lui demandera de surveiller un tel descripteur de fichier. Ssortingctement parlant, ceci est à peine correct. Cela ne fait que signaler son manque de soutien de manière explicite. Normalement, j’applaudirais des conditions d’échec explicites, mais celle-ci n’est pas documentée (pour autant que je sache) et aboutisse à une application complètement cassée, plutôt qu’à une application qui fonctionne avec des performances potentiellement dégradées.

En pratique, le seul endroit où j’ai vu cela est l’interaction avec stdio. Un utilisateur peut redirect stdin ou stdout depuis / vers un fichier normal. Alors que précédemment stdin et stdout auraient été un tube – supporté par epoll très bien – il devient alors un fichier normal et epoll échoue bruyamment, brisant l’application.

Dans les tests de mon entreprise, un problème avec epoll () est apparu, donc un coût unique par rapport à la sélection.

Lors de la tentative de lecture du réseau avec un délai d’attente, la création d’un epoll_fd (au lieu d’un FD_SET) et l’ajout de la fd à epoll_fd est beaucoup plus coûteuse que la création d’un FD_SET (qui est un malloc simple).

Comme dans la réponse précédente, lorsque le nombre de FD dans le processus augmente, le coût de select () augmente, mais dans nos tests, même avec des valeurs fd dans les 10 000, select était toujours gagnant. Il existe des cas où un thread attend un seul fichier fd, et essaie simplement de surmonter le fait que la lecture réseau et l’écriture réseau n’expire pas lorsqu’il utilise un modèle de thread bloquant. Bien entendu, les modèles de threads bloquants sont peu performants par rapport aux systèmes de réacteurs non bloquants, mais il est parfois nécessaire, pour les intégrer à une base de code héritée particulière.

Ce type de cas d’utilisation est rare dans les applications hautes performances, car un modèle de réacteur n’a pas besoin de créer un nouvel epoll_fd à chaque fois. Pour le modèle où un epoll_fd a une longue durée de vie – ce qui est clairement préférable pour toute conception de serveur haute performance -, epoll est clairement le gagnant à tous les niveaux.