CANN hcomm 通用通信抽象层的后端插件化架构

cann组织链接:https://atomgit.com/cann

hcomm仓库链接:https://atomgit.com/cann/hcomm

前言

在异构计算与大规模分布式训练场景中,底层通信能力直接影响系统性能与可移植性。CANN(Compute Architecture for Neural Networks)开源项目中的 hcomm (Huawei Communication)仓库(https://atomgit.com/cann/hcomm)作为其通信栈的基础组件,提供了一套通用、高性能的点对点通信抽象层 。其核心设计亮点在于后端插件化架构(Backend Plugin Architecture),使得同一套上层接口可无缝适配多种物理网络(如 PCIe、RoCE、自研互连)与传输协议(如 RDMA、TCP、共享内存)。

1. hcomm 的定位与整体架构

hcomm 并非直接面向最终用户,而是作为 中间通信层,服务于 hccl(集合通信)、hixl(低延迟通信)等组件。其目标是:

  • 屏蔽底层差异:上层无需关心是 RoCE 还是 PCIe;
  • 最大化性能:选择最优后端执行通信;
  • 支持扩展:第三方可开发新后端插件。

架构概览

调用
hccl / hixl
hcomm API
Backend Manager
Plugin: RoCE
Plugin: Shared Memory
Plugin: TCP
libibverbs / RDMA Core
POSIX shm / mmap
Socket API

关键创新在于 Backend Manager 的插件调度机制。


2. 插件接口定义与抽象层设计

hcomm 通过纯虚类定义后端插件接口。

2.1 核心通信接口

cpp 复制代码
// hcomm/include/backend/backend_interface.h
class ICommBackend {
public:
    virtual ~ICommBackend() = default;
    
    // 初始化通信上下文
    virtual Status Init(const CommContext& ctx) = 0;
    
    // 异步发送
    virtual Status ISend(
        void* buffer,
        size_t size,
        int dest_rank,
        int tag,
        CommRequest* req
    ) = 0;
    
    // 异步接收
    virtual Status IRecv(
        void* buffer,
        size_t size,
        int src_rank,
        int tag,
        CommRequest* req
    ) = 0;
    
    // 等待请求完成
    virtual Status Wait(CommRequest* req) = 0;
    
    // 查询是否支持当前设备拓扑
    virtual bool IsSupported(const TopologyInfo& topo) const = 0;
    
    // 返回后端名称(用于日志与调试)
    virtual std::string Name() const = 0;
};

所有后端插件必须继承此接口。

2.2 内存注册接口(用于零拷贝)

为支持 RDMA 等零拷贝技术,hcomm 额外定义内存管理接口:

cpp 复制代码
struct IMemoryRegion {
    virtual void* ptr() = 0;
    virtual size_t size() = 0;
    virtual ~IMemoryRegion() = default;
};

class ICommBackend {
public:
    virtual std::unique_ptr<IMemoryRegion> RegisterMemory(
        void* ptr, size_t size, MemType type
    ) = 0;
};

3. 插件注册与动态加载机制

3.1 静态注册(编译时链接)

对于内置后端(如 RoCE、SHMEM),hcomm 使用 工厂模式 + 静态初始化

cpp 复制代码
// hcomm/plugins/roce/roce_backend.cpp
class RoceBackend : public ICommBackend { /* ... */ };

// 全局工厂注册
static BackendRegistrar g_roce_registrar("roce", []() {
    return std::make_unique<RoceBackend>();
});

其中 BackendRegistrar 定义如下:

cpp 复制代码
// hcomm/core/backend_factory.h
class BackendFactory {
public:
    using CreatorFunc = std::function<std::unique_ptr<ICommBackend>()>;
    
    static void Register(const std::string& name, CreatorFunc creator) {
        instance().creators_[name] = std::move(creator);
    }
    
    static std::unique_ptr<ICommBackend> Create(const std::string& name) {
        auto it = instance().creators_.find(name);
        return it != instance().creators_.end() ? it->second() : nullptr;
    }
private:
    std::unordered_map<std::string, CreatorFunc> creators_;
};

3.2 动态插件(运行时加载)

hcomm 支持 .so 插件动态加载:

cpp 复制代码
// hcomm/core/plugin_loader.cpp
std::unique_ptr<ICommBackend> LoadPlugin(const std::string& path) {
    void* handle = dlopen(path.c_str(), RTLD_LAZY);
    if (!handle) return nullptr;
    
    // 查找符号:create_backend
    auto create_func = (ICommBackend*(*)())dlsym(handle, "create_backend");
    if (!create_func) {
        dlclose(handle);
        return nullptr;
    }
    
    return std::unique_ptr<ICommBackend>(create_func());
}

插件需提供 extern "C" ICommBackend* create_backend(); 入口。

优势:用户可不重新编译 hcomm 即扩展新后端。


4. 后端选择与上下文管理

4.1 多后端共存与优先级

hcomm 允许多个后端同时注册,并按优先级排序

yaml 复制代码
# config/hcomm_backends.yaml
backends:
  - name: "roce"
    priority: 100   # 越高越优先
    enabled: true
  - name: "shmem"
    priority: 90
    enabled: true
  - name: "tcp"
    priority: 50
    enabled: true

加载时按优先级排序:

cpp 复制代码
// hcomm/core/backend_manager.cpp
void BackendManager::LoadBackendsFromConfig() {
    auto configs = ParseYaml("hcomm_backends.yaml");
    std::sort(configs.begin(), configs.end(),
              [](auto& a, auto& b) { return a.priority > b.priority; });
    
    for (auto& cfg : configs) {
        if (cfg.enabled) {
            auto backend = BackendFactory::Create(cfg.name);
            if (backend && backend->IsSupported(current_topology_)) {
                backends_.push_back(std::move(backend));
            }
        }
    }
}

4.2 通信上下文(CommContext)绑定

每次通信操作通过 CommContext 指定目标后端:

cpp 复制代码
// hcomm/api/hcomm_api.cpp
HcclResult hcomm_isend(
    void* buf, size_t size, int dest, int tag, aclrtStream stream
) {
    // 获取当前上下文(隐式或显式)
    auto ctx = CommContextManager::GetCurrent();
    
    // 由 BackendManager 选择最优后端
    auto backend = BackendManager::Instance().SelectBackend(ctx);
    
    CommRequest req;
    backend->ISend(buf, size, dest, tag, &req);
    
    // 将请求绑定到 stream(用于异步等待)
    StreamRequestBinder::Bind(stream, req);
    return HCCL_SUCCESS;
}

若未指定上下文,hcomm 自动使用最高优先级可用后端


5. 典型后端插件实现剖析

5.1 RoCE 后端(RDMA over Converged Ethernet)

基于 libibverbs 实现零拷贝通信:

cpp 复制代码
// hcomm/plugins/roce/roce_backend.cpp
Status RoceBackend::ISend(
    void* buffer, size_t size, int dest, int tag, CommRequest* req
) {
    // 1. 获取已注册的 MR(Memory Region)
    auto mr = memory_registry_.Find(buffer);
    if (!mr) {
        return ERROR_NOT_REGISTERED;
    }
    
    // 2. 构造 RDMA SEND WR
    ibv_sge sge;
    sge.addr = reinterpret_cast<uint64_t>(buffer);
    sge.length = size;
    sge.lkey = mr->lkey();
    
    ibv_send_wr wr{};
    wr.wr_id = reinterpret_cast<uint64_t>(req);
    wr.sg_list = &sge;
    wr.num_sge = 1;
    wr.opcode = IBV_WR_SEND;
    
    // 3. 提交至 QP
    ibv_send_wr* bad_wr;
    if (ibv_post_send(qp_, &wr, &bad_wr)) {
        return ERROR_RDMA_POST;
    }
    
    return SUCCESS;
}

💡 关键优化 :QP(Queue Pair)按 (src, dst) 缓存,避免重复创建。

5.2 共享内存后端(SHMEM)

适用于单机多卡场景:

cpp 复制代码
// hcomm/plugins/shmem/shmem_backend.cpp
Status ShmemBackend::Init(const CommContext& ctx) {
    // 创建命名共享内存段
    std::string name = "/hcomm_shm_" + std::to_string(ctx.comm_id);
    int fd = shm_open(name.c_str(), O_CREAT | O_RDWR, 0666);
    ftruncate(fd, kShmemBufferSize);
    shmem_ptr_ = mmap(nullptr, kShmemBufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    // 初始化环形缓冲区控制块
    ring_buffer_ = new (shmem_ptr_) RingBuffer(kShmemBufferSize - sizeof(RingBuffer));
    return SUCCESS;
}

Status ShmemBackend::ISend(void* buf, size_t size, int dest, int tag, CommRequest* req) {
    // 直接拷贝至共享内存环形缓冲区
    ring_buffer_->Push(buf, size, dest, tag);
    req->status = REQUEST_COMPLETED; // 同步完成
    return SUCCESS;
}

优势:零系统调用,延迟 < 1μs。


6. 性能隔离与故障恢复

6.1 后端健康检查

hcomm 定期探测后端状态:

cpp 复制代码
// hcomm/core/health_monitor.cpp
void HealthMonitor::CheckBackends() {
    for (auto& backend : backends_) {
        if (!backend->Ping()) {
            LOG(WARNING) << "Backend " << backend->Name() << " failed";
            backend->MarkUnhealthy();
        }
    }
}

失败后端自动降级至次优选项。

6.2 请求超时与重试

每个 CommRequest 支持超时设置:

cpp 复制代码
req->timeout_ms = 5000; // 5秒
if (backend->Wait(req) == ERROR_TIMEOUT) {
    // 切换至备用后端重试
    auto fallback = GetFallbackBackend(backend);
    fallback->Resend(original_request);
}

结语

CANN hcomm 通过精心设计的插件化架构,成功实现了通信能力的解耦、复用与扩展。其不仅支持 RoCE、共享内存等主流后端,更通过动态加载、优先级调度与故障恢复机制,确保了在复杂异构环境下的鲁棒性与高性能。作为 CANN 通信栈的基石,hcomm 的抽象层设计为上层 hccl、hixl 等组件提供了坚实支撑,同时也为社区贡献新后端(如 InfiniBand、自研 NoC)打开了大门。随着 AI 集群规模持续扩大,hcomm 的插件化模型将成为构建高效、可移植分布式系统的关键基础设施。

cann组织链接:https://atomgit.com/cann

hcomm仓库链接:https://atomgit.com/cann/hcomm

相关推荐
牛奶10 小时前
《前端架构设计》:除了写代码,我们还得管点啥
前端·架构·设计
苏渡苇11 小时前
Java + Redis + MySQL:工业时序数据缓存与持久化实战(适配高频采集场景)
java·spring boot·redis·后端·spring·缓存·架构
麦聪聊数据11 小时前
如何用 B/S 架构解决混合云环境下的数据库连接碎片化难题?
运维·数据库·sql·安全·架构
2的n次方_12 小时前
CANN HCOMM 底层架构深度解析:异构集群通信域管理、硬件链路使能与算力重叠优化机制
架构
技术传感器12 小时前
大模型从0到精通:对齐之心 —— 人类如何教会AI“好“与“坏“ | RLHF深度解析
人工智能·深度学习·神经网络·架构
小北的AI科技分享13 小时前
万亿参数时代:大语言模型的技术架构与演进趋势
架构·模型·推理
一条咸鱼_SaltyFish16 小时前
从零构建个人AI Agent:Node.js + LangChain + 上下文压缩全流程
网络·人工智能·架构·langchain·node.js·个人开发·ai编程
码云数智-园园16 小时前
解决 IntelliJ IDEA 运行 Spring Boot 测试时“命令行过长”错误
架构
AC赳赳老秦17 小时前
虚拟化技术演进:DeepSeek适配轻量级虚拟机,实现AI工作负载高效管理
人工智能·python·架构·数据挖掘·自动化·数据库架构·deepseek
Francek Chen17 小时前
【大数据存储与管理】分布式文件系统HDFS:01 分布式文件系统
大数据·hadoop·分布式·hdfs·架构