Forum Programmation.c Polling

Posté par  .
Étiquettes : aucune
0
7
oct.
2005
Bonsoir, j'ai encore une question relative au réseau ;)

J'ai testé mon serveur avec siege, et le bousin dépasse difficilement les 100 requêtes par seconde... Tandis qu'un *vrai* serveur genre thttpd, en traite plus de mille sans broncher.

Après quelques expérimentations, j'en suis à réécrire le polling. Avant, il était géré avec un bête select(), mais forcément ça marche moins bien en charge. J'ai testé epoll, c'est vraiment une bonne API, mais en même temps ce n'est pas du tout portable...

J'ai pensé à un compromis qui me parait être "moins pire" que le bête select(), mais qui serait aussi portable.

Avant le pseudocode, le contexte:

- dead et alive, deux FIFO. 'dead' contient les sockets qui ont causé une erreur EWOULDBLOCK, 'alive' les sockets prêts à l'emploi.

- L'insertion d'un socket dans dead se fait via une fonction qui vérifie qu'il est bien mort en lecture/écriture, le remet dans alive autrement.

Voilà le pseudocode du machin en question, pour un thread dédié:

void *socket_polling(void *)
{
    while(control == true)
    {
        /* traite les sockets de la pile 'dead' */
        if ( (item = pop(dead)) != NULL)
        {
            FD_SET(set, item->fd);

            /* verifie que le socket est bien mort */
            select(item->fd, ...);

            if (FD_ISSET(set, item->fd))
            {
                /* il simulait, au boulot fainéant */
                push(alive, item);
            }
            else
            {
                /* bel et bien mort (pour le moment) */
                push(dead, item);
            }
        }
    }

    return NULL;
}


Ce travail également pourrait être avantageusement réparti entre différents thread travailleurs: chacun récupère un socket sur la pile dead, s'il est prêt tant mieux, sinon il est rejeté dans la pile et un socket fiable est récupéré sur la pile alive.

Question subsidiaire: il existe une ioctl bien commode pour savoir s'il y a de la lecture sur un socket, FIONREAD, qui est assez portable (même Windows l'a). Y a-t-il un équivalent pour l'écriture ? J'ai eu vent de FIONSPACE ou FIONWRITE, mais elles n'ont pas l'air très portables... Ça permettrait de complètement supprimer select().

