HCCL 05 错误处理:出参改返回值,异常承载错误码

举报
黄生 发表于 2026/10/24 21:50:54 2026/10/24
【摘要】 C 侧HCCL 的 5 个通信域接口都用"返回码 + 出参"模式:HcclResult HcclGetRootInfo(HcclRootInfo* rootInfo); // rootInfo 是出参HcclResult HcclCommInitRootInfoConfig(nRanks, rootInfo, rank, config, HcclComm* comm);...

C 侧

HCCL 的 5 个通信域接口都用"返回码 + 出参"模式:

HcclResult HcclGetRootInfo(HcclRootInfo* rootInfo);           // rootInfo 是出参
HcclResult HcclCommInitRootInfoConfig(nRanks, rootInfo, rank, config, HcclComm* comm);  // comm 是出参
HcclResult HcclCommDestroy(HcclComm comm);                    // 无出参

返回 HcclResult(int,0 = 成功),结果经指针出参带回。这是 C 的惯例:返回值传状态码,实际数据走指针出参。。

Python 侧的翻译

出参改返回值:

C 接口 Python 接口 返回值
HcclGetRootInfo(rootInfo*) get_root_info() HcclRootInfo 对象
HcclCommInitRootInfoConfig(..., comm*) init_rank_config(nranks, root_info, rank, config=None) hccl_comm 句柄
HcclCommInitClusterInfoConfig(..., comm*) init_cluster_config(cluster_info, rank, config=None) hccl_comm 句柄
HcclCommConfigInit(config*) config_init() HcclCommConfig 对象
HcclCommDestroy(comm) destroy_comm(comm) HcclResult(0)

返回值位置给了业务对象(rootInfo / comm / config),错误码没地方放了。

解法:失败抛异常,异常携带错误码。

class HcclError(Exception):
    def __init__(self, result):
        self.result = result
        super().__init__(f"HCCL error, HcclResult={result}")

def init_rank_config(nranks, root_info, rank, config=None):
    cfg = config if config is not None else config_init()
    comm = ctypes.c_void_p()
    ret = hccl.HcclCommInitRootInfoConfig(
        ctypes.c_uint32(nranks), ctypes.byref(root_info),
        ctypes.c_uint32(rank), ctypes.byref(cfg), ctypes.byref(comm))
    if ret != HCCL_SUCCESS:
        raise HcclError(ret)     # 错误码原样携带,不截断不覆盖
    return comm                  # 成功才返回句柄

为什么用异常

  1. 无位置承载错误码。 要么返回 tuple (result, obj)(丑且易忽略错误),要么用异常。
  2. 通信域创建是低频操作。 每进程每通信域一次,异常的开销(栈展开)可忽略。算子是热路径,但算子没有出参,成功返回 HcclResult(0),失败抛异常——热路径上不触发异常时零开销。

HcclError 携带的是 C 侧原始 HcclResult 值(1~24 与 1041):

try:
    comm = init_rank_config(2, bad_info, 0)
except HcclError as e:
    print(e.result)  # 直接拿到 C 侧错误码,可与 types.HcclResult 比对
    if e.result == int(types.HcclResult.HCCL_E_INTERNAL):
        ...

用户可以拿 e.result 与 hccl4py.types.HcclResult 枚举比对,知道具体是什么错误——参数错、内存错、不支持、超时等,25 种错误码一一对应 hccl_types.h。

destroy_comm 不做幂等包装

def destroy_comm(comm):
    ret = hccl.HcclCommDestroy(comm)
    if ret != HCCL_SUCCESS:
        raise HcclError(ret)
    return ret

重复销毁同一句柄,C 侧行为是返回错误码(好像是未定义行为,禁止如此操作吧)。Python 侧原样透传:抛 HcclError。不做 Python 侧状态记录(比如"已销毁标记"),因为加状态记录意味着绑定层要维护句柄生命周期表,违背薄绑定原则;

UT 验证了这个行为:

def test_destroy_twice_raises(acl_env):
    comm = init_rank_config(1, get_root_info(), 0)
    destroy_comm(comm)
    with pytest.raises(HcclError):
        destroy_comm(comm)    # 第二次必须抛异常

算子侧的错误处理

算子没有出参,成功返回 HcclResult(0),失败抛异常。以 broadcast 为例:

def broadcast(hccl_comm, buf, count, data_type, root, stream):
    _check_stream(stream)
    ret = hccl_broadcast(buf, int(count), int(data_type), int(root),
                         hccl_comm, stream)
    if ret != types.HCCL_SUCCESS:
        raise HcclError(ret)
    return ret

热路径上 ret == 0,不触发异常,开销仅一次 int 比较和一次函数返回。

下一篇:14 个通信算子巡览。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。