Préparation ou achèvement Utilisation de la mémoire Async IO?

12

Je regardais cet exposé sur l'implémentation d'Async IO dans Rust et Carl mentionne deux modèles potentiels. Préparation et achèvement.

Modèle de préparation:

  • vous dites au noyau que vous voulez lire depuis une socket
  • faire d'autres choses pendant un certain temps…
  • le noyau vous indique quand le socket est prêt
  • vous lisez (remplissez un tampon)
  • faites tout ce dont vous avez besoin
  • libérer le tampon (se produit automatiquement avec Rust)

Modèle d'achèvement:

  • vous allouez un tampon pour le noyau à remplir
  • faire d'autres choses pendant un certain temps…
  • le noyau vous indique quand le tampon a été rempli
  • faites tout ce dont vous avez besoin avec les données
  • libérer le tampon

Dans l'exemple de Carl d'utilisation du modèle de préparation, vous pouvez parcourir les sockets prêts en remplissant et en libérant un tampon global, ce qui donne l'impression qu'il utiliserait beaucoup moins de mémoire.

Maintenant mes hypothèses:

Sous le capot (dans l'espace noyau), lorsqu'un socket est dit "prêt", les données existent déjà. Il est entré dans le socket sur le réseau (ou de n'importe où) et le système d'exploitation conserve les données.

Ce n'est pas comme si cette allocation de mémoire ne se produisait pas comme par magie dans le modèle de préparation. C'est juste que le système d'exploitation l'abstrait de vous. Dans le modèle d'achèvement, le système d'exploitation vous demande d'allouer de la mémoire avant que les données n'affectent réellement et c'est évident ce qui se passe.

Voici ma version modifiée du modèle de préparation:

  • vous dites au noyau que vous voulez lire depuis une socket
  • faire d'autres choses pendant un certain temps…
  • AMENDEMENT: les données arrivent dans le système d'exploitation (une certaine place dans la mémoire du noyau)
  • le noyau vous dit que le socket est prêt
  • vous lisez (remplissez un autre tampon distinct du tampon du noyau ci-dessus (ou vous obtenez un pointeur vers celui-ci?))
  • faites tout ce dont vous avez besoin
  • libérer le tampon (se produit automatiquement avec Rust)

/ Mes hypothèses

Il se trouve que j'aime garder le programme de l'espace utilisateur petit mais je voulais juste des éclaircissements sur ce qui se passe en réalité ici. Je ne vois pas qu'un modèle utiliserait intrinsèquement moins de mémoire ou prendrait en charge un niveau plus élevé d'E / S simultanées. J'adorerais entendre des pensées et des explications plus profondes à ce sujet.

kjs3
la source
Je suis également arrivé ici de cette conférence YouTube. Pour tous ceux qui apprennent comment async IO ou comment implémenter des boucles d'événements, l'équipe de Rust a cette liste de lecture d '"Aysnc Interviews" ici interviewant des gens très bien informés de la communauté
cacoder

Réponses:

5

Dans le modèle de préparation, la consommation de mémoire est proportionnelle à la quantité de données non consommées par l'application.

Dans le modèle d'achèvement, la consommation de mémoire est proportionnelle à la quantité d'appels de socket en attente.

S'il existe de nombreux sockets qui sont pour la plupart inactifs, le modèle de préparation consomme moins de mémoire.

Il existe une solution simple pour le modèle d'achèvement: lancer une lecture de 1 octet. Cela consomme seulement un petit tampon. Lorsque la lecture est terminée, émettez une autre lecture (peut-être synchrone) qui récupère le reste des données.

Dans certaines langues, le modèle d'achèvement est extrêmement simple à mettre en œuvre. Je considère que c'est un bon choix par défaut.

usr
la source
1

Dans le modèle d'achèvement, le système d'exploitation vous demande d'allouer de la mémoire avant que les données n'affectent réellement et c'est évident ce qui se passe.

Mais que se passe-t-il si plus de données arrivent que vous n'en avez alloué d'espace? Le noyau doit encore allouer son propre tampon afin de ne pas supprimer les données. (Par exemple, c'est pourquoi l'astuce de lecture sur 1 octet mentionnée dans la réponse de usr fonctionne.)

Le compromis est que, même si le modèle d'achèvement consomme plus de mémoire, il peut également (parfois) effectuer moins d'opérations de copie, car le fait de conserver le tampon signifie que le matériel peut DMA directement à l'extérieur ou à l'intérieur. Je soupçonne également (mais je suis moins sûr) que le modèle d'achèvement a tendance à effectuer l'opération de copie réelle (lorsqu'elle existe) sur un autre thread, au moins pour l'IOCP de Windows, tandis que le modèle de préparation le fait dans le cadre du non-blocage read()ou write()appel.

rpjohnst
la source