dimanche 28 février 2010

Sockets - Select vs epoll

Je me suis livré à une petite comparaison de la latence de diverses solutions pour écouter sur une liste de sockets. Mon programme de test contient un thread qui mitraille l'interface de loopback avec de petits messages UDP broadcast contenant un timestamp (obtenu avec clock_gettime sur CLOCK_REALTIME, et surtout pas sur CLOCK_PROCESS_CPUTIME_ID qui peut renvoyer des résultats inconsistants depuis des threads différents).

Un autre thread va écouter sur la même interface, décoder le message, et afficher la différence de temps entre le timestamp reçu et l'heure courante.

Cet autre thread est implémenté soit en utilisant la bibliothèque Asio de chez boost, soit un appel à select, soit un epoll. Cet autre thread, en plus d'écouter sur l'interface, va également écouter d'autres sockets qui ne reçoivent rien. L'on a ainsi un socket très occupé, et les autres qui attendent sagement. Voici donc les résultats de latence (en microsecondes) en fonction du nombre de sockets:



Lorsque l'on écoute sur un seul socket, select est le plus rapide, suivi par epoll et asio, bon dernier. Par contre, lorsque l'on veut écouter sur plus d'un socket à la fois, select ne scale pas bien du tout! La raison est que select va regarder tous les descripteurs de fichier entre 0 et le plus élevé de l'appel, et flagger ceux que l'on veut suivre. Epoll (et Asio, qui est basé sur epoll) reposent sur des mécanismes plus évolués, qui permettent de garder une complexité constante au fur et à mesure que le nombre de descripteurs augmente.

5 commentaires:

Socrate a dit…

Alors, si tu veux faire de l'udp, select et epoll, c pas Le plus rapide, car tu n'as pas vraiment de notions de stream en udp. Le mieux, c'est de recevoir tout tes packet sur la meme socket, et de faire le tri dans ton code. Et si tu veux aller encore plus vite je te conseille PF Ring.

M87 a dit…

Puisque j'ai besoin de causer à des ports différents, si je ne veux me baser que sur 1 socket, il me faut abandonner bind, et passer par les sockets raw... J'ai fait un petit essai, en zappant simplement l'en-tête udp ip, et les résultats sont un peu meilleurs: on est à 7 us en mode non bloquant, et à 9 us en mode bloquant. Étrangement, certains paquets sont plus lents que les autres, et mettent jusqu'à 25 us à arriver, alors qu'avec epoll, au moins, l'on était systématiquement entre 12 et 15 us.

Socrates a dit…

7 us, ou 7 ms?

M87 a dit…

7 us. Mais c'est sur l'interface de loopback, à partir du même programme. Ce que je veux mesurer, c'est le coût des divers moyens pour le noyau de me dire qu'il y a quelque chose.

M87 a dit…

Et je m'étais effectivement gourré d'unités dans le post, j'ai tout remis en microsecondes :)