5bcf420f创建于 2025年5月30日历史提交

aclnnAlltoAllAllGatherBatchMatMul

📄 查看源码

产品支持情况

产品 是否支持
Ascend 950PR/Ascend 950DT ×
Atlas A3 训练系列产品/Atlas A3 推理系列产品
Atlas A2 训练系列产品/Atlas A2 推理系列产品 ×
Atlas 200I/500 A2 推理产品 ×
Atlas 推理系列产品 ×
Atlas 训练系列产品 ×

功能说明

  • 接口功能:完成AllToAll、AllGather集合通信与BatchMatMul计算融合、并行。

  • 计算公式

    计算逻辑如下,其中y1、y2、y3为输出

    x1=AllToAll(x)x1 = AllToAll(x)

    y2=AllGather(x1)y2 = AllGather(x1)

    y3=BatchMatMul(y2,weight,bias)y3 = BatchMatMul(y2, weight, bias)

    y1=激活函数(y3)y1 = 激活函数(y3)

函数原型

每个算子分为两段式接口,必须先调用"aclnnAlltoAllAllGatherBatchMatMulGetWorkspaceSize"接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用"aclnnAlltoAllAllGatherBatchMatMul"接口执行计算。

aclnnStatus aclnnAlltoAllAllGatherBatchMatMulGetWorkspaceSize(
    const aclTensor* x,
    const aclTensor* weight,
    const aclTensor* biasOptional,
    const char*      groupEp,
    const char*      groupTp,
    int64_t          epWorldSize,
    int64_t          tpWorldSize,
    int64_t          xShardType,
    int64_t          actType,
    aclTensor*       y1Out,
    aclTensor*       y2OutOptional,
    aclTensor*       y3OutOptional,
    uint64_t*        workspaceSize,
    aclOpExecutor**  executor)
aclnnStatus aclnnAlltoAllAllGatherBatchMatMul(
    void*           workspace,
    uint64_t        workspaceSize,
    aclOpExecutor*  executor,
    aclrtStream     stream)

aclnnAlltoAllAllGatherBatchMatMulGetWorkspaceSize

  • 参数说明

    参数名 输入/输出 描述 使用说明 数据类型 数据格式 维度(shape) 非连续Tensor
    x(aclTensor*) 输入 通信后结果作为BatchMatMul计算的左矩阵。 该输入进行AllToAll、AllGather集合通信,必须为3维。 FLOAT16、BFLOAT16 ND 3 ×
    weight(aclTensor*) 输入 BatchMatMul计算的右矩阵。 数据类型与x保持一致,必须为3维。 FLOAT16、BFLOAT16 ND 3 ×
    biasOptional(aclTensor*) 输入 BatchMatMul计算的bias。
    • x为FLOAT16时,biasOptional需为FLOAT16。
    • x为BFLOAT16时,biasOptional需为FLOAT32,支持两维或三维。支持传入空指针。
    FLOAT16、FLOAT32 ND 2、3 ×
    groupEp(char*) 输入 ep通信域名称,专家并行的通信域。 字符串长度需大于0,小于128。 STRING - - -
    groupTp(char*) 输入 tp通信域名称,Tensor并行的通信域。 字符串长度需大于0,小于128。 STRING - - -
    epWorldSize(int64_t) 输入 ep通信域size。 支持2/4/8/16/32。 INT64 - - -
    tpWorldSize(int64_t) 输入 tp通信域size。 支持2/4/8/16/32。 INT64 - - -
    xShardType(int64_t) 输入 表示在tp域上allgather的维度
    • 0表示在H维度(即x的第2维,x为3维,分别为第0维、第1维、第2维)按tp域进行allgather。
    • 1表示在C维度(即x的第1维)上按tp域进行allgather。
    INT64 - - -
    actType(int64_t) 输入 激活函数类型。 支持0/1/2/3/4的输入,0表示无激活函数,对应关系为[0:None,1:GELU,2:Silu,3:Relu,4:FastGELU]。 INT64 - - -
    y1Out(aclTensor*) 输出 最终计算结果,如果有激活函数则为激活函数的输出,否则为BatchMatMul的输出。 支持3维,数据类型与输入x保持一致。 FLOAT16、BFLOAT16 ND 3 ×
    y2OutOptional(aclTensor*) 可选输出 AllGather的输出,反向可能需要。 支持3维,数据类型与输入x保持一致。空指针表示不需要该输出。 FLOAT16、BFLOAT16 ND 3 ×
    y3OutOptional(aclTensor*) 可选输出 有激活函数时,BatchMatMul的输出。 支持3维,数据类型与输入x保持一致。空指针表示不需要该输出。 FLOAT16、BFLOAT16 ND 3 ×
    workspaceSize(uint64_t*) 输出 workspace大小。 返回需要在Device侧申请的workspace大小。 UINT64 - - -
    executor(aclOpExecutor**) 输出 返回op执行器,包含了算子计算流程。 - aclOpExecutor* - - -
  • 返回值

    返回aclnnStatus状态码,具体参见aclnn返回码

    第一阶段接口完成入参校验,出现以下场景报错:

    返回值 错误码 描述
    ACLNN_ERR_PARAM_NULLPTR 161001 1. 传入的x、weight、groupEp、groupTp或y1Out是空指针。
    ACLNN_ERR_PARAM_INVALID 161002 1. groupEp或groupTp字符串长度不合法。
    2. 输入不支持的数据类型。
    3. 属性值不合法。
    4. aclTensor维度不合法。
    5. aclTensor shape不合法。
    6. 使能可选输出场景非法——当需要y3OutOptional可选输出时,actType需不为0。

