HIXL C++ 数据结构详解:内存描述、传输请求与异步状态机
2026/9/18 2:20:20 网站建设 项目流程

HIXL C++ 数据结构详解:内存描述、传输请求与异步状态机

【免费下载链接】hixlHIXL(Huawei Xfer Library)是一个灵活、高效的昇腾单边通信库,面向集群场景提供简单、可靠、高效的点对点数据传输能力。项目地址: https://gitcode.com/cann/hixl

HIXL(Huawei Xfer Library)是昇腾平台面向集群场景的单边通信库,其 C++ 接口依赖一组精确定义的数据结构完成内存注册、建链、读写传输与异步状态查询。本文以 HIXL C++ API 的数据结构为核心,逐个讲解MemDescTransferOpDescTransferArgsGetTransferStatusArgsNotifyDescFeatureType等类型的字段语义、与对应接口的配合方式及底层实现行为,并结合仓库源码与单元测试给出可验证的依据。读完本文,你将能准确构造 HIXL 各类接口的参数对象,理解异步传输请求从下发、查询到资源释放的完整生命周期,并能在业务代码中正确使用预留字段与能力探测机制。

一、数据结构在 HIXL 中的定位

HIXL 对外暴露的 C++ 接口(构造函数、InitializeRegisterMemTransferSyncTransferAsyncGetTransferStatus等)定义在 include/hixl/hixl.h,而所有接口涉及的数据结构统一声明在头文件 include/hixl/hixl_types.h 的hixl命名空间中。该头文件同时定义了Statusuint32_t)、AscendStringOPTION_*初始化选项常量以及SUCCESSPARAM_INVALID等错误码常量。

从源码结构看,HIXL 的数据结构可归为四类,本文按此组织:

  • 内存描述类MemDescMemHandleMemType——服务于RegisterMem/DeregisterMem
  • 传输请求类TransferOpTransferOpDescTransferArgsTransferReq——服务于TransferSync/TransferAsync
  • 异步状态类TransferStatusGetTransferStatusArgsTransferResultAsyncConnectStatus——服务于GetTransferStatus/GetAsyncConnectStatus
  • 通知与能力类NotifyDescFeatureTypeFEATURE_SUPPORTED/FEATURE_NOT_SUPPORTED——服务于SendNotify/GetNotifies/GetCapability

这些类型在头文件中均以 ABI 稳定为导向设计:绝大多数结构体末尾带有reserved预留数组,便于后续版本在不破坏二进制兼容的前提下扩展字段。

二、内存描述类:MemDesc、MemHandle、MemType

2.1 MemDesc:内存的描述信息

MemDesc用于向 HIXL 描述一块待注册的内存区域,定义如下:

struct MemDesc { uintptr_t addr; size_t len; uint8_t reserved[128] = {}; };
字段类型说明
addruintptr_t内存起始地址。Device 内存通常来自aclrtMalloc的返回指针,Host 内存通常来自aclrtMallocHost(HDK 25.5 之前约束场景)或malloc
lensize_t内存长度,单位字节
reserveduint8_t[128]预留字段,保持结构体 ABI 稳定,使用默认值即可

MemDesc的典型用法出现在 examples/cpp/hixl_example_quickstart.cpp:先用aclrtMalloc申请 Device 内存,再把指针与长度填入MemDesc

ACL_EXIT_ON_FAILURE(aclrtMalloc(&ctx.buf, kBufSize, ACL_MEM_MALLOC_HUGE_ONLY)); ctx.desc.addr = reinterpret_cast<uintptr_t>(ctx.buf); ctx.desc.len = kBufSize;

之后将desc传入RegisterMem完成注册。需要注意的是,接口文档 HIXL-interface.md 对注册内存有明确约束:建议单个 Hixl 实例注册的内存个数不超过 4K;Device 内存建议使用aclrtMalloc申请,若通过 HCCS 传输则内存分配规则需配置为ACL_MEM_MALLOC_HUGE_ONLY;Host 内存按型号不同有 20GB~1TB 的注册上限。

2.2 MemHandle:内存的 Handle

