开发者
资源

创建资源

通信资源计算

通信算子在执行时依赖底层的硬件通信资源,因此在算子任务编排下发前,首先需要申请所需的通信资源。AIV通信算子需要的通信资源主要分为三类:
  • Vector Core:用于表达单个rank内的并发关系。在算法编排过程中,可将不同rank或不同数据块拆分,并在多个计算核上并行处理。多个AI Core共享同一份代码逻辑,各个计算核上运行的实例之间唯一的区别在于核ID(下标)不同,每个核根据自己的下标来确定所走的算法分支流程。
  • Notify:用于实现同步机制,包括rank内不同Vector Core之间的同步以及rank间的同步。由于AI Core内无法调用硬件能力来实现rank间同步,因此需要通过软件算法来模拟同步功能的实现,称为软同步,软同步使用到的Notify本质是一块片上内存。
  • Channel:用于跨rank传输数据的通道。

不同的通信算法所需的通信资源类型和数量往往不相同。下面以单机4卡的拓扑组网、使用AIV通信引擎为例,讲解Mesh全连接通信算法所需的通信资源数量计算逻辑。

Mesh算法的硬件拓扑如下图所示,每个rank与其他所有rank直接通信。

上述组网中Mesh全连接通信算法所需要申请或者占用的通信资源数量如下:

  • Vector Core:每个rank需要跟其他所有rank进行通信,将每个rank上的通信任务(包含本rank的本地拷贝)分配给不同的核异步执行。因此,每个rank共需要占用4个Vector Core资源,这些资源在算子下发时由系统自动分配,无需提前申请。
  • Notify:每个rank需与其他所有rank进行同步,同步操作分为前同步和后同步,二者需要配对使用。因此,每个rank共需要6个Notify资源,每个软同步Notify资源分配32 Byte的片上内存,通常可直接预留1KB至1MB左右的空间。
  • Channel:每个rank需要与其他所有rank进行通信,因此每个rank共需要建立3条Channel通信通道。

代码示例

下面以AllGather通信算子为例,介绍其在Host上的资源创建代码片段。

  1. 申请Context内存,用以存储资源信息。
    uint64_t size = 需要申请的Context内存的大小;
    void *ctx = nullptr;
    char *tag = 保存资源需要用到的tag;
    CommEngine engine = COMM_ENGINE_CPU_TS; // 使用Host侧内存缓存资源
    HcclResult ret = HcclEngineCtxGet(comm, tag, engine, &ctx, &size);
    if (ret != HCCL_SUCCESS) {
       // 即之前没有创建过tag表示的资源
       HcclEngineCtxCreate(comm, tag, engine, size, ctx); 
    }else {
       // 说明之前创建过资源,直接用ctx就好
    }
  1. 申请Notify资源。
    char tag[] = "allgather";
    CommEngine engine = CommEngine::COMM_ENGINE_AIV;                   // AIV通信引擎
    void* aivTagBufPtr = nullptr;                                      // 软同步标记区地址
    HcclEngineCtxCreate(comm, tag, engine, AIV_TAG_BUFF_LEN, &aivTagBufPtr);  // 创建Notify资源
    aclrtMemset(aivTagBufPtr, AIV_TAG_BUFF_LEN, 0, AIV_TAG_BUFF_LEN);  // 清零标记区
  2. 将Notify软同步内存注册到通信域,建立通信链路Channel,两个rank之间建立1条通信通道。
    HcclMemHandle memHandle;
    CommMem regMem{COMM_MEM_TYPE_DEVICE, aivTagBufPtr, AIV_TAG_BUFF_LEN};
    HcclCommMemReg(comm, tag, &regMem, &memHandle); // 将软同步内存注册到通信域
    
    HcclChannelDesc channelDesc;
    HcclChannelDescInit(&channelDesc, 1);
    channelDesc.remoteRank = destRank;
    channelDesc.channelProtocol = CommProtocol::COMM_PROTOCOL_UB_MEM;
    channelDesc.memHandles = &memHandle;           // 建链时指定交换软同步内存
    channelDesc.memHandleNum = 1;
    ChannelHandle channelHandle;
    HcclChannelAcquire(comm, engine, &channelDesc, 1, &channelHandle);
  3. 分配通信内存。
    // 分配并获取本端通信内存
    void *localAddr;
    uint64_t localSize;
    HcclGetHcclBuffer(comm, &localAddr, &localSize);
    // 获取远端通信内存,用于后续读写操作
    void *remoteAddr;
    uint64_t remoteSize;
    HcclChannelGetHcclBuffer(comm, channelHandle, &remoteAddr, &remoteSize);
    // 获取远端软同步标记区内存,用于后续同步操作
    uint32_t memNum;
    CommMem* remoteMems;
    char** memTags;
    HcclChannelGetRemoteMems(comm, channelHandle, &memNum, &remoteMems, &memTags);
    void *remoteTagBufAddr = remoteMems[0].addr;