Je vous remercie d'avance pour vos réponses :)
  • # Pas clair

    Posté par  . Évalué à 2.

    La description de ton problème n'est clair du tout:

    1. tu dis que le _bête_ polling qui est basé sur select n'est pas très efficace
    2. tu dis que tu réimplémentes
    3. tu montres un pseudo-code de réimplémentation ... qui utilise select !

    Le problème ne vient donc sans douet pas du select mais de l'utilisation que tu en fais. Dans ta première implémentation, as tu identifié que qui ne te permet pas de monter en charge (opérations bloquantes, charge CPU, autre) ?
    • [^] # Re: Pas clair

      Posté par  . Évalué à 1.

      En fait, j'utilisais select() sur un tableau de sockets précédemment... la méthode classique. Donc plus j'avais de connections, plus l'appel lui-même (et ensuite le scan avec FD_ISSET()) était long.

      Là en fait, select() est utilisé sur un seul socket (et pourrait parfaitement être remplacé par ioctl(FIONREAD) et un équivalent en écriture, malheureusement non portable).
      • [^] # Re: Pas clair

        Posté par  . Évalué à 3.

        Et tu crois que 1 appel de select sur n socket est plus long que n appels de select sur 1 socket ?
        Je crois que tu devrais plutôt revoir la conception de tout ce qui entoure cet appel à select. L'utilisation de threads peut simplifier la programmation mais ne va pas intrinsèquement augmenter la vélocité de l'éxécution.
        • [^] # Re: Pas clair

          Posté par  . Évalué à 1.

          Effectivement, ça mettra le même temps dans l'absolu (voire plus, avec l'overhead dû aux push()/pop() et aux context switches), mais en "fragmentant" le select() j'espère gagner en "répondant". En gros, au lieu d'attendre le résultat d'un gros select() pendant n secondes et de le dispatcher aux travailleurs, là les évènements devraient être distribués aux travailleurs en flux continu tout au long de ces n secondes.

          Cependant, j'ai l'impression que ma démarche est un peu naïve, et j'aimerais avoir des avis sur une utilisation de select() qui soit "scalable". En interrogeant google, j'ai vu par exemple que le très performant serveur Zeus (closed source) utilise select() d'une manière optimisée. Mais vu que les sources ne sont pas disponibles, pas moyen de voir comment il procède...

          Bien sûr, "ce qui entoure l'appel à select()" est également sur l'établi, mais j'ai particulièrement besoin de conseils pour l'écriture d'une méthode de polling portable...
          • [^] # Re: Pas clair

            Posté par  . Évalué à 2.

            En gros, au lieu d'attendre le résultat d'un gros select() pendant n secondes et de le dispatcher aux travailleurs, là les évènements devraient être distribués aux travailleurs en flux continu tout au long de ces n secondes.

            Tu n'as pas bien compris comment fonctionne select : la fonction est bloquante *tant qu*''il n'y a pas d'évènements associés à tes sockets.
            Si on se place dans le cas de la lecture, le select rend la main dès q'une des sockets à des données à lire. Tu ne perds donc pas de temps à scruter les sockets une par une s'il n'y a rien à faire, l'OS s'en charge pour toi. Il n'y a donc aucune perte de temps à scruter des choses inutiles. Et crois moi, l'OS fais souvent les choses très bien, souvent mieux que toi (et que moi). Dans le cas des FD_SET et de ses petits frères, ce sont souvent des macros qui font des tests sur des masques de bits, et crois moi c'est bien plus rapide que de lier et de délier des objets dans des listes!

            Si on se cantonne à d'un programme mono-process, outre le fait que ce soit un bonheur à programmer par rapport à un programme multithread (pas de mutex à tout va), la difficulté dans ton cas concerne principalement la gestion de l'écriture, qui doit se faire en mode non bloquant. Là, pas de solution miracle: bien réfléchir !

            A ce propos, je pense que tu pourrais prendre des idées dans ce qui se fait dans le monde objet, et plus précisément en ce qui concerne les patterns 'proactor' et 'reactor'. Ces patterns sont dédiés au démultiplexage et à la distribution d'évènements
            • [^] # Re: Pas clair

              Posté par  . Évalué à 1.

              http://www.atnf.csiro.au/people/rgooch/linux/docs/io-events.html(...)

              Héhé, certes select() retournera avant le timeout s'il y a de l'activité sur les sockets... Mais sur un grand nombre de sockets, elle est quand même pénalisante car:

              - le kernel scanne tous les sockets du FD_SET avant de retourner, il ne retourne pas dès qu'un socket a été trouvé prêt.

              - select() retourne certes le nombre de socket prêts, mais il faut scanner tout le tableau de fds pour les retrouver.

              - le nombre de fds enregistrable dans un set varie suivant l'OS et est parfois très limité (64 fds sous Windows...). L'implémentation des macros FD_* varie aussi suivant l'OS: sous GNU/Linux, c'est effectivement une affaire de bitmask, donc c'est rapide, mais sous Windows, chaque appel à FD_ISSET() scanne tout le tableau...

              Le gros avantage de l'API epoll, c'est qu'on enregistre son fd et sa structure associée avec epoll_ctl, et qu'on reçoit ensuite directement un (ou plusieurs) fd prêt et sa structure avec epoll_wait. Donc pas de scans, pas de limitation de nombre de fds, et une notification immédiate. Dommage que ça ne soit pas portable !

              J'ai regardé à quoi correspondait les proactor patterns, en diagonale, et ça ressemble furieusement au principe des I/Os asynchrones couplées avec un mécanisme de notification à la completion... Genre le hack SIGIO pour GNU/Linux ou les I/O completion ports sous Windows NT. C'est très puissant, mais pas portable non plus :) Quant au reactor, ça semble plus proche du principe d'epoll, select ou poll, avec des I/Os synchrones.

              Donc en fait, j'aimerais arriver à avoir un truc proche de epoll en utilisant select() (ou autre chose de portable), mais je me demande si c'est très faisable...
              • [^] # Polling?

                Posté par  . Évalué à 1.

                Bonjour
                Ce n'est pas la première fois que j'entends ce terme dans un contexte réseau. Quelle signification a t-il au juste? ça veut dire quoi?
                MErci
                • [^] # Re: Polling?

                  Posté par  . Évalué à 1.

                  Le terme 'polling' n'est pas limité à la programmation réseau, il désigne en gros l'action de surveiller une ressource (souvent un descripteur de fichier) pour déterminer si elle est prête à être utilisée.

                  Après, il y a plein de méthodes différentes pour ce faire :)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.