MemHandle是内存注册成功后的句柄类型,用于后续解注册:

using MemHandle = void *;

RegisterMem成功后输出该句柄,DeregisterMem接收该句柄进行解注册。接口文档给出了两个幂等语义:对同一内存区域(相同 addr 和相同 len)重复调用RegisterMem会返回 SUCCESS 并返回与首次注册相同的mem_handle,不会创建新的底层资源;对同一mem_handle重复调用DeregisterMem,第一次正确释放资源,后续调用返回 SUCCESS 但不执行实际操作;传入nullptr则返回PARAM_INVALID

2.3 MemType:内存的类型

enum MemType { MEM_DEVICE, MEM_HOST };
枚举值说明
MEM_DEVICEDevice 侧内存
MEM_HOSTHost 侧内存

MemType决定内存注册的路径。在 quickstart 示例中,两端均注册 Device 内存:

HixlExitOnFailure(ctx.engine.RegisterMem(ctx.desc, MEM_DEVICE, ctx.handle), "RegisterMem");

从仓库测试 tests/cpp/hixl/engine/hixl_engine_uboe_unittest.cc 可以看到,测试用例会分别构造src_mem(Host)、device_mem(Device)等不同MemDesc并配合MEM_HOST/MEM_DEVICE注册,以覆盖不同传输路径。

三、传输请求类:TransferOp、TransferOpDesc、TransferArgs、TransferReq

3.1 TransferOp:传输操作的类型

enum TransferOp { READ, WRITE };
枚举值说明
READ将远端内存读到本地
WRITE将本地内存写到远端

该枚举直接决定TransferSync/TransferAsync的数据方向。quickstart 中 Client 用READ从 Server 读取数据:

HixlExitOnFailure(ctx.engine.TransferSync(kServerEngine, READ, {ctx.op}, kTimeoutMs), "TransferSync");

3.2 TransferOpDesc:传输操作的描述信息

struct TransferOpDesc { uintptr_t local_addr; uintptr_t remote_addr; size_t len; };
字段类型说明
local_addruintptr_t本地内存地址,需在当前 HIXL 注册(或在中转模式下合法)
remote_addruintptr_t远端内存地址,需在远端 HIXL 注册
lensize_t传输长度,单位字节

TransferOpDesc是单次传输的地址描述,而TransferSync/TransferAsync接收std::vector<TransferOpDesc>支持批量传输。quickstart 中通过 socket 交换远端地址后构造该结构体:

ctx.op.local_addr = ctx.desc.addr; ctx.op.remote_addr = remote_addr; ctx.op.len = kBufSize;

接口文档对TransferSync的约束包括:op_desc中的本地内存和远端内存有一个未注册就会判断为需要走中转传输模式;中转传输模式下所有op_desc的传输类型需相同;Fabric Mem 传输模式下系统会根据第一个op_desc的内存类型判定传输方向。TransferAsync当前仅支持直传,暂不支持中转传输。

3.3 TransferArgs:传输操作的可选参数

