下载
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助
昇腾小AI

使用说明

Ascend C提供一组Hccl高阶API,方便算子Kernel开发用户在AI Core侧灵活编排集合通信任务。相比于一般算子,使用Hccl高阶API的算子在开发和执行过程中的注意事项请参考通算融合算子

Hccl为集合通信任务客户端,主要对外提供了集合通信原语接口(以下统称为Prepare接口),对标集合通信C++接口,详细可参见HCCL接口参考,当前支持AllReduceAllGatherReduceScatterAlltoAll接口等。本章的所有接口运行在AI Core上,且不执行通信任务,而是由用户调用Prepare接口将对应类型的通信任务信息发送给AI CPU服务端,并在合适的时机通过Commit接口通知AI CPU上的服务端执行对应的通信任务。

所谓合适的时机,取决于用户编排的是先通信后计算的任务,还是先计算后通信的任务。对于这两种场景,简述如下:

  • 先通信后计算的任务:典型的如AllGather通信+MatMul计算任务编排。此场景下,用户在调用AllGather接口下发通信任务之后,通过AllGather接口返回的该通信任务标识handleId,可立即调用Commit接口通知服务端执行该handleId对应的任务,同时用户调用Wait阻塞接口等待服务端通知handleId对应的通信任务执行结束,待该通信任务结束后,再执行计算任务。
  • 先计算后通信的任务:典型的如MatMul计算+AllReduce通信任务编排。此场景下,用户可以先调用AllReduce接口通知服务端先下发通信任务,再调用MatMul计算接口进行计算,这样AllReduce任务的组装和任务下发过程可以被MatMul的计算流水所掩盖,待计算任务完成后,调用Commit接口通知服务端执行AllReduce任务,无须调用Wait接口等待通信任务执行结束。

当后续无通信任务时,调用Finalize接口,通知服务端后续无通信任务,执行结束后退出,客户端检测并等待最后一个通信任务执行结束。以上介绍的AI Core下发Hccl通信任务的机制示意图如下图所示。

图1 AI Core下发Hccl通信任务机制

