struct ibv_dm
是 RDMA (远程直接内存访问) 接口中的一个数据结构,用于表示 设备内存(Device Memory, DM)。设备内存是 RDMA 硬件设备的一部分,它允许用户将数据直接存储在 RDMA 设备中,而不是通过主机内存。这种机制可以降低数据传输的延迟并提高性能,尤其是在某些性能敏感的应用场景中。
以下是结构体成员的详细介绍:
struct ibv_dm {
struct ibv_context *context;
int (*memcpy_to_dm)(struct ibv_dm *dm, uint64_t dm_offset,
const void *host_addr, size_t length);
int (*memcpy_from_dm)(void *host_addr, struct ibv_dm *dm,
uint64_t dm_offset, size_t length);
uint32_t comp_mask;
uint32_t handle;
};
- 类型: 指针,指向
struct ibv_context
。 - 作用:
- 表示与 RDMA 设备相关的上下文环境。
- 这是 RDMA 核心数据结构,用于表示与设备的通信接口。
context
中包含了设备的基本信息,以及进行 RDMA 操作时需要用到的资源。
- 类型: 函数指针。
- 作用:
- 将数据从主机内存(
host_addr
)复制到设备内存(dm
)的指定偏移位置(dm_offset
)。 - 参数说明:
struct ibv_dm *dm
:目标设备内存。uint64_t dm_offset
:设备内存中的偏移地址。const void *host_addr
:主机内存的起始地址。size_t length
:要复制的数据长度。
- 返回值:
- 成功返回 0,失败返回非 0 错误码。
- 将数据从主机内存(
- 类型: 函数指针。
- 作用:
- 将数据从设备内存(
dm
)的指定偏移位置(dm_offset
)复制到主机内存(host_addr
)。 - 参数说明:
void *host_addr
:主机内存的目标地址。struct ibv_dm *dm
:源设备内存。uint64_t dm_offset
:设备内存中的偏移地址。size_t length
:要复制的数据长度。
- 返回值:
- 成功返回 0,失败返回非 0 错误码。
- 将数据从设备内存(
- 类型: 无符号 32 位整数。
- 作用:
- 用于扩展设备内存结构的兼容性掩码(Compatibility Mask)。
- 在 RDMA 核心库中,
comp_mask
通常用于指示哪些字段是有效的。 - 通过该掩码可以在向后兼容的情况下扩展数据结构,而不会破坏现有代码的功能。
- 类型: 无符号 32 位整数。
- 作用:
- 表示设备内存的标识符(Handle)。
- 这是 RDMA 核心库使用的内部引用,用于标识特定的设备内存。
- RDMA 操作会通过该句柄与设备内存进行交互。
struct ibv_dm
的主要作用是抽象 RDMA 设备内存的管理功能。它允许用户直接管理设备内存,并提供高效的数据传输机制。
-
设备内存的优势:
- 低延迟: 数据可以直接存储在 RDMA 设备中,避免通过主机内存进行多余的传输。
- 高吞吐: 在某些场景下,直接使用设备内存可以提高数据传输的吞吐量。
- 更高效的 CPU 使用: 避免 CPU 多次拷贝内存。
-
常见场景:
- 传输固定格式的小型控制消息。
- 存储用于数据直接访问的临时数据。
- 优化 GPU-RDMA 的数据路径(如 GPU 内存与设备内存之间的直接通信)。
以下是一个使用 ibv_dm
的简单示例,展示如何将数据从主机复制到设备内存并再复制回来:
#include <infiniband/verbs.h>
#include <stdio.h>
#include <string.h>
int main() {
struct ibv_context *ctx; // RDMA 设备上下文
struct ibv_dm *dm; // 设备内存
const char *data = "Hello RDMA!";
char buffer[128] = {0};
size_t length = strlen(data) + 1;
// 假设已经初始化 RDMA 设备上下文 ctx...
// 分配设备内存
struct ibv_alloc_dm_attr dm_attr = {
.length = 128, // 分配 128 字节的设备内存
};
dm = ibv_alloc_dm(ctx, &dm_attr);
if (!dm) {
perror("Failed to allocate device memory");
return -1;
}
// 将数据从主机内存复制到设备内存
if (dm->memcpy_to_dm(dm, 0, data, length)) {
perror("Failed to copy to device memory");
return -1;
}
// 将数据从设备内存复制回主机内存
if (dm->memcpy_from_dm(buffer, dm, 0, length)) {
perror("Failed to copy from device memory");
return -1;
}
printf("Data from device memory: %s\n", buffer);
// 释放设备内存
ibv_free_dm(dm);
return 0;
}
struct ibv_dm
是 RDMA 编程中管理设备内存的重要工具,其核心功能包括:
- 提供主机和设备内存之间的数据拷贝接口。
- 允许用户将性能敏感的数据存储在设备内存中,减少内存拷贝和传输延迟。
- 提供一个抽象层,使得设备内存的使用更加灵活和高效。
ibv_cq
vs ibv_cq_ex
union {
struct ibv_cq *cq; // 普通 Completion Queue (CQ)
struct ibv_cq_ex *cq_ex; // 扩展 Completion Queue (CQ)
} cq_s;
struct ibv_cq *cq
- 作用: 指向一个普通的 完成队列(Completion Queue, CQ)。
- 用途: 用于普通 RDMA 操作时的工作请求(Work Request, WR)的完成通知。
- 普通 CQ 的功能: 提供基本的完成事件处理,适合较简单的 RDMA 应用场景。
struct ibv_cq_ex *cq_ex
- 作用: 指向一个 扩展完成队列(Extended Completion Queue, CQ)。
- 用途: 用于扩展的 RDMA 操作(更高级功能),例如具有更多元数据的完成通知。
- 扩展 CQ 的功能:
- 提供比普通 CQ 更详细的操作结果信息(例如时间戳等)。
- 支持更复杂的 RDMA 操作场景。
- RDMA 编程中,
ibv_cq
和ibv_cq_ex
分别用于不同的场景:- 如果程序只需要普通 CQ 的功能,使用
ibv_cq
即可。 - 如果程序需要扩展 CQ 的功能,则使用
ibv_cq_ex
。
- 如果程序只需要普通 CQ 的功能,使用
- 由于普通 CQ 和扩展 CQ 是互斥的,一个 RDMA 操作不会同时使用这两种 CQ 类型,所以将它们放在联合体中以节省内存。
cq_s
可以通过访问cq
或cq_ex
来灵活处理两种 CQ 类型,而不需要为每种类型分配独立的空间。
普通 CQ 和扩展 CQ 的区别:
- 普通 CQ(
ibv_cq
): 提供基本的完成事件处理,适合简单的 RDMA 应用。 - 扩展 CQ(
ibv_cq_ex
): 提供更详细的操作结果信息,适合复杂的 RDMA 应用。
应用层 - UDP 应用程序
传输层 - UDP
网络层 - IP/RoCE v2
数据链路层 - 以太网/RoCE v2
物理层 - 物理网络
限制 1:TCP/IP 堆栈处理引入了数十微秒的延迟。当TCP协议栈接收和发送消息时,内核需要进行多次上下文切换,每次消耗5-10微秒。另外,至少三个数据副本和协议工作依赖于 CPU。这意味着仅仅协议处理就会带来几十微秒的固定延迟。协议栈的延迟成为最明显的瓶颈。
(0 -- 1 -- 2 -- 3 -- 4 -- 0)降低 QP 队列对的使用
全连接拓扑:每台机器创建 n-1 个 QP,分别与其余 n-1台机器建立连接。每对机器之间都有直接的 QP ,用于通信。 优点:
- 每台机器可以直接与任意其他机器通信,延迟最低。
- 不依赖中间节点,无需数据转发。 缺点:
- 随着机器数量 nnn 增加,QP 数量呈 O(n2)O(n^2)O(n2) 增长,资源消耗巨大。
- 对于大规模集群,无法扩展。 适用场景 小规模集群(如 n≤16n \leq 16n≤16),对延迟要求极高的场景。