aclnnAlltoAllAllGatherBatchMatMul

  • 参数说明

    参数名 输入/输出 描述
    workspace 输入 在Device侧申请的workspace内存地址。
    workspaceSize 输入 在Device侧申请的workspace大小,由第一段接口aclnnAlltoAllAllGatherBatchMatMulGetWorkspaceSize获取。
    executor 输入 op执行器,包含了算子计算流程。
    stream 输入 指定执行任务的Stream。
  • 返回值

    返回aclnnStatus状态码,具体参见aclnn返回码

约束说明

  • 确定性计算:

    • aclnnAlltoAllAllGatherBatchMatMul默认确定性实现。
  • 因为集合通信及BatchMatMul计算所需,输入输出shape需满足以下数学关系:(其中ep=epWorldSize,tp=tpWorldSize)

    • 按H轴进行AllGather场景,即xShardType为0场景:

      • x: (E, C, H/tp)
      • weight:(E/ep, H, M/tp)
      • biasOptional:非空指针情况下,三维时为(E/ep, 1, M/tp),两维时为(E/ep, M/tp)
      • y1Out:(E/ep, ep*C, M/tp)
      • y2OutOptional:(E/ep, ep*C, H)
      • y3OutOptional:(E/ep, ep*C, M/tp)
    • 按C轴进行AllGather场景,即xShardType为1场景:

      • x: (E, C/tp, H)
      • weight:(E/ep, H, M/tp)
      • biasOptional:非空指针情况下,三维时为(E/ep, 1, M/tp),两维时为(E/ep, M/tp)
      • y1Out:(E/ep, ep*tp*C/tp, M/tp)
      • y2OutOptional:(E/ep, ep*tp*C/tp, H)
      • y3OutOptional:(E/ep, ep*tp*C/tp, M/tp)
  • 数据关系说明:

    • 比如x.size(0)等于E,weight.size(0)等于E/ep,则表示,x.size(0) = ep*weight.size(0),x.size(0)是ep的整数倍;其他关系类似。
    • E的取值范围为[2, 512],且E是ep的整数倍。
    • H的取值范围为:[1, 65535],当xShardType为0时,H是tp的整数倍。
    • M/tp的取值范围为:[1, 65535]。
    • E/ep的取值范围为:[1, 32]。
    • ep、tp均仅支持2、4、8、16、32。
    • groupEp和groupTp名称不能相同。
    • C必须大于0,上限为算子device内存上限,当xShardType为1时,C是tp的整数倍。
    • 通算融合算子不支持并发调用,不同的通算融合算子也不支持并发调用。
    • 不支持跨超节点,只支持超节点内。

调用示例

