Pour un très petit nombre de sockets (cela 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 termes d'utilisation de la mémoire et de 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 select et epoll sont mis à l'échelle de manière linéaire. Une grande différence, cependant, est que les API orientées vers l'espace utilisateur présentent des complexités basées sur des choses différentes. Le coût d'un select
appel correspond à peu près à la valeur du descripteur de fichier numéroté le plus élevé que vous lui transmettez. Si vous sélectionnez sur un seul fd, 100, alors c'est à peu près deux fois plus cher que de sélectionner sur un seul fd, 50. Ajouter plus de fds en dessous du plus élevé n'est pas tout à fait gratuit, donc c'est un peu plus compliqué que cela en pratique, mais cela est une bonne première approximation pour la plupart des implémentations.
Le coût d'epoll est plus proche du nombre de descripteurs de fichiers qui contiennent réellement des événements. Si vous surveillez 200 descripteurs de fichiers, mais que seuls 100 d'entre eux ont des événements, vous ne payez (très grossièrement) que pour ces 100 descripteurs de fichiers actifs. C'est là qu'epoll a tendance à offrir l'un de ses principaux avantages par rapport à select. Si vous avez un millier de clients pour la plupart inactifs, lorsque vous utilisez select, vous payez toujours pour les mille d'entre eux. 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 qu'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 compliqué. select
parvient à représenter toutes les informations nécessaires de manière très compacte (un bit par descripteur de fichier). Et la limitation FD_SETSIZE (généralement 1024) sur le nombre de descripteurs de fichiers que vous pouvez utiliser select
signifie que vous ne dépenserez jamais plus de 128 octets pour chacun des trois ensembles de fd que vous pouvez utiliser avecselect
(lecture, écriture, exception). Comparé à ces 384 octets maximum, epoll est une sorte de porc. Chaque descripteur de fichier est représenté par une structure multi-octets. Cependant, en termes absolus, cela n'utilisera toujours pas beaucoup de mémoire. Vous pouvez représenter un grand nombre de descripteurs de fichiers dans quelques dizaines de kilo-octets (environ 20k pour 1000 descripteurs de fichiers, je pense). Et vous pouvez également ajouter le fait que vous devez dépenser les 384 octets avec select
si vous ne voulez surveiller qu'un seul descripteur de fichier mais que sa valeur se trouve être de 1024, alors qu'avec epoll vous ne dépenseriez 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 d'epoll, dont vous êtes peut-être déjà conscient, qu'il n'est pas limité aux descripteurs de fichier FD_SETSIZE. Vous pouvez l'utiliser pour surveiller autant de descripteurs de fichiers que vous en avez. Et si vous n'avez qu'un seul descripteur de fichier, mais que sa valeur est supérieure à FD_SETSIZE, epoll fonctionne également avec cela, mais select
pas.
Au hasard, j'ai également découvert récemment 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'est-à-dire les fichiers sur un système de fichiers), select
et poll
présente ce manque de support comme rapportant de tels descripteurs comme toujours lisibles et toujours inscriptibles. Cela les rend inadaptés à tout type significatif d'E / S de système de fichiers non bloquant, un programme qui utilise select
ou poll
et rencontre un descripteur de fichier du système de fichiers continuera au moins à fonctionner (ou s'il échoue, ce ne sera pas parce que de select
ou poll
), mais il peut - être pas la meilleure performance.
D'autre part, epoll
échouera rapidement avec une erreur ( EPERM
, apparemment) lorsqu'on lui demande de surveiller un tel descripteur de fichier. À proprement parler, ce n'est guère incorrect. Cela signale simplement son manque de soutien de manière explicite. Normalement, j'applaudirais les conditions d'échec explicites, mais celle-ci est non documentée (pour autant que je sache) et entraîne une application complètement cassée, plutôt qu'une application qui fonctionne simplement avec des performances potentiellement dégradées.
En pratique, le seul endroit où j'ai vu cela se produire est lors de l'interaction avec stdio. Un utilisateur peut rediriger 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, cassant l'application.
poll
pour être complet?man select
) Le noyau Linux impose limite pas fixe, mais les marques de mise en œuvre de la glibc fd_set un type de taille fixe, avec FD_SETSIZE définie comme 1024, et les macros FD _ * () fonctionnant selon cette limite. Pour surveiller les descripteurs de fichiers supérieurs à 1023, utilisez plutôt poll (2). Sur CentOS 7, j'ai déjà rencontré des problèmes où mon propre code a échoué à select () car le noyau a renvoyé un descripteur de fichier> 1023 et je suis actuellement à la recherche d'un problème qui sent qu'il peut être Twisted et que je rencontre le même problème.Lors de tests dans mon entreprise, un problème avec epoll () est survenu, donc un coût unique par rapport à select.
Lorsque vous essayez de lire depuis le réseau avec un délai d'expiration, créer un epoll_fd (au lieu d'un FD_SET) et ajouter le fd à epoll_fd, est beaucoup plus coûteux que de créer un FD_SET (qui est un simple malloc).
Selon la réponse précédente, à mesure que le nombre de FD dans le processus devient important, le coût de select () devient plus élevé, mais dans nos tests, même avec des valeurs fd dans les 10000, select était toujours un gagnant. Ce sont des cas où il n'y a qu'un seul fd qu'un thread attend, et essayant simplement de surmonter le fait que la lecture réseau et l'écriture réseau ne dépassent pas le délai lors de l'utilisation d'un modèle de thread bloquant. Bien sûr, les modèles de filetage bloquant sont peu performants par rapport aux systèmes de réacteurs non bloquants, mais il y a des occasions où, pour s'intégrer à une base de code héritée particulière, il est nécessaire.
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 nouveau 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éré pour toute conception de serveur haute performance - epoll est clairement le gagnant à tous égards.
la source
select()
si vous avez des valeurs de descripteur de fichier dans la gamme 10k + - à moins que vous ne recompiliez la moitié de votre système pour changer FD_SETSIZE - donc je me demande comment cette stratégie a fonctionné. Pour le scénario que vous avez décrit, je regarderais probablement ce qui ressemblepoll()
beaucoup plus à ceselect()
que c'estepoll()
- mais supprime la limitation FD_SETSIZE.FD_SETSIZE
est une constante de temps de compilation définie lorsque votre bibliothèque C est compilée. Si vous la définissez avec une valeur différente lorsque vous créez votre application, votre application et la bibliothèque C ne seront pas d'accord et les choses iront mal. Si vous avez des références prétendant qu'il est sûr de redéfinir,FD_SETSIZE
je serais intéressé de les voir.