实现AI Core下发一个通信任务的具体步骤如下:

  1. 用户创建Hccl对象,并调用初始化接口。
    1
    2
    3
    Hccl<HCCL_SERVER_TYPE_AICPU> hccl;  // 创建Hccl对象
    GM_ADDR context = AscendC::GetHcclContext<HCCL_GROUP_ID_0>();  // 获取hccl上下文信息
    hccl.Init(context);  // hccl对象初始化
    

    调用Init初始化接口时需要传入通信上下文信息,可以通过框架提供的获取通信上下文的接口GetHcclContext获取,该接口的函数原型如下;示例中HCCL_GROUP_ID_0为定义的全局变量,可参考表1。该接口返回的数据结构为HcclCombinOpParam,开发者也可以构造该数据结构作为Hccl对象初始化的信息传入,HcclCombinOpParam结构体说明参考表2

    1
    2
    template <uint32_t index> 
    __aicore__ inline __gm__ uint8_t* __gm__ GetHcclContext(void)
    
    表1 全局变量说明

    变量

    说明

    HCCL_GROUP_ID_0

    通信域的编号,只有一个通信域时为0。(通信域指的是参与集合通信的进程组,即多个卡组成的通信集合)

    表2 结构体HcclCombineOpParam说明

    数据类型

    说明

    HcclCombineOpParam

    Hccl通信上下文。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    struct HcclCombineOpParam {
    uint64_t workSpace;     // client和server之间通信的地址,hccl申请并清零
    uint64_t workSpaceSize; // client和server之间通信的空间大小
    uint32_t rankId;  // 当前卡rankId
    uint32_t rankNum; // 所有卡的数量
    uint64_t winSize;  // 每个win大小
    uint64_t windowsIn[HCCL_MAX_RANK_NUM];  // 作为输入的windows地址,rankID对应的为本卡地址,其他位置为跨卡映射地址
    uint64_t windowsOut[HCCL_MAX_RANK_NUM]; // 作为输出的windows地址,rankID对应的为本卡地址,其他位置为跨卡映射地址
    // 其他字段AI CPU使用,对AI Core隐藏
    }
    
  2. 用户通过对应的Prepare接口异步下发对应类型的通信任务,并获取到该任务的标识handleId,服务端接收到后开始通信任务的展开和下发,示例如下。
    1
    2
    3
    4
    5
    6
    7
    8
    auto handleId = hccl.ReduceScatter<false>(aGM, cGM, recvCount, 
                                              AscendC::HCCL_DATA_TYPE_FP16,
                                              HCCL_REDUCE_SUM, strideCount, 1);
    // 对于Prepare接口,在调试时可增加异常值校验和PRINTF打印
    // if (handleId == INVALID_HANDLE_ID) {
    // 	PRINTF("[ERROR] call ReduceScatter failed, handleId is -1.");
    //	return;
    // }
    

    示例的Prepare接口为ReduceScatter,其他接口可参考后续章节的内容。其中的参数AscendC::HCCL_DATA_TYPE_FP16是Hccl任务的数据类型,其数据结构为HcclDataType,对应的参数说明参考表3;参数HCCL_REDUCE_SUM是一种Reduce操作,AllReduce和ReduceScatter规约操作支持的Reduce操作类型参见表4

    表3 HcclDataType参数说明

    数据类型

    说明

    HcclDataType

    Hccl任务的数据类型。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    enum HcclDataType {
    HCCL_DATA_TYPE_INT8 = 0,   /* int8 */
    HCCL_DATA_TYPE_INT16 = 1,  /* int16 */
    HCCL_DATA_TYPE_INT32 = 2,  /* int32 */
    HCCL_DATA_TYPE_FP16 = 3,   /* half或float16 */
    HCCL_DATA_TYPE_FP32 = 4,   /* float32 */
    HCCL_DATA_TYPE_INT64 = 5,  /* int64 */
    HCCL_DATA_TYPE_UINT64 = 6, /* uint64 */
    HCCL_DATA_TYPE_UINT8 = 7,  /* uint8 */
    HCCL_DATA_TYPE_UINT16 = 8, /* uint16 */
    HCCL_DATA_TYPE_UINT32 = 9, /* uint32 */
    HCCL_DATA_TYPE_FP64 = 10,  /* float64 */
    HCCL_DATA_TYPE_BFP16 = 11, /* bfloat16 */
    HCCL_DATA_TYPE_RESERVED    /* reserved */
    }
    
    表4 HcclReduceOp参数说明

    数据类型

    说明

    HcclReduceOp

    Reduce操作类型。

    1
    2
    3
    4
    5
    6
    7
    enum HcclReduceOp {
    HCCL_REDUCE_SUM = 0,  /* sum */
    HCCL_REDUCE_PROD = 1, /* prod */
    HCCL_REDUCE_MAX = 2,  /* max */
    HCCL_REDUCE_MIN = 3,  /* min */
    HCCL_REDUCE_RESERVED  /* reserved */
    }
    
  3. 用户调用Commit接口通知服务端执行handleId对应的通信任务。
    1
    2
    // 等待通信任务执行时机成熟,调用Commit接口通知服务端执行
    hccl.Commit(handleId);
    
  4. 用户调用Wait阻塞接口,等待服务端执行完对应的通信任务。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    auto ret = hccl.Wait(handleId);
    // 对于Wait和Query接口,在调试时可增加异常值校验和PRINTF打印
    // if (ret == HCCL_FAILED) {
    //	PRINTF("[ERROR] call Wait for handleId[%d] failed.", handleId);
    //	return;
    // }
    
    // 调用核间同步接口,防止部分核执行较快退出,触发hccl析构,影响执行较慢的核
    // 开发者可根据实际的业务场景,选择调用, , 接口,保证全部核的任务完成后再退出执行
    
  5. 用户调用Finalize接口,通知服务端后续无通信任务,执行结束后退出;客户端检测并等待最后一个通信任务执行结束。
    1
    hccl.Finalzie();
    

注意:调用步骤2到步骤5的接口前,必须指定接口代码运行在AI Cube核或者AI Vector核上,实现时如下代码所示。

1
2
3
4
5
// 通过g_coreType来判断AI Cube核(即AIC核)或者AI Vector核(即AIV核)
if (g_coreType == AIV) {
// if (g_coreType == AIC) {
        调用Hccl接口
    }

