📜  远程直接内存访问(RDMA)(1)

📅  最后修改于: 2023-12-03 14:57:57.470000             🧑  作者: Mango

远程直接内存访问(RDMA)

远程直接内存访问(RDMA)是一种网络协议,在高性能计算(HPC)环境中被广泛使用,以实现高吞吐量和低延迟的数据传输。RDMA使得计算机节点可以在不涉及CPU的情况下直接访问远程计算机节点的内存,从而避免了数据传输过程中CPU的介入,提高了数据传输的效率。

RDMA的实现依赖于底层网络技术,如InfiniBand、RoCE和iWARP等,这些技术提供了低延迟和高速的网络通信方式,从而实现了RDMA协议。

RDMA的优点

RDMA技术与传统TCP/IP协议的数据传输方式相比有如下优点:

  • 高吞吐量:RDMA实现了零拷贝技术,减少了数据传输的次数,提高了数据传输的效率,因此具有优异的吞吐量;
  • 低延迟:RDMA的数据传输过程中避免了CPU的介入,从而减少了数据传输的延迟;
  • 高并发:RDMA支持高并发的数据传输方式,从而提高了网络吞吐量;
  • 降低CPU负载:RDMA采用零拷贝技术,不需要CPU参与数据传输,因此降低了CPU的负载,提高了CPU的可用性。
RDMA的应用

RDMA技术在数据中心、云计算和HPC等领域得到广泛应用。以下是RDMA技术的主要应用场景:

  • 数据库:RDMA可以提高数据库的读写性能,加速数据库的读写操作;
  • 存储:RDMA可以实现快速、高效的远程文件共享,提高存储的性能;
  • 分布式计算:RDMA可以实现高性能计算集群的互联,提高数据传输的效率;
  • 云计算:RDMA可以提高云计算平台的网络通信速度,降低云计算平台的延迟;
  • 视频传输:RDMA可以实现高清视频的远程传输,提高视频播放的质量。
RDMA编程模型

RDMA编程模型分为两种:

  • 基于RDMA通信涉及的RDMA驱动和RDMA硬件。这种模型需要了解RDMA硬件和驱动的细节,以及RDMA API的使用方式,从而实现高效的数据传输。

  • 基于Verbs API的RDMA编程。RDMA Verbs API是一个抽象层,隐藏了底层的RDMA设备和驱动细节,简化了程序员的开发,提高了开发效率。在这种模型下,程序员只需要了解Verbs API的使用方式即可进行RDMA编程。

RDMA内核支持

RDMA技术需要操作系统的支持,Linux内核从2.6.27版本开始支持RDMA技术。用户可以通过安装RDMA软件包来使用RDMA技术。

RDMA应用实例

以下是使用RDMA技术进行数据传输的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rdma/rdma_cma.h>

#define BUFFER_SIZE 1024

struct rdma_context {
    struct rdma_cm_id *id;
    struct rdma_event_channel *ec;
    struct ibv_pd *pd;
    struct ibv_cq *cq;
    struct ibv_mr *mr;
    char *buffer;
    unsigned int buffer_size;
    char *remote_buffer;
    unsigned int remote_buffer_size;
};

void register_memory(struct rdma_context *ctx)
{
    ctx->mr = ibv_reg_mr(ctx->pd,
                         ctx->buffer,
                         ctx->buffer_size,
                         IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);
    if (!ctx->mr) {
        fprintf(stderr, "ibv_reg_mr failed\n");
        exit(EXIT_FAILURE);
    }
}

void build_context(struct rdma_context *ctx)
{
    memset(ctx, 0, sizeof(*ctx));
    ctx->buffer_size = BUFFER_SIZE;
    ctx->buffer = malloc(ctx->buffer_size);
    if (!ctx->buffer) {
        fprintf(stderr, "failed to allocate buffer\n");
        exit(EXIT_FAILURE);
    }
    register_memory(ctx);
    ctx->remote_buffer_size = BUFFER_SIZE;
    ctx->remote_buffer = malloc(ctx->remote_buffer_size);
    if (!ctx->remote_buffer) {
        fprintf(stderr, "failed to allocate remote buffer\n");
        exit(EXIT_FAILURE);
    }
}

void destroy_context(struct rdma_context *ctx)
{
    ibv_dereg_mr(ctx->mr);
    free(ctx->buffer);
    free(ctx->remote_buffer);
    rdma_destroy_event_channel(ctx->ec);
    rdma_destroy_id(ctx->id);
}

