web-dev-qa-db-fra.com

Pourquoi epoll est-il plus rapide que select?

J'ai vu beaucoup de comparaisons qui disent que select doit parcourir la liste fd, et c'est lent. Mais pourquoi epoll n'a-t-il pas à le faire?

51
amazingjxq

Il y a beaucoup de désinformation à ce sujet, mais la vraie raison est la suivante:

Un serveur typique peut traiter, disons, 200 connexions. Il desservira toutes les connexions qui doivent avoir des données écrites ou lues, puis il faudra attendre qu'il y ait plus de travail à faire. Pendant qu'il attend, il doit être interrompu si des données sont reçues sur l'une de ces 200 connexions.

Avec select, le noyau doit ajouter le processus à 200 listes d'attente, une pour chaque connexion. Pour ce faire, il a besoin d'un "thunk" pour attacher le processus à la liste d'attente. Lorsque le processus se réveille enfin, il doit être supprimé des 200 listes d'attente et tous ces thunks doivent être libérés.

En revanche, avec epoll, le socket epoll lui-même a une liste d'attente. Le processus doit être mis sur cette seule liste d'attente en utilisant un seul thunk. Lorsque le processus se réveille, il doit être supprimé d'une seule liste d'attente et un seul thunk doit être libéré.

Pour être clair, avec epoll, le socket epoll lui-même doit être attaché à chacune de ces 200 connexions. Mais cela se fait une fois, pour chaque connexion, lorsqu'elle est acceptée en premier lieu. Et cela est supprimé une fois, pour chaque connexion, lorsqu'il est supprimé. En revanche, chaque appel à select que les blocs doivent ajouter le processus à chaque file d'attente pour chaque socket surveillé.

Ironiquement, avec select, le plus gros coût vient de la vérification si les sockets qui n'ont eu aucune activité ont eu une activité. Avec epoll, il n'est pas nécessaire de vérifier les sockets qui n'ont eu aucune activité car s'ils avaient une activité, ils auraient informé la socket epoll quand cette activité s'est produite. Dans un sens, select interroge chaque socket à chaque fois que vous appelez select pour voir s'il y a une activité tandis que epoll la structure de sorte que l'activité de socket elle-même notifie le processus.

95
David Schwartz

La principale différence entre epoll et select est que dans select() la liste des descripteurs de fichiers à attendre n'existe que pour la durée d'une seule select() appel et la tâche d'appel ne reste dans les files d'attente d'attente des sockets que pendant la durée d'un seul appel. Dans epoll, d'un autre côté, vous créez un descripteur de fichier unique qui agrège les événements de plusieurs autres descripteurs de fichier que vous souhaitez attendre, et donc la liste des fd surveillés est longue et les tâches restent sur le socket attendre des files d'attente sur plusieurs appels système. De plus, puisqu'un epoll fd peut être partagé entre plusieurs tâches, il ne s'agit plus d'une seule tâche dans la file d'attente, mais d'une structure qui elle-même contient une autre file d'attente, contenant tous les processus actuellement en attente sur le epoll fd. (En termes d'implémentation, ceci est résumé par les files d'attente des sockets contenant un pointeur de fonction et un pointeur de données void* Pour passer à cette fonction).

Donc, pour expliquer un peu plus la mécanique:

  1. Un descripteur de fichier epoll a un struct eventpoll Privé qui garde la trace des fd attachés à ce fd. struct eventpoll Possède également une file d'attente qui conserve la trace de tous les processus qui sont actuellement epoll_wait Sur ce fd. struct epoll Possède également une liste de tous les descripteurs de fichiers actuellement disponibles en lecture ou en écriture.
  2. Lorsque vous ajoutez un descripteur de fichier à un epoll fd à l'aide de epoll_ctl(), epoll ajoute le struct eventpoll À la file d'attente de ce fd. Il vérifie également si le fd est actuellement prêt pour le traitement et l'ajoute à la liste des fichiers prêts, le cas échéant.
  3. Lorsque vous attendez un epoll fd à l'aide de epoll_wait, Le noyau vérifie d'abord la liste des éléments prêts et renvoie immédiatement si des descripteurs de fichiers sont déjà prêts. Sinon, il s'ajoute à la file d'attente unique à l'intérieur de struct eventpoll Et se met en veille.
  4. Lorsqu'un événement se produit sur un socket qui est epoll() ed, il appelle le rappel epoll, qui ajoute le descripteur de fichier à la liste des prêts, et réveille également tous les serveurs en attente que struct eventpoll.

De toute évidence, un verrouillage minutieux est nécessaire sur struct eventpoll Et les différentes listes et files d'attente, mais c'est un détail d'implémentation.

La chose importante à noter est qu'à aucun moment ci-dessus, je n'ai décrit une étape qui parcourt tous les descripteurs de fichiers d'intérêt. En étant entièrement basé sur les événements et en utilisant un ensemble durable de fd et une liste prête, epoll peut éviter de prendre O (n) le temps d'une opération, où n est le nombre de descripteurs de fichier surveillé.

20
elite21