Question

J'écris un serveur d'applications qui traite des images (grandes données). J'essaie de minimiser les copies lors de l'envoi de données d'image aux clients. Les images traitées que je dois envoyer aux clients sont des tampons obtenus à partir de Jemalloc. La manière dont j'ai pensé que l'envoi des données au client est la suivante:

1) Appel d'écriture simple.

// Allocate buffer buf.
// Store image data in this buffer.
write(socket, buf, len);

2) J'obtient le tampon à travers le MMAP au lieu de JemalLoc, bien que je présume que JemalLoc crée déjà le tampon en utilisant du MMAP. Je fais ensuite un appel simple à écrire.

buf = mmap(file, len);  // Imagine proper options.
// Store image data in this buffer.
write(socket, buf, len);

3) J'ai obtenu un tampon à travers le mime comme avant. J'utilise ensuite SendFile pour envoyer les données:

buf = mmap(in_fd, len);  // Imagine proper options.
// Store image data in this buffer.
int rc;
rc = sendfile(out_fd, file, &offset, count);
// Deal with rc.

On dirait que (1) et (2) fera probablement la même chose que Jemalloc attribue probablement une mémoire à travers le MMAP en premier lieu. Je ne suis pas sûr de (3) cependant. Cela conduira-t-il vraiment à des avantages? La figure 4 sur ce Article sur les méthodes de copie zéro Linux suggère qu'un autre La copie peut être évitée à l'aide de SendFile:

Aucune donnée n'est copiée dans le tampon de la prise. Au lieu de cela, seuls des descripteurs avec des informations sur la localisation et la longueur des données sont annexé au tampon de prise. Le moteur DMA transmet directement des données du tampon de noyau au moteur du protocole, éliminant ainsi le Copie finale restante.

Cela semble être une victoire si tout fonctionne. Je ne sais pas si mon tampon de lamapé compte comme un tampon de noyau cependant. De plus, je ne sais pas quand il est prudent de réutiliser ce tampon. Étant donné que la FD et la longueur sont la seule chose à ajouter au tampon de prise, je suppose que le noyau écrit réellement ces données sur la prise de manière asynchrone. Si cela fait, quel est le retour de SendFile signifier? Comment savoir quand reputiliser ce tampon?

Alors mes questions sont:

  1. Quel est le moyen le plus rapide d'écrire de gros tampons (images dans mon cas) à une prise? Les images sont maintenues en mémoire.
  2. Est-ce une bonne idée d'appeler SendFile sur un fichier mmpped? Si oui, quels sont les gothas? Cela mène-t-il même à des victoires?
Était-ce utile?

La solution

On dirait que mes soupçons étaient corrects. J'ai reçu mes informations à partir de ce article . Citant de celui-ci:

Aussi ces appels de système d'écriture réseau, y compris SendFile, pourraient et Dans de nombreux cas, revenez avant que les données soient envoyées sur TCP par la méthode appel a été reconnu. Ces méthodes reviennent dès que toutes les données est écrit dans les tampons de prise (SK Buff) et est poussé au TCP Ecrivez la file d'attente, le moteur TCP peut gérer seul de ce point. Dans Autres mots à l'époque SendFile Retourne la dernière fenêtre Envoyer TCP est non réellement envoyé à l'hôte distant mais en file d'attente. Dans les cas où DMA DCART-RECHERCHE DMA est supporté Il n'y a pas de tampon séparé qui contient ces octets, plutôt que les tampons (SK Buffs) détiennent simplement des pointeurs pour Les pages du cache tampon OS, où se trouve le contenu du fichier. Cela pourrait conduire à une condition de course si nous modifions le contenu de la fichier correspondant aux données de la dernière fenêtre d'envoi de TCP dès que SendFile est retourné. En conséquence, le moteur TCP peut envoyer un nouvellement écrit données à l'hôte distant au lieu de ce que nous voulions initialement Envoyer.

À condition que le tampon à partir d'un fichier mmpped est même considéré comme "DMA-capable", semble qu'il n'existe aucun moyen de savoir s'il est prudent de la réutiliser sans accusé de réception explicite (sur le réseau) du client. Je serais peut-être à coller à des appels d'écriture simples et d'inciter la copie supplémentaire. Il y a un papier (également de l'article) avec plus de détails.

edit : ce article sur l'appel de l'épissure indique également les problèmes. Citer:

Soyez conscient, lors de l'épissage des données d'un tampon Mpl'ed à un réseau Prise, il n'est pas possible de dire que toutes les données ont été envoyées. Même si Retours de l'épissure (), la pile de réseau n'a peut-être pas encore envoyé toutes les données. Donc Réutiliser le tampon peut remplacer les données non séduites.

Autres conseils

Pour les cas 1 et 2 - L'opération que vous avez marquée comme // stocke les données d'image dans ce tampon nécessite une conversion? Est-ce juste une copie simple de la mémoire à buf?

Si c'est juste une copie simple, vous pouvez utiliser écrire directement sur le pointeur obtenu à partir de Jemalloc.

En supposant que IMG est un pointeur obtenu à partir de JemalLoc et de taille est une taille de votre image, il suffit d'exécuter le code suivant:

int result;
int sent=0;
while(sent<size) {
    result=write(socket,img+sent,size-sent);
    if(result<0) {
        /* error handling here */
        break;
    }
    sent+=result;
}

Il fonctionne correctement pour bloquer les E / S (le comportement par défaut). Si vous avez besoin d'écrire des données de manière non bloquante, vous devriez pouvoir retravailler le code par vous-même, mais vous avez maintenant l'idée.

Pour le cas 3 - SendFile est destiné à envoyer des données d'un descripteur à un autre. Cela signifie que vous pouvez, par exemple, envoyer des données de fichier directement à la prise TCP et vous n'avez pas besoin d'allouer de tampon supplémentaire. Donc, si l'image que vous souhaitez envoyer à un client est dans un fichier, allez simplement pour un envoi. Si vous l'avez en mémoire (parce que vous l'avez traitée en quelque sorte, ou simplement générer), utilisez l'approche que j'ai mentionnée précédemment.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top