示例代码如下,仅供参考,具体编译和执行过程请参考编译与运行样例。

  • Atlas A3 训练系列产品/Atlas A3 推理系列产品:

    #include <thread>
    #include <iostream>
    #include <string>
    #include <vector>
    #include "acl/acl.h"
    #include "hccl/hccl.h"
    #include "aclnnop/aclnn_all_to_all_all_gather_batch_matmul.h"
    
    #define CHECK_RET(cond, return_expr) \
        do {                             \
            if (!(cond)) {               \
                return_expr;             \
            }                            \
        } while (0)
    
    #define LOG_PRINT(message, ...)         \
        do {                                \
            printf(message, ##__VA_ARGS__); \
        } while(0)
    
    constexpr int EP_WORLD_SIZE = 4;
    constexpr int TP_WORLD_SIZE = 2;
    constexpr int DEV_NUM = EP_WORLD_SIZE * TP_WORLD_SIZE;
    
    int64_t GetShapeSize(const std::vector<int64_t> &shape)
    {
        int64_t shape_size = 1;
        for (auto i : shape) {
            shape_size *= i;
        }
        return shape_size;
    }
    
    template<typename T>
    int CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr,
        aclDataType dataType, aclTensor **tensor)
    {
        auto size = GetShapeSize(shape) * sizeof(T);
        auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtMalloc failed. ret: %d\n", ret); return ret);
        ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtMemcpy failed. ret: %d\n", ret); return ret);
        std::vector<int64_t> strides(shape.size(), 1);
        for (int64_t i = shape.size() - 2; i >= 0; i--) {
            strides[i] = shape[i + 1] * strides[i + 1];
        }
        *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
            shape.data(), shape.size(), *deviceAddr);
        return 0;
    }
    
    struct Args {
        int rankId;
        HcclComm hcclEpComm;
        HcclComm hcclTpComm;
        aclrtStream stream;
        aclrtContext context;
    };
    
    int LaunchOneThreadAlltoAllAllGatherBmm(Args &args)
    {
        int ret = aclrtSetCurrentContext(args.context);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtSetCurrentContext failed. ret: %d\n", ret); return ret);
        char hcomEpName[128] = {0};
        ret = HcclGetCommName(args.hcclEpComm, hcomEpName);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] HcclGetEpCommName failed. ret: %d\n", ret); return -1);
        char hcomTpName[128] = {0};
        ret = HcclGetCommName(args.hcclTpComm, hcomTpName);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] HcclGetTpCommName failed. ret: %d\n", ret); return -1);
        LOG_PRINT("[INFO] rank = %d, hcomEpName = %s, hcomTpName = %s, stream = %p, context = %p\n", args.rankId,
        hcomEpName, hcomTpName, args.stream, args.context);
        
        int64_t E = 2 * EP_WORLD_SIZE;
        int64_t C = 2 * TP_WORLD_SIZE;
        int64_t H = 6 * TP_WORLD_SIZE;
        int64_t M = 6 * TP_WORLD_SIZE;
        int64_t xShardType = 1; // 可切换为0,使能gather H 轴场景
        int64_t actType = 1;
        
        std::vector<int64_t> xShape;
        std::vector<int64_t> weightShape;
        std::vector<int64_t> biasShape;
        std::vector<int64_t> y1OutShape;
        std::vector<int64_t> y2OutShape;
        std::vector<int64_t> y3OutShape;
        
        if (xShardType == 1) {
            xShape = {E, C / TP_WORLD_SIZE, H};
            weightShape = {E / EP_WORLD_SIZE, H, M / TP_WORLD_SIZE};
            biasShape = {E / EP_WORLD_SIZE, 1, M / TP_WORLD_SIZE};
            y1OutShape = {E / EP_WORLD_SIZE, EP_WORLD_SIZE * TP_WORLD_SIZE * C / TP_WORLD_SIZE, M / TP_WORLD_SIZE};
            y2OutShape = {E / EP_WORLD_SIZE, EP_WORLD_SIZE * TP_WORLD_SIZE * C / TP_WORLD_SIZE, H};
            y3OutShape = {E / EP_WORLD_SIZE, EP_WORLD_SIZE * TP_WORLD_SIZE * C / TP_WORLD_SIZE, M / TP_WORLD_SIZE};
        } else if (xShardType == 0) {
            xShape = {E, C, H / TP_WORLD_SIZE};
            weightShape = {E / EP_WORLD_SIZE, H, M / TP_WORLD_SIZE};
            biasShape = {E / EP_WORLD_SIZE, 1, M / TP_WORLD_SIZE};
            y1OutShape = {E / EP_WORLD_SIZE, EP_WORLD_SIZE * C, M / TP_WORLD_SIZE};
            y2OutShape = {E / EP_WORLD_SIZE, EP_WORLD_SIZE * C, H};
            y3OutShape = {E / EP_WORLD_SIZE, EP_WORLD_SIZE * C, M / TP_WORLD_SIZE};
        } else {
            LOG_PRINT("[ERROR] unsupported xShardType = %ld.\n", xShardType);
            return -1;
        }
    
        void *xDeviceAddr = nullptr;
        void *weightDeviceAddr = nullptr;
        void *biasDeviceAddr = nullptr;
        void *y1OutDeviceAddr = nullptr;
        void *y2OutDeviceAddr = nullptr;
        void *y3OutDeviceAddr = nullptr;
        aclTensor *x = nullptr;
        aclTensor *weight = nullptr;
        aclTensor *bias = nullptr;
        aclTensor *y1Out = nullptr;
        aclTensor *y2Out = nullptr;
        aclTensor *y3Out = nullptr;
    
        uint64_t workspaceSize = 0;
        aclOpExecutor *executor = nullptr;
        void *workspaceAddr = nullptr;
    
        long long xShapeSize = GetShapeSize(xShape);
        long long weightShapeSize = GetShapeSize(weightShape);
        long long biasShapeSize = GetShapeSize(biasShape);
        long long y1OutShapeSize = GetShapeSize(y1OutShape);
        long long y2OutShapeSize = GetShapeSize(y2OutShape);
        long long y3OutShapeSize = GetShapeSize(y3OutShape);
        
        std::vector<int16_t> xHostData(xShapeSize, 1);
        std::vector<int16_t> weightHostData(weightShapeSize, 2);
        std::vector<int16_t> biasHostData(biasShapeSize, 3);
        std::vector<int16_t> y1OutHostData(y1OutShapeSize, 0);
        std::vector<int16_t> y2OutHostData(y2OutShapeSize, 0);
        std::vector<int16_t> y3OutHostData(y3OutShapeSize, 0);
    
        ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT16, &x);
        CHECK_RET(ret == ACL_SUCCESS, return ret);
        ret = CreateAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_FLOAT16, &weight);
        CHECK_RET(ret == ACL_SUCCESS, return ret);
        ret = CreateAclTensor(y1OutHostData, y1OutShape, &y1OutDeviceAddr, aclDataType::ACL_FLOAT16, &y1Out);
        CHECK_RET(ret == ACL_SUCCESS, return ret);
        ret = CreateAclTensor(y2OutHostData, y2OutShape, &y2OutDeviceAddr, aclDataType::ACL_FLOAT16, &y2Out);
        CHECK_RET(ret == ACL_SUCCESS, return ret);
        ret = CreateAclTensor(y3OutHostData, y3OutShape, &y3OutDeviceAddr, aclDataType::ACL_FLOAT16, &y3Out);
        CHECK_RET(ret == ACL_SUCCESS, return ret);
    
        // 调用第一阶段接口
        ret = aclnnAlltoAllAllGatherBatchMatMulGetWorkspaceSize(x, weight, bias, hcomEpName, hcomTpName, EP_WORLD_SIZE,
            TP_WORLD_SIZE, xShardType, actType, y1Out, y2Out, y3Out, &workspaceSize, &executor);
        CHECK_RET(ret == ACL_SUCCESS,
            LOG_PRINT("[ERROR] aclnnAlltoAllAllGatherBatchMatMulGetWorkspaceSize failed. ret = %d \n", ret); return ret);
        // 根据第一阶段接口计算出的workspaceSize申请device内存
        if (workspaceSize > 0) {
            ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
            CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtMalloc workspace failed. ret = %d \n", ret); return ret);
        }
        // 调用第二阶段接口
        ret = aclnnAlltoAllAllGatherBatchMatMul(workspaceAddr, workspaceSize, executor, args.stream);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclnnAlltoAllAllGatherBatchMatMul failed. ret = %d \n", ret);
            return ret);
        // (固定写法)同步等待任务执行结束
        ret = aclrtSynchronizeStreamWithTimeout(args.stream, 10000);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtSynchronizeStreamWithTimeout failed. ret = %d \n", ret);
            return ret);
        LOG_PRINT("[INFO] device_%d aclnnAlltoAllAllGatherBatchMatMul execute successfully.\n", args.rankId);
        HcclCommDestroy(args.hcclEpComm);
        HcclCommDestroy(args.hcclTpComm);
        // 释放device资源,需要根据具体API的接口定义修改
        if (x != nullptr) {
            aclDestroyTensor(x);
        }
        if (weight != nullptr) {
            aclDestroyTensor(weight);
        }
        if (bias != nullptr) {
            aclDestroyTensor(bias);
        }
        if (y1Out != nullptr) {
            aclDestroyTensor(y1Out);
        }
        if (y2Out != nullptr) {
            aclDestroyTensor(y2Out);
        }
        if (y3Out != nullptr) {
            aclDestroyTensor(y3Out);
        }
        if (xDeviceAddr != nullptr) {
            aclrtFree(xDeviceAddr);
        }
        if (weightDeviceAddr != nullptr) {
            aclrtFree(weightDeviceAddr);
        }
        if (biasDeviceAddr != nullptr) {
            aclrtFree(biasDeviceAddr);
        }
        if (y1OutDeviceAddr != nullptr) {
            aclrtFree(y1OutDeviceAddr);
        }
        if (y2OutDeviceAddr != nullptr) {
            aclrtFree(y2OutDeviceAddr);
        }
        if (y3OutDeviceAddr != nullptr) {
            aclrtFree(y3OutDeviceAddr);
        }
        if (workspaceSize > 0) {
            aclrtFree(workspaceAddr);
        }
        ret = aclrtDestroyStream(args.stream);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtDestroyStream failed. ret = %d \n", ret); return ret);
        ret = aclrtResetDevice(args.rankId);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtResetDevice failed. ret = %d \n", ret); return ret);
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        // 本样例基于Atlas A3实现,必须在Atlas A3上运行
        int ret = aclInit(nullptr);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclInit failed. ret = %d \n", ret); return ret);
        aclrtStream stream[DEV_NUM];
        aclrtContext context[DEV_NUM];
        for (uint32_t rankId = 0; rankId < DEV_NUM; rankId++) {
        ret = aclrtSetDevice(rankId);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtSetDevice failed. ret = %d \n", ret); return ret);
        ret = aclrtCreateContext(&context[rankId], rankId);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtCreateContext failed. ret = %d \n", ret); return ret);
        ret = aclrtCreateStream(&stream[rankId]);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] aclrtCreateStream failed. ret = %d \n", ret); return ret);
        }
    
        int32_t devicesEp[DEV_NUM];
        int32_t devicesTp[DEV_NUM];
    
        // 初始化ep域  ep = 4  {0,2,4,6} {1,3,5,7}
        HcclComm commsEp[DEV_NUM];
        for (int i = 0; i < TP_WORLD_SIZE; i++) {
            for (int j =0; j < EP_WORLD_SIZE; j++) {
                devicesEp[j + i * EP_WORLD_SIZE] = i + j * TP_WORLD_SIZE;
            }
            ret = HcclCommInitAll(EP_WORLD_SIZE, &devicesEp[i * EP_WORLD_SIZE], &commsEp[i * EP_WORLD_SIZE]);
            CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] HcclCommInitAll ep world %d failed. ret = %d \n", i, ret);
                return ret);
        }
    
        // 初始化tp域  tp = 4  {0,1},{2,3},{4,5},{6,7}
        HcclComm commsTp[DEV_NUM];
        for (int i = 0; i < EP_WORLD_SIZE; i++) {
            for (int j =0; j < TP_WORLD_SIZE; j++) {
                devicesTp[j + i * TP_WORLD_SIZE] = j + i * TP_WORLD_SIZE;
            }
            ret = HcclCommInitAll(TP_WORLD_SIZE, &devicesTp[i * TP_WORLD_SIZE], &commsTp[i * TP_WORLD_SIZE]);
            CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("[ERROR] HcclCommInitAll tp world %d failed. ret = %d \n", i, ret);
                return ret);
        }
    
        Args args[DEV_NUM];
        // 启动多线程
        std::vector<std::unique_ptr<std::thread>> threads(DEV_NUM);
        for (uint32_t rankId = 0; rankId < DEV_NUM; rankId++) {
            args[rankId].rankId = rankId;
            args[rankId].hcclEpComm = commsEp[rankId % TP_WORLD_SIZE * EP_WORLD_SIZE + rankId / TP_WORLD_SIZE];
            // Tp init按照顺序init,但同域是device id配对为[0,4],[1,5],...,[3,7].
            args[rankId].hcclTpComm = commsTp[rankId];
            std::cout << "test devices id " << rankId << " = " << devicesTp[rankId] << std::endl;
            if (rankId == (DEV_NUM - 1)) {
                args[rankId].hcclTpComm = commsTp[rankId];
            }
            args[rankId].stream = stream[rankId];
            args[rankId].context = context[rankId];
            threads[rankId].reset(new std::thread(&LaunchOneThreadAlltoAllAllGatherBmm, std::ref(args[rankId])));
        }
        for (uint32_t rankId = 0; rankId < DEV_NUM; rankId++) {
            threads[rankId]->join();
        }
    
        aclFinalize();
        return 0;
    }