void on_connect(void *context)
{
    struct rdma_cm_id *id = (struct rdma_cm_id *)context;
    struct rdma_context *ctx = (struct rdma_context *)id->context;
    struct ibv_send_wr wr, *bad_wr = NULL;
    struct ibv_sge sge;
    memset(&wr, 0, sizeof(wr));
    wr.opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
    wr.send_flags = IBV_SEND_SIGNALED;
    wr.imm_data = 0;
    wr.wr_id = 1;
    wr.wr.rdma.remote_addr = (uint64_t)ctx->remote_buffer;
    wr.wr.rdma.rkey = id->context->recv_buf[2].imm_data;
    sge.addr = (uint64_t)ctx->buffer;
    sge.length = BUFFER_SIZE;
    sge.lkey = ctx->mr->lkey;
    wr.sg_list = &sge;
    wr.num_sge = 1;
    if (ibv_post_send(ctx->id->qp, &wr, &bad_wr)) {
        fprintf(stderr, "ibv_post_send failed\n");
        exit(EXIT_FAILURE);
    }
}

void on_completion(struct ibv_wc *wc)
{
    printf("on completion: status=%d, opcode=%d\n", wc->status, wc->opcode);
}

void build_connection(struct rdma_context *ctx)
{
    struct rdma_conn_param conn_param;
    struct rdma_cm_event *event;
    struct rdma_cm_id *id;
    struct rdma_conn_param conn_param;
    struct rdma_cm_event *event;
    memset(&conn_param, 0, sizeof(conn_param));
    conn_param.responder_resources = 1;
    conn_param.initiator_depth = 1;
    conn_param.retry_count = 3;
    rdma_create_event_channel(&ctx->ec);
    rdma_create_id(ctx->ec, &ctx->id, ctx, RDMA_PS_TCP);
    rdma_bind_addr(ctx->id, NULL);
    rdma_listen(ctx->id, 10);
    rdma_get_cm_event(ctx->ec, &event);
    rdma_ack_cm_event(event);
    rdma_get_cm_event(ctx->ec, &event);
    if (event->event != RDMA_CM_EVENT_CONNECT_REQUEST) {
        fprintf(stderr, "unexpected event: %s\n", rdma_event_str(event->event));
        exit(EXIT_FAILURE);
    }
    id = event->id;
    rdma_ack_cm_event(event);
    memcpy(&ctx->remote_buffer_size, id->context->recv_buf, sizeof(ctx->remote_buffer_size));
    register_memory(ctx);
    conn_param.private_data = &ctx->remote_buffer_size;
    conn_param.private_data_len = sizeof(ctx->remote_buffer_size);
    rdma_accept(id, &conn_param);
    rdma_get_cm_event(ctx->ec, &event);
    if (event->event != RDMA_CM_EVENT_ESTABLISHED) {
        fprintf(stderr, "unexpected event: %s\n", rdma_event_str(event->event));
        exit(EXIT_FAILURE);
    }
    rdma_ack_cm_event(event);
}

int main(int argc, char **argv)
{
    struct rdma_context ctx;
    build_context(&ctx);
    build_connection(&ctx);
    on_connect(ctx.id);
    while (1) {
        struct ibv_cq *cq;
        struct ibv_wc wc;
        int ne;
        ne = ibv_poll_cq(ctx.cq, 1, &wc);
        if (ne < 0) {
            fprintf(stderr, "ibv_poll_cq failed\n");
            exit(EXIT_FAILURE);
        } else if (ne > 0) {
            if (wc.status != IBV_WC_SUCCESS) {
                fprintf(stderr, "completion with error: %s\n", ibv_wc_status_str(wc.status));
            } else {
                on_completion(&wc);
            }
        } else {
            /* no work queued, wait for next event */
        }
    }
    destroy_context(&ctx);
    return 0;
}
总结

RDMA技术是一种具有优异性能的网络传输协议,能够提高数据传输的吞吐量、降低延迟和CPU负载。在高性能计算、HPC、云计算等领域得到广泛应用。RDMA编程需要了解RDMA硬件和驱动细节、RDMA API的使用方式和Verbs API的使用方式。在Linux内核中,从2.6.27版本开始支持RDMA技术。RDMA应用实例包括数据库、存储、分布式计算、云计算和视频传输等场景。