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.