[AI][昇腾950]同步BufID 方案
Ascend 950PR (Ascend950) bufid 原理、实现约束与使用约束目标芯片Ascend 950PR / DTAtlas A5__NPU_ARCH__ Ascend950核心结论bufidbuffer ID与mutex_id互斥锁编号是同一个硬件资源——一个 5 位编号[0, 31]命名 32 个硬件取缓冲/释放缓冲信号量。底层指令get_buf(pipe, bufId, mode)/rls_buf(pipe, bufId, mode)2201 差异2201无 mutex 能力使用set_flag/wait_flag替代1. bufid 是什么1.1 定义在 Ascend950 上每个 AI Core 内置32 个硬件缓冲锁槽位buffer-lock slots每个槽位由一个 5 位编号bufid ∈ [0, 31]标识。每个槽位是一个二值信号量可被核内异步流水线MTE2/MTE1/M/V/MTE3/FIX/S通过get_buf/rls_buf指令获取与释放。1.2 类型与常量// include/basic_api/kernel_common.h:143usingMutexIDuint8_t;// impl/basic_api/kernel_event.h:708-710constexprTBufId INVALID_TBUFID255;// static_castuint8_t(-1)constexprTBufId MAX_TBUFID31;// 硬件上限constexprTBufId MAX_MUTEXID27;// 用户可用上限28~31 系统预留autoidAllocMutexID();Mutex::Lockpipe(id);xxx Mutex::UnLockpipe(id);三种用途共享同一 32 槽位池由AllocMutexID/ReleaseMutexID统一分配。2. 硬件原理2.1 信号量机制每个 bufid 槽位是一个硬件二值信号量。get_buf(pipe, id, mode)在pipe流水线上尝试获取槽位id若槽位空闲获取成功pipe流水线继续执行后续指令若槽位已被占用前序get_buf未配对rls_buf阻塞该 pipe 流水线BLOCK 模式或继续执行NON_BLOCK 模式rls_buf(pipe, id, mode)释放槽位id使等待该槽位的后续get_buf解除阻塞。2.2 跨流水线同步语义bufid 的核心价值在于跨异步流水线建立依赖。典型场景MTE2 流水: DataCopy(GM→UB) → rls_buf(MTE2, id) // 数据就绪释放锁 V 流水: get_buf(V, id) → Add → rls_buf(V, id) // 等数据就绪后计算 MTE3流水: get_buf(MTE3, id) → DataCopy(UB→GM) // 等计算完成后搬出get_buf/rls_buf配对在不同流水线上形成 happens-before 链无需set_flag/wait_flag。3. Basic-APIMutex::Lock/Mutex::UnlockusingMutexIDuint8_t;classMutex{public:templatepipe_t pipestatic__aicore__inlinevoidLock(MutexID id);// 仅 Ascend950/5102 编译templatepipe_t pipestatic__aicore__inlinevoidUnlock(MutexID id);// 仅 Ascend950/5102 编译};实现特点运行时ASCENDC_ASSERT(id MAX_MUTEXID)即id 27比 C-API 更严格3.1 ID 分配器AllocMutexID/ReleaseMutexID__aicore__inlineMutexIDAllocMutexID();// 返回最低空闲位__aicore__inlinevoidReleaseMutexID(MutexID id);实现kernel_common.h:172-186__BLOCK_LOCAL__ __inline__uint32_tg_bufId;// 32 位掩码每 bit 对应一个槽位MutexID idstatic_castuint8_t(sff0(Internal::g_bufId));// find-first-zeroInternal::g_bufIdsbitset1(Internal::g_bufId,id);// 置位ASCENDC_ASSERT(idMAX_MUTEXID,...);returnid;__BLOCK_LOCAL__每 AI Core block 独立一份非 per-pipe、非 per-subblock4. 实现约束4.1 编号范围约束AllocMutexIDReleaseMutexID只分配 0~27, 超过范围运行时 assert文档明确“28-31为系统内部规划预留不建议使用。”4.2 模式约束Basic-APIMutex::Lock/Unlock配对的 lock/unlock 必须使用相同 mode否则硬件行为不可预测4.3 配对规则同一mutex_id的Mutex::Lock必须在unlock之前程序序lock/unlock必须严格配对lock, unlock, lock, unlock不可lock, lock, unlock, unlock配对的lock/unlock必须使用相同mutex_id和相同mode配对的lock/unlock必须使用相同pipe4.4 禁止嵌套相同mutex_id的 lock/unlock 对不可嵌套无论pipe/mode是否相同否则硬件行为不可预测。错误: lock(id1) → lock(id1) → unlock(id1) → unlock(id1) // 嵌套 正确: lock(id1) → unlock(id1) → lock(id1) → unlock(id1) // 顺序4.5 同流水同 ID 的失效陷阱同一pipe 同一mutex_id连续出现两次 lock/unlock 对时第二次 lock 不会阻塞流水无法建立同步。同流水线内部依赖应使用PipeBarrierpipe()。4.6 双缓冲交替模式推荐用法为允许流水重叠分配两个MutexID按迭代交替使用uint8_tid0AllocMutexID();uint8_tid1AllocMutexID();for(inti0;in;i){uint8_tid(i%20)?id0:id1;// 用 id 同步 MTE2→V→MTE3}ReleaseMutexID(id0);ReleaseMutexID(id1);这样第 N1 次的 MTE2 可与第 N 次的 V/MTE3 并行不同 id 不互斥。4.7 多锁联合join 依赖同一流水可同时持有多个锁表达多源依赖的 joinMutex::Lock(PIPE_V,input_mutex);// 等输入就绪Mutex::Lock(PIPE_V,output_mutex);// 等上次输出缓冲释放// ... 计算 ...Mutex::Unlock(PIPE_V,input_mutex);Mutex::Unlock(PIPE_V,output_mutex);4.8 跨核同步不适用bufid/mutex 是核内机制。跨核cross-core同步使用ffts_cross_core_sync/wait_flag_dev flag ID不使用bufid。core_mng/roc/的 cube_group/group_barrier 也用 event ID不涉及 bufid。5. 实战示例5.1 Basic-API 双缓冲流水官方示例__aicore__inlinevoidProcess(){// 分配双缓冲 tensorLocalTensorfloatxLocal0ubAllocator.Allocfloat,TILE_LENGTH();LocalTensorfloatxLocal1ubAllocator.Allocfloat,TILE_LENGTH();// ... yLocal0/1, zLocal0/1 ...uint8_tmutexId0AscendC::AllocMutexID();uint8_tmutexId1AscendC::AllocMutexID();for(int32_ti0;iloopCount;i){uint8_tmutexId(i%20)?mutexId0:mutexId1;autox(i%20)?xLocal0:xLocal1;autoy(i%20)?yLocal0:yLocal1;autoz(i%20)?zLocal0:zLocal1;// MTE2: GM → UBAscendC::Mutex::LockPIPE_MTE2(mutexId);AscendC::DataCopy(x,src0Global[...],TILE_LENGTH);AscendC::DataCopy(y,src1Global[...],TILE_LENGTH);AscendC::Mutex::UnlockPIPE_MTE2(mutexId);// V: AddAscendC::Mutex::LockPIPE_V(mutexId);AscendC::Add(z,x,y,TILE_LENGTH);AscendC::Mutex::UnlockPIPE_V(mutexId);// MTE3: UB → GMAscendC::Mutex::LockPIPE_MTE3(mutexId);AscendC::DataCopy(dstGlobal[...],z,TILE_LENGTH);AscendC::Mutex::UnlockPIPE_MTE3(mutexId);}AscendC::ReleaseMutexID(mutexId0);AscendC::ReleaseMutexID(mutexId1);}