struct TransferArgs { const void *user_data = nullptr; // 用户自定义信息,需配合获取全部异步传输请求状态接口使用 uint8_t reserved[120] = {}; // 预留参数 };
字段类型说明
user_dataconst void *用户自定义信息,随请求注册,批量查询状态时原样带回(见TransferResult
reserveduint8_t[120]预留参数

user_data是异步传输请求与业务上下文关联的关键通道。从实现看,src/hixl/engine/hixl_engine.cc 在TransferAsync内部会调用client_manager_.RegisterTransferReq(req, client_ptr, optional_args.user_data),将user_data与请求句柄绑定保存;后续通过GetTransferStatus(GetTransferStatusArgs, std::vector<TransferResult>&)批量查询时,TransferResult会携带该user_data原样返回,从而让调用方无需维护req -> 业务对象的映射表。

3.4 TransferReq:传输请求的 Handle

using TransferReq = void *;

TransferReqTransferAsync输出的请求句柄,也是单请求查询接口GetTransferStatus(const TransferReq &req, TransferStatus &status)的输入。其生命周期与异步请求绑定:查询到COMPLETEDFAILED后资源被释放,该场景下不支持再次查询;若用户判断任务超时,需要调用Disconnect销毁链路并清理相关资源。

四、异步状态类:TransferStatus、GetTransferStatusArgs、TransferResult、AsyncConnectStatus

4.1 TransferStatus:异步传输的状态

enum class TransferStatus { WAITING, COMPLETED, TIMEOUT, //暂不支持 FAILED };
枚举值说明
WAITING传输等待中
COMPLETED传输完成
TIMEOUT超时(当前版本暂不支持,预留)
FAILED传输失败

4.2 GetTransferStatusArgs:获取全部异步传输请求状态时的参数

struct GetTransferStatusArgs { uint32_t max_query_count = UINT32_MAX; // 最大查询出的传输请求状态的个数 bool skip_waiting = false; // 查询是否跳过状态为TransferStatus::WAITING的传输请求 uint8_t reserved[123] = {}; // 预留参数 };
字段类型默认值说明
max_query_countuint32_tUINT32_MAX单次最多返回的传输请求状态个数,用于控制批量结果规模
skip_waitingboolfalse是否跳过WAITING状态的请求,便于快速收敛到已完成/失败的结果
reserveduint8_t[123]{}预留参数

接口文档给出了批量查询的调用示例:

GetTransferStatusArgs args = { .max_query_count = 4, .skip_waiting = true }; std::vector<TransferResult> results; Status query_status = client_engine.GetTransferStatus(args, results);

批量查询接口的返回值中有一个值得注意的UNSUPPORTED分支:当 Hixl 初始化 options 未配置 LocalCommRes 的 version 为 1.3,且未配置GlobalResourceConfigcomm_resource_config.protocol_desc包含uboe:deviceub_rtp:device时,不支持通过该接口查询。

4.3 TransferResult:每一个请求的结果信息

struct TransferResult { TransferReq req = nullptr; // 传输请求的Handle const void *user_data = nullptr; // 用户自定义信息 TransferStatus status = TransferStatus::WAITING; // 传输请求状态 uint8_t reserved[108] = {}; // 预留参数 };
字段类型说明
reqTransferReq传输请求句柄,对应TransferAsync的输出
user_dataconst void *下发请求时TransferArgs.user_data中原样带回的用户信息
statusTransferStatus该请求当前的传输状态
reserveduint8_t[108]预留参数

4.4 批量查询的底层行为(源码级验证)

GetTransferStatus(const GetTransferStatusArgs &args, std::vector<TransferResult> &results)的实现在 src/hixl/engine/hixl_engine.cc。从实现看可以确认以下行为:

  • args.max_query_count == 0时直接返回(不产出结果);
  • 对已断链引擎上残留的请求,会以TransferStatus::FAILED产出结果;
  • args.skip_waiting为 true 时,状态为WAITING的请求会被跳过(不放入 results),但仍在内部保留;
  • 每收集一个结果即检查results.size() >= max_query_count,达到上限立即停止,保证批量查询有界;
  • 返回的TransferResult携带requser_data,其中req与内部注册的请求一一对应。

单元测试 tests/cpp/hixl/engine/hixl_engine_unittest.cc 从三个维度验证了上述语义:

  • ReturnsResultsInSubmitOrderWithUserData:结果按提交顺序返回,且user_data原样带回;状态为COMPLETED的请求查询后即释放(GetClientByReq返回 nullptr),WAITING的请求仍保留;
  • SkipWaitingAndMaxQueryCountFilterResultsskip_waiting = truemax_query_count = 1时,只返回第一个非 WAITING 结果;
  • DisconnectedEngineStopsAtMaxQueryCount:引擎断链后查询,请求以FAILED返回且受max_query_count限制。

注意:单请求查询与批量查询在资源释放语义上一致——某请求状态为COMPLETEDFAILED后,相关资源即被释放,再次查询将不再返回该请求状态。

4.5 AsyncConnectStatus:异步建链/拆链的状态

enum class AsyncConnectStatus { NOT_CONNECT, // 未连接 CONNECT_PENDING, // 建链待执行 CONNECTING, // 建链执行中 CONNECTED, // 建链成功 CONNECT_FAILED, // 建链失败 DISCONNECT_PENDING, // 断链待执行 DISCONNECTING // 断链执行中 };
枚举值说明
NOT_CONNECT未连接
CONNECT_PENDING建链任务已入队,待执行
CONNECTING建链执行中
CONNECTED建链成功
CONNECT_FAILED建链失败
DISCONNECT_PENDING断链任务已入队,待执行
DISCONNECTING断链执行中

该枚举用于GetAsyncConnectStatus的两个重载:单查询版本(GetAsyncConnectStatus(const AscendString &remote_engine, AsyncConnectStatus &status))与全量查询版本(GetAsyncConnectStatus(std::map<AscendString, AsyncConnectStatus> &statuses))。接口文档的约束说明指出:接口返回值仅表示调用是否成功,异步建链/断链任务状态由输出参数表示;ConnectAsync/DisconnectAsync不与同步版Connect/Disconnect混用;对同一 remote_engine 下发多个任务时按下发顺序执行,不同 remote_engine 的任务允许并发执行,获取的状态为最新下发任务的状态。

五、通知与能力类:NotifyDesc、FeatureType、能力常量

5.1 NotifyDesc:Notify 的描述信息

struct NotifyDesc { AscendString name; AscendString notify_msg; };
字段类型说明
nameAscendStringNotify 名称,长度上限 1024 字符
notify_msgAscendStringNotify 消息内容,长度上限 1024 字符

NotifyDesc服务于SendNotify/GetNotifies这对接口:Client 通过SendNotify(remote_engine, notify, timeout)向 Server 发送通知;Server 通过GetNotifies(std::vector<NotifyDesc> &notifies)一次性取回并清空已收到的全部 Notify。接口文档给出的示例:

NotifyDesc notify; notify.name = AscendString("cache_ready"); notify.notify_msg = AscendString("block_0"); Status ret = client_engine.SendNotify(remote_engine, notify, 1000);

约束要点:notify.name/notify_msg长度超过 1024 或timeout_in_millis <= 0时返回PARAM_INVALID;每条链路最多存在 4096 条 Notify,需要远端及时调用GetNotifies消费,防止触发上限导致发送失败。单元测试 tests/cpp/hixl/engine/hixl_engine_unittest.cc 中大量用例覆盖了SendNotify成功、超时、GetNotifies取回并清空等路径。

5.2 FeatureType:库能力特性类型

enum FeatureType : int32_t { AUTO_CONNECT = 0, CLIENT_SERVER_COMM = 1, };
枚举值描述
AUTO_CONNECTAuto Connect 模式,对应 Initialize 时 OPTION_AUTO_CONNECT 选项
CLIENT_SERVER_COMMClient/Server 通信模式,即 Server 端监听端口、Client 端发起建链的能力

FeatureType专用于Hixl::GetCapability(FeatureType, int32_t &value)能力探测接口。该枚举有一项硬性约定:枚举值必须显式赋值,新增能力仅允许在末尾扩展,这与 include/hixl/hixl_types.h 头文件中的注释一致,目的是保证新旧库版本间的 ABI 兼容。

5.3 FEATURE_SUPPORTED / FEATURE_NOT_SUPPORTED

constexpr int32_t FEATURE_SUPPORTED = 1; constexpr int32_t FEATURE_NOT_SUPPORTED = 0;

这两个常量是GetCapability输出参数value的取值:1 表示支持,0 表示不支持(含未知特性)。接口文档给出的探测示例:

int32_t value = FEATURE_NOT_SUPPORTED; Status ret = Hixl::GetCapability(AUTO_CONNECT, value); bool supports_auto_connect = (value == FEATURE_SUPPORTED);

GetCapability是静态方法,无需调用Initialize即可使用,适合上层在初始化前探测当前库版本是否支持 Auto Connect、Client/Server 通信等能力,避免硬编码默认值或与旧版.so不兼容。当feature_type为负数时返回PARAM_INVALID,未知或不支持的特性类型返回 SUCCESS 且valueFEATURE_NOT_SUPPORTED

六、数据结构与错误码的关联

Status在 include/hixl/hixl_types.h 中被定义为uint32_t,其取值常量与各接口的返回语义详见 HIXL-error-code.md。与本文数据结构直接相关的典型错误码包括:

错误码含义与数据结构的关联场景
PARAM_INVALID (103900)参数错误MemDesc地址非法、DeregisterMem(nullptr)notify.name/notify_msg超长、timeout_in_millis <= 0
NOT_CONNECTED (103902)没有建链TransferSync/TransferAsync在未建链时下发请求
ALREADY_CONNECTED (103903)已经建链对已建链对端重复Connect
UNSUPPORTED (103905)不支持的参数或接口批量GetTransferStatus在未满足 LocalCommRes 1.3 等条件时返回
RESOURCE_EXHAUSTED (203900)资源耗尽ConnectAsync/DisconnectAsync任务队列已满
FAILED (503900)通用失败通用传输失败

七、实战:数据结构在完整流程中的串联

以 examples/cpp/hixl_example_quickstart.cpp 的 Client/Server 流程为例,可以看到本文所有传输类数据结构的完整配合方式:

  1. 初始化aclrtSetDevice后,构造std::map<AscendString, AscendString> opts(如配置OPTION_GLOBAL_RESOURCE_CONFIG{"comm_resource_config.protocol_desc": ["hccs:device"]}),调用engine.Initialize(local, opts)
  2. 申请与描述内存aclrtMalloc申请 Device 内存后,填充MemDesc{addr, len}
  3. 注册内存RegisterMem(desc, MEM_DEVICE, handle)得到MemHandle
  4. 交换地址:通过 socket 交换远端地址,构造TransferOpDesc{local_addr, remote_addr, len}
  5. 建链Connect(remote_engine, timeout_in_millis)
  6. 传输TransferSync(remote_engine, READ, {op}, timeout)完成远端读;异步场景则改用TransferAsync(remote_engine, operation, op_descs, TransferArgs{user_data}, req)获得TransferReq,再用GetTransferStatus(单请求或批量)轮询;
  7. 断链与清理DisconnectDeregisterMem(handle)aclrtFreeFinalize()

上述流程中,MemDescMemTypeMemHandle构成内存生命周期,TransferOpTransferOpDescTransferArgsTransferReq构成请求生命周期,TransferStatusGetTransferStatusArgsTransferResult构成状态查询链路,各司其职且环环相扣。

八、快速参考表

数据结构类型关联接口核心字段
MemDescstructRegisterMemaddr、len
MemHandlevoid *RegisterMem / DeregisterMem
MemTypeenumRegisterMemMEM_DEVICE / MEM_HOST
TransferOpenumTransferSync / TransferAsyncREAD / WRITE
TransferOpDescstructTransferSync / TransferAsynclocal_addr、remote_addr、len
TransferArgsstructTransferAsyncuser_data
TransferReqvoid *TransferAsync / GetTransferStatus
TransferStatusenum classGetTransferStatusWAITING / COMPLETED / TIMEOUT / FAILED
GetTransferStatusArgsstructGetTransferStatus(批量)max_query_count、skip_waiting
TransferResultstructGetTransferStatus(批量)req、user_data、status
AsyncConnectStatusenum classGetAsyncConnectStatus7 种建链/断链状态
NotifyDescstructSendNotify / GetNotifiesname、notify_msg
FeatureTypeenumGetCapabilityAUTO_CONNECT、CLIENT_SERVER_COMM

如需查看各数据结构在真实接口签名中的用法与完整约束,可进一步阅读 HIXL-interface.md;如需确认错误码语义,可参考 HIXL-error-code.md;完整可运行示例位于 examples/cpp 目录。

【免费下载链接】hixlHIXL(Huawei Xfer Library)是一个灵活、高效的昇腾单边通信库,面向集群场景提供简单、可靠、高效的点对点数据传输能力。项目地址: https://gitcode.com/cann/hixl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询