基于以上对单个通信任务下发的了解,介绍各Prepare接口中repeat参数的灵活使用方式。每个handleId对应的通信任务,其Commit接口和Wait接口的调用次数,与Prepare接口的repeat入参的值相等。以图2 ReduceScatter通信示例进行说明,假设共4张卡,每张卡上源数据首先按照rankId均匀分成4份,每份数据被切分成3份,最终被切分后的每份数据的个数为TiliLen,每次ReduceScatter通信仅通信一组切分数据(如图中数据0-0、1-0、2-0、3-0为一组切分数据),因此需要做3次ReduceScatter操作,全部数据才能通信完。

注意:数据的切分数量不要超过16块,否则会影响算子性能或者可能导致部分资源不足。一个通信域内,Prepare接口的总调用次数不能超过32次,否则通信任务会下发失败,Prepare接口返回INVALID_HANDLE_ID,即返回-1。

这种场景下,可以调用3次repeat参数为1的ReduceScatter接口,下发3个通信任务,同时更新每个ReduceScatter任务的收发地址,得到3个任务的handleId,每个handleId任务调用1次Commit和Wait接口,对应代码片段如下。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const size_t rankSize = 4U; // 4张卡
const size_t tileCnt = 3U;  // 卡上的数据按照rankId均匀分成rankSize份,且每份又被切分成3份
const size_t TileLen = 100U;// 被切分后的每份数据个数

// for循环中生成了3个handleId,每个handleId只调用了repeat=1次Commit和Wait接口
for (int i = 0; i < tileCnt; ++i) {
	auto handleId = ReduceScatter(sendBuf, recvBuf, TileLen, HCCL_DATA_TYPE_FP32 , HCCL_REDUCE_SUM, TileLen*tileCnt, repeat = 1);
	Commit(handleId);
	auto ret = Wait(handleId);
	// 执行其他计算逻辑 ....
        
        // 更新ReduceScatter的收发地址
        constexper uint32_t kSizeOfFloat32 = 4U;
	sendBuf += TileLen * kSizeOfFloat32;
	recvBuf += TileLen * kSizeOfFloat32;
}

由于每张卡上3份数据的源地址SendBuf是连续的,且每张卡中目的地址recvBuf用来存储3份通信结果数据的内存也是连续的,因此以上代码可以优化,将ReduceScatter接口中的repeat参数设置为3,从而调用1次ReduceScatter接口,达到下发3个通信任务的效果。此时,只有1个handleId的任务,但是需要调用3次Commit和Wait接口,对应代码片段如下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const size_t rankSize = 4U; // 4张卡
const size_t tileCnt = 3U;  // 卡上的数据按照rankId均匀分成rankSize份,且每份又被切分成3份
const size_t TileLen = 100U;// 被切分后的每份数据个数

auto handleId = ReduceScatter(sendBuf, recvBuf, TileLen, HCCL_DATA_TYPE_FP32, HCCL_REDUCE_SUM, TileLen*tileCnt, repeat = tileCnt);

for (int i = 0; i < tileCnt; ++i) {
	Commit(handleId);
	auto ret = Wait(handleId);
	// 执行其他计算逻辑 ....
}
图2 ReduceScatter通信示例
表5 MC2_BUFFER_LOCATION参数说明

数据类型

说明

MC2_BUFFER_LOCATION

预留参数。计算和通信中间结果的Buffer存放位置。用户在Tiling侧可设置该字段。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
enum class MC2_BUFFER_LOCATION {
MC2_BUFFER_TYPE_DEFAULT = 0,
MC2_BUFFER_TYPE_OUTPUT,
MC2_BUFFER_TYPE_WINDOW_IN,
MC2_BUFFER_TYPE_WINDOW_OUT,
MC2_BUFFER_TYPE_WORKSPACE,
MC2_BUFFER_TYPE_INPUT,
MC2_BUFFER_TYPE_COMMOUT,
MC2_BUFFER_TYPE_END
}
搜索结果
找到“0”个结果

当前产品无相关内容

未找到相关内容,请尝试其他搜索词