昇腾社区首页
中文
注册

支持自动同步

使用Ascend C编写算子时,通过设置毕昇编译器自动同步编译选项 “--cce-auto-sync”(kernel直调算子工程和自定义算子开发工程已默认开启,无需开发者设置), 能够自动插入如下AI Core内部执行单元间的同步指令:

  • MTE2与Scalar之间
  • MTE3与Scalar之间
  • Vector与Scalar之间
  • Vector与Vector之间

Ascend C编程框架和编译器为开发者提供以下自动同步功能,详细内容请参考同步控制简介

  • 单流水同步:PIPE_V由编译器自动完成同步插入, PIPE_MTE2/PIPE_MTE3在搬运地址有重叠的情况下需要开发者插入同步。
  • 多流水同步:PIPE_V、PIPE_MTE2、PIPE_MTE3、PIPE_S之间的多流水同步,都是双向的,如下图所示,黄色线条表示的同步由编译器自动完成同步插入,剩余的同步由Ascend C框架完成。

自动同步使用约束

使用自动同步功能需要满足以下约束:核函数中调用的所有函数需要为inline函数;规范使用Ascend C编程模型

  • 核函数中调用的所有函数需要为inline函数
    下面的样例中,非inline函数不支持自动同步功能。
    ...
    // 算子类的实现中Process函数
    __aicore__ void Process()
    {
        CopyIn();
        Compute();
        CopyOut();
    }
    __aicore__ void CopyIn()
    {
        LocalTensor<int32_t> srcLocal = inQueueSrc.AllocTensor<int32_t>();
        DataCopy(srcLocal, srcGlobal, 512);
        inQueueSrc.EnQue(srcLocal);
    }
    __aicore__ void Compute()
    {
        LocalTensor<int32_t> srcLocal = inQueueSrc.DeQue<int32_t>();
        LocalTensor<int32_t> dstLocal = outQueueDst.AllocTensor<int32_t>()
        uint64_t mask = 64;
        Copy(dstLocal, srcLocal, mask, 4, { 1, 1, 8, 8 });
        outQueueDst.EnQue<int32_t>(dstLocal);
        inQueueSrc.FreeTensor(srcLocal);
    }
    __aicore__ void CopyOut()
    {
        LocalTensor<int32_t> dstLocal = outQueueDst.DeQue<int32_t>();
        DataCopy(dstGlobal, dstLocal, 512);
        outQueueDst.FreeTensor(dstLocal);
    }
    ...
    上述样例需要改写为如下形式才可以支持自动同步。
    ...
    // 算子类的实现中Process函数
    __aicore__ inline void Process()
    {
        CopyIn();
        Compute();
        CopyOut();
    }
    __aicore__ inline void CopyIn()
    {
        LocalTensor<int32_t> srcLocal = inQueueSrc.AllocTensor<int32_t>();
        DataCopy(srcLocal, srcGlobal, 512);
        inQueueSrc.EnQue(srcLocal);
    }
    
    __aicore__ inline void Compute()
    {
        LocalTensor<int32_t> srcLocal = inQueueSrc.DeQue<int32_t>();
        LocalTensor<int32_t> dstLocal = outQueueDst.AllocTensor<int32_t>()
        uint64_t mask = 64;
        Copy(dstLocal, srcLocal, mask, 4, { 1, 1, 8, 8 });
        outQueueDst.EnQue<int32_t>(dstLocal);
        inQueueSrc.FreeTensor(srcLocal);
    }
    
    __aicore__ inline void CopyOut()
    {
        LocalTensor<int32_t> dstLocal = outQueueDst.DeQue<int32_t>();
        DataCopy(dstGlobal, dstLocal, 512);
        outQueueDst.FreeTensor(dstLocal);
    }
    ...
  • 规范使用Ascend C编程模型

    下面的样例中,没有使用Ascend C编程模型(EnQue()、DeQue()、AllocTensor()、FreeTensor()接口),不支持自动同步。

    ...
    // 不使用Ascend C编程模型
    __aicore__ inline void CopyIn()
    {
        DataCopy(srcLocal, srcGlobal, 512);
    }
    __aicore__ inline void Compute()
    {
        for(int i = 0;i<dstDataSize; i++) {
           dstLocal.SetValue(i,srcLocal.GetValue(i));
        }
    }
    __aicore__ inline void CopyOut()
    {
        DataCopy(dstGlobal, dstLocal, 512);
    }
    private:
        TPipe pipe;
        LocalTensor<int32_t> srcLocal, dstLocal;
        GlobalTensor<int32_t> srcGlobal, dstGlobal;
        int dstDataSize = 512;
    ...

    需要改写为如下形式才可以支持自动同步。

    // 合理按照编程范式写法使用EnQue()、DeQue()、AllocTensor()以及FreeTensor()等内存管理与同步控制API接口
    ...
    __aicore__ inline void CopyIn()
    {
        LocalTensor<int32_t> srcLocal = inQueueSrc.AllocTensor<int32_t>();
        DataCopy(srcLocal, srcGlobal, 512);
        inQueueSrc.EnQue(srcLocal);
    }
    __aicore__ inline void Compute()
    {
        LocalTensor<int32_t> srcLocal = inQueueSrc.DeQue<int32_t>();
        LocalTensor<int32_t> dstLocal = outQueueDst.AllocTensor<int32_t>()
        for(int i = 0;i<dstDataSize; i++) {
           dstLocal.SetValue(i,srcLocal.GetValue(i));
        }
        outQueueDst.EnQue<int32_t>(dstLocal);
        inQueueSrc.FreeTensor(srcLocal);
    }
    __aicore__ inline void CopyOut()
    {
        LocalTensor<int32_t> dstLocal = outQueueDst.DeQue<int32_t>();
        DataCopy(dstGlobal, dstLocal, 512);
        outQueueDst.FreeTensor(dstLocal);
    }
    private:
        TPipe pipe;
        TQue<QuePosition::VECIN, 1> inQueueSrc;
        TQue<QuePosition::VECOUT, 1> outQueueDst;
        GlobalTensor<int32_t> srcGlobal, dstGlobal;
        int dstDataSize = 512;
    ...

自动同步debug日志功能

毕昇编译器提供“--cce-auto-sync-log=<file>”编译选项可以输出同步插入信息到<file>文件中,帮助开发者显式地识别编译器在算子文件中插入的同步指令信息。需debug模式(添加-g编译选项)编译算子,用于获取算子代码文件行号。

  • 直接使用毕昇编译器的场景,可以直接在编译命令中添加该编译选项
  • 使用Ascend C kernel直调算子工程,可以通过ascendc_compile_options添加该编译选项
  • 使用Ascend C自定义算子开发工程,可以通过add_ops_compile_options添加该编译选项

如下的代码文件sync_log_test.h:

LocalTensor<T> dstLocal;
T ave_tmp = 0;
Vector_OP1(dstLocal, params); 
ave_tmp = dstLocal.GetValue(0);
Vector_OP2(dstLocal, params); 
for (int i = 0; i < ave_tmp; ++i) {
    dstLocal.SetValue(i,0);
}

开启自动同步后,同步指令的插入位置如下:

LocalTensor<T> dstLocal;
T ave_tmp = 0;
Vector_OP1(dstLocal, params); 
SetFlag<HardEvent::V_S>(EVENT_ID0);
WaitFlag<HardEvent::V_S>(EVENT_ID0);
ave_tmp = dstLocal.GetValue(0);
PipeBarrier<PIPE_V>();
SetFlag<HardEvent::S_V>(EVENT_ID0);
WaitFlag<HardEvent::S_V>(EVENT_ID0);
Vector_OP2(dstLocal, params); 
SetFlag<HardEvent::V_S>(EVENT_ID0);
WaitFlag<HardEvent::V_S>(EVENT_ID0);
for (int i = 0; i < ave_tmp; ++i) {
    dstLocal.SetValue(i,0);
}

开启自动同步debug日志功能后,输出日志如下:

The BiSheng Auto Sync log of sync_log_test :  
Position: absolute-path/sync_log_test.h:4 : line before insert sync : SetFlag<HardEvent::V_S>(EVENT_ID0);
Position: absolute-path/sync_log_test.h:4 : line before insert sync : WaitFlag<HardEvent::V_S>(EVENT_ID0);
Position: absolute-path/sync_log_test.h:5 : line before insert sync : PipeBarrier<PIPE_V>();
Position: absolute-path/sync_log_test.h:5 : line before insert sync : SetFlag<HardEvent::S_V>(EVENT_ID0);
Position: absolute-path/sync_log_test.h:5 : line before insert sync : WaitFlag<HardEvent::S_V>(EVENT_ID0);
Position: absolute-path/sync_log_test.h:6 : line before insert sync : SetFlag<HardEvent::V_S>(EVENT_ID0);
Position: absolute-path/sync_log_test.h:6 : line before insert sync : WaitFlag<HardEvent::V_S>(EVENT_ID0);

其中,line before表示紧接着当前行前面插入的同步指令。