Pyverbs 基本教程

RDMA 发布于 Apr 18, 2023 更新于 May 18, 2024

2024-05-18 大概是因为大模型爆火,发现最近这篇文章的点击量在快速上升。说明一下,pyverbs是我23年初在灵均投资实习做RDMA的数据传输时学习的,当时发现pyverbs有一些难以排查的bug,可能和当时使用的IB卡型号和驱动有关,不清楚一年以后有没有改善,如果有顾虑可以选择rdma-core的C-API进行开发。不过通过本文了解Pyverbs的基本概念和用法是足够的。

Pyverbs

Pyverbs 是 Mellanox 开源的 rdma-core 接口封装,提供了一套简单易懂的服务原语。

打开设备

from pyverbs import device


ctx = device.Context(name='mlx5_0')

使用device模块的Context类初始化一个设备上下文,name参数指定设备名称。ctx创建后,即可通过ctx访问设备。

查询设备信息

from pyverbs import device


with device.Context(name='mlx5_0') as ctx:
    attr = ctx.query_device()

这段代码会打印出设备的信息。

查询GID

from pyverbs import device


with device.Context(name='mlx5_0') as ctx:
    gid = ctx.query_gid(port_num=1, index=3)
    print(gid)

这段代码会打印指定端口及index的GID。在基于IB的RDMA概念中,Node及Node的端口都有各自的GID,并且端口可以通过GID表维护多个GID。

查询端口

from pyverbs import device


with device.Context(name='mlx5_0') as ctx:
    port_attr = ctx.query_port(1)

这段代码会打印端口1的信息。

创建RDMA相关对象

Protection Domain

from pyverbs import device
from pyverbs.pd import PD


with device.Context(name='mlx5_0') as ctx:
    pd = PD(ctx)

这段代码创建了一个PD。

Protection Domain是一种集合,内部元素只能与内部元素交互,这些元素可以是AH,QP,MR,SRQ。相当于提供一个RDMA工作的容器。

Memory Region

from pyverbs import device
from pyverbs.pd import PD


with device.Context(name='mlx5_0') as ctx:
    with PD(ctx) as pd:
        flags = e.IBV_ACCESS_LOCAL_WRITE
        mr = MR(pd, mr_len, flags)

这段代码为该设备mlx5_0创建了一个MR。

Memory Region是用于为RDMA存放收发数据的内存区域,MR需要绑定到PD并被注册以供RDMA网卡进行DMA访问。

Memory Window

from pyverbs import enums, device
from pyverbs.pd import PD
from pyverbs.mr import MW


with device.Context(name='mlx5_0') as ctx:
    with PD(ctx) as pd:
        mw = MW(pd, enums.IBV_MW_TYPE_1)

这段代码创建了一个MW。

Memory Window从MR中内存空间分配的内存空间,MW在创建的时候会被绑定到一个已注册的MR上,MW是MR的子集,提供更加灵活的权限控制。

Device Memory

from pyverbs import device
from pyverbs.device import DM, AllocDmAttr


with device.Context(name='mlx5_0') as ctx:
    attr = ctx.query_device_ex()
    if attr.max_dm_size != 0:
        dm_len = random.randint(4, attr.max_dm_size)
        dm_attrs = AllocDmAttr(dm_len)
        dm = DM(ctx, dm_attrs)

这段代码创建了一个DM,使用设备内存。

Device Memory是位于RDMA网卡上的内存,使用DM和使用Host Memory在使用上没有太大区别,使用DM最大的优势是显著降低了包的传输延迟。

Device Memory Memory Region

import random

from pyverbs import enums, device
from pyverbs.device import DM, AllocDmAttr
from pyverbs.mr import DMMR
from pyverbs.pd import PD


with device.Context(name='mlx5_0') as ctx:
    attr = ctx.query_device_ex()
    if attr.max_dm_size != 0:
        dm_len = random.randint(4, attr.max_dm_size)
        dm_attrs = AllocDmAttr(dm_len)
        dm_mr_len = random.randint(4, dm_len)
        with DM(ctx, dm_attrs) as dm:
            with PD(ctx) as pd:
                dm_mr = DMMR(pd, dm_mr_len, enums.IBV_ACCESS_ZERO_BASED, dm=dm,
                             offset=0)

本段代码在设备上创建了一个DMMR。

DMMR与MR是同一级别的概念。区别是MR空间在主机内存中,DMMR空间在RDMA网卡内存中。

Completion Queue

import random

from pyverbs.cq import CompChannel, CQ
from pyverbs as device


with device.Context(name='mlx5_0') as ctx:
    num_cqes = random.randint(0, 200) # Just arbitrary values. Max value can be
                                      # found in device attributes
    comp_vector = 0 # An arbitrary value. comp_vector is limited by the
                    # context's num_comp_vectors
    if random.choice([True, False]):
        with CompChannel(ctx) as cc:
            cq = CQ(ctx, num_cqes, None, cc, comp_vector)
    else:
        cq = CQ(ctx, num_cqes, None, None, comp_vector)
    print(cq)

这段代码创建了一个CQ。

Completion Queue用于接收硬件传输来的CQE,用于从硬件向应用层传递某个WEQ/WR的完成情况。

地址与路由相关概念与接口的分析推迟到完成demo后继续补充。

Queue Pair

from pyverbs.qp import QPCap, QPInitAttr, QPAttr, QP
from pyverbs.addr import GlobalRoute
from pyverbs.addr import AH, AHAttr
from pyverbs import enums, device
from pyverbs.pd import PD
from pyverbs.cq import CQ
import pyverbs.wr as pwr


ctx = device.Context(name='mlx5_0')
pd = PD(ctx)
cq = CQ(ctx, 100, None, None, 0)
cap = QPCap(100, 10, 1, 1, 0)
qia = QPInitAttr(cap=cap, qp_type = enums.IBV_QPT_UD, scq=cq, rcq=cq)
# A UD QP will be in RTS if a QPAttr object is provided
udqp = QP(pd, qia, QPAttr())
port_num = 1
gid_index = 3 # Hard-coded for RoCE v2 interface
gid = ctx.query_gid(port_num, gid_index)
gr = GlobalRoute(dgid=gid, sgid_index=gid_index)
ah_attr = AHAttr(gr=gr, is_global=1, port_num=port_num)
ah = AH(pd, ah_attr)
wr = pwr.SendWR()
wr.set_wr_ud(ah, 0x1101, 0) # in real life, use real values
udqp.post_send(wr)

这段代码创建了一个QP,并且使用post_send原语发送了一个wr。

Queue Pair包含Send Queue和Receive Queue,应用层访问下层的重要接口。

Extended Reliable Connection

拓展可靠连接域,高级特性先不分析

Extended Reliable Connection Domain

拓展可靠连接域,高级特性先不分析

Shared Receive Queue

共享接收队列,高级特性先不分析

标签

Noam Chi

An Innovative Quant Developer. 2018 VEX World Final THINK Award🏆