5 votes

Infiniband rdma mauvaise bande passante de transfert

Dans mon application, j'utilise une infrastructure Infiniband pour envoyer un flux de données d'un serveur à un autre. J'ai utilisé IP sur Infiniband pour faciliter le développement car je suis plus familier avec la programmation de sockets. Jusqu'à présent, les performances (bande passante max) me convenaient (je savais que je n'atteignais pas la bande passante maximale possible), maintenant j'ai besoin d'obtenir plus de bande passante à partir de cette connexion Infiniband.

ib_write_bw affirme que ma bande passante maximale atteignable est d'environ 1500 Mo/s (je n'obtiens pas 3000 Mo/s car ma carte est installée dans un PCI 2.0 8x).

Jusque-là tout va bien. J'ai codé mon canal de communication en utilisant ibverbs et rdma, mais j'obtiens bien moins de bande passante que ce que je pourrais obtenir, j'obtiens même un peu moins de bande passante qu'avec les sockets mais au moins mon application ne consomme pas de puissance CPU :

ib_write_bw : 1500 Mo/s

sockets : 700 Mo/s <= Un cœur de mon système est à 100% pendant ce test

ibvers+rdma : 600 Mo/s <= Aucun CPU n'est utilisé du tout pendant ce test

Il semble que le goulot d'étranglement se situe ici :

ibv_sge sge;
sge.addr = (uintptr_t) mémoire_à_transférer;
sge.length = taille_mémoire_à_transférer;
sge.lkey = memory_to_transfer_mr->lkey;

ibv_send_wr wr;
memset(&wr, 0, sizeof(wr));
wr.wr_id = 0;
wr.opcode = IBV_WR_RDMA_WRITE;
wr.sg_list = &sge;
wr.num_sge = 1;
wr.send_flags = IBV_SEND_SIGNALED;
wr.wr.rdma.remote_addr = (uintptr_t) thePeerMemoryRegion.addr;
wr.wr.rdma.rkey = thePeerMemoryRegion.rkey;

ibv_send_wr *bad_wr = NULL;
if (ibv_post_send(theCommunicationIdentifier->qp, &wr, &bad_wr) != 0) {
  notifyError("Impossible d'envoyer ibv post receive");
}

à ce stade, le code suivant attend la complétion qui est :

//Attendre la complétion
ibv_cq *cq;
void* cq_context;
if (ibv_get_cq_event(theCompletionEventChannel, &cq, &cq_context) != 0) {
  notifyError("Impossible d'obtenir un événement ibv cq");
}

ibv_ack_cq_events(cq, 1);

if (ibv_req_notify_cq(cq, 0) != 0) {
  notifyError("Impossible d'obtenir une notification de requête");
}

ibv_wc wc;
int myRet = ibv_poll_cq(cq, 1, &wc);
if (myRet > 1) {
  LOG(WARNING) << "Plus d'un ibv_wc reçu, un seul était prévu";
}

Le temps entre mon ibv_post_send et quand ibv_get_cq_event renvoie un événement est de 13,3ms lors du transfert de chunks de 8 Mo, ce qui permet d'atteindre environ 600 Mo/s.

Pour être plus précis (en pseudocode de ce que je fais globalement) :

Côté actif :

envoyer un message de réception
connexion rdma
attendre l'événement de connexion rdma
<<à ce stade le flux de transmission tx démarre>>
début :
enregistrer la mémoire contenant les octets à transférer
attendre l'adresse/clé de la région mémoire distante (j'attends un ibv_wc)
envoyer des données avec ibv_post_send
envoyer un message de réception
attendre l'événement ibv_post_send (j'attends un ibv_wc) (cela dure 13,3 ms)
envoyer le message "TERMINÉ"
désenregistrer la mémoire
aller vers le début

Côté passif :

envoyer un message de réception
accepter rdma
attendre l'événement de connexion rdma
<<à ce stade le flux de réception rx démarre>>
début :
enregistrer la mémoire qui doit recevoir les octets
envoyer l'adresse/clé de la mémoire enregistrée
attendre le message "TERMINÉ"
désenregistrer la mémoire
envoyer un message de réception
aller vers le début

Est-ce que quelqu'un sait ce que je fais de travers ? Ou ce que je peux améliorer ? Je ne suis pas affecté par le syndrome du "Pas inventé ici", donc je suis même prêt à jeter ce que j'ai fait jusqu'à présent et à adopter autre chose. J'ai juste besoin d'un transfert contigu point à point.

4voto

Roland Points 3607

En fonction de votre pseudocode, il semble que vous enregistrez et supprimez une région mémoire pour chaque transfert. Je pense que c'est probablement la principale raison pour laquelle les choses sont lentes : l'enregistrement en mémoire est une opération assez coûteuse, vous voulez donc le faire le moins possible et réutiliser votre région mémoire autant que possible. Tout le temps passé à enregistrer la mémoire est du temps que vous ne passez pas à transférer des données.

Cela soulève un deuxième problème avec votre pseudocode : vous attendez de manière synchrone la fin de la requête et ne postez pas une autre demande de travail jusqu'à ce que la précédente se termine. Cela signifie que pendant le laps de temps entre la fin de la demande de travail et la publication d'une autre demande, le HCA est inactif. Vous êtes bien mieux de garder plusieurs demandes de travail d'envoi/réception en cours, de sorte que lorsque le HCA termine une demande de travail, il peut passer immédiatement à la suivante.

2voto

Gaetano Mendola Points 581

J'ai résolu le problème en allouant mes tampons à transmettre alignés sur la taille de la page. Dans mon système, la taille de la page est de 4 Ko (valeur renvoyée par sysconf(_SC_PAGESIZE)). En faisant cela, je suis maintenant capable (je continue toujours à m'enregistrer/désenregistrer) d'atteindre environ 1400 Mo/s.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X