news 2026/5/30 15:58:08

CANNBot MrgSort API指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANNBot MrgSort API指南

MrgSort API 使用指南

【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills

适用场景:使用排序/归并 API(Sort/Concat/MrgSort/Extract)时,正确选择 API、计算偏移、避免常见错误。


概述

排序类 API 支持 tile 内排序和 4-way 外部归并:

API功能910b 支持使用场景
Sort<T, true>tile 内排序Phase 1 tile 排序
Concat合成 proposalSort 前准备
MrgSort<T, true>4 路归并(高阶)Phase 2/3/4 归并
MrgSort4 路归并(基础)Phase 2/3/4 归并
MrgSort44 路归并(基础)禁止使用
Extract分离 proposalPhase 4 输出
ArithProgression索引初始化Phase 1 索引生成

辅助 API(tmpBuffer 大小计算)

API功能使用场景
GetSortTmpSize计算 Sort tmpBuffer 大小Phase 1 UB 规划
GetConcatTmpSize计算 Concat tmpBuffer 大小Phase 1 UB 规划
GetSortLen根据 Sort 数据量获取 proposal 格式字节大小proposal 格式地址转换
GetSortOffset根据 Sort 数据索引获取 proposal 格式字节偏移量proposal 格式地址转换

API 选择对照表

平台排序 API归并 API禁用 API
ascend910b (A2)Sort<T, true>MrgSort<T, true>MrgSortMrgSort4

关键约束:

  • MrgSort4基础 API 在 910b 上触发 VEC_ERROR
  • 归并 API 输入的 4 路数据必须各自有序

场景1:tile 内排序(Sort + Concat)

API 接口

Sort 高阶 API:

AscendC::Sort<T, true>(dstProposal, srcValue, srcIndex, tmpBuffer, repeatTimes); // 参数: // dstProposal: 输出 LocalTensor(proposal 格式,排序结果) // srcValue: 输入 value LocalTensor(proposal 格式,由 Concat 合成) // srcIndex: 输入 index LocalTensor(uint32_t 类型索引数组) // tmpBuffer: 临时 buffer // repeatTimes: repeat 次数 = ceil(alignTileNum / TOPK_SORT_NUM)

ArithProgression API(索引初始化):

ArithProgression<T>(dst, startValue, stride, count); // 参数: // dst: 输出 LocalTensor(存放生成的序列) // startValue: 起始值 // stride: 步长(相邻元素的差值) // count: 元素个数

Concat API:

Concat(dst, srcValue, tmpBuffer, repeatTimes); // 参数: // dst: 输出 LocalTensor(proposal 格式,每 2 个 float 组成 1 个 proposal) // srcValue: 输入 value LocalTensor(float 类型) // tmpBuffer: 临时 buffer // repeatTimes: repeat 次数 = ceil(alignTileNum / TOPK_CONCAT_NUM)

完整示例

// Phase 1: tile 排序完整流程 // 1. DataCopyPad 输入数据 DataCopyPad(inputLocal, inputValueGm_[offsetPerCore], copyParams, padParams); // 2. 类型转换(如 bf16 → float) uint32_t alignTileNum = (tileNum + TOPK_BLOCK_UB - 1) / TOPK_BLOCK_UB * TOPK_BLOCK_UB; Cast(inputValueTempLocal, inputLocal, AscendC::RoundMode::CAST_NONE, alignTileNum); // 3. 初始化索引(Sort 需要的 index buffer) LocalTensor<int32_t> tempIndexLocal = sortedValueIndexLocal.ReinterpretCast<int32_t>(); ArithProgression<int32_t>(tempIndexLocal, static_cast<int32_t>(offsetPerCore), 1, tileNum); // 4. Concat 合成 proposal(仅 value 部分) LocalTensor<float> concatTempLocal = concatTempBuf_.Get<float>(); uint32_t concatRepeatTimes = (alignTileNum + TOPK_CONCAT_NUM - 1) / TOPK_CONCAT_NUM; LocalTensor<float> concatLocal; Concat(concatLocal, inputValueTempLocal, concatTempLocal, concatRepeatTimes); // 输出:concatLocal 中每 2 个 float 组成 1 个 proposal(value + 待填充的 index 位) // 5. Sort 排序(输出 value + index) LocalTensor<float> sortTempLocal = sortTempBuf_.Get<float>(); uint32_t sortRepeatTimes = (alignTileNum + TOPK_SORT_NUM - 1) / TOPK_SORT_NUM; Sort<float, true>(sortedValueLocal, concatLocal, sortedValueIndexLocal, sortTempLocal, sortRepeatTimes); // 输出:降序排列的 sortedValueLocal(proposal 格式)和 sortedValueIndexLocal(index) // 6. CopyOut 到 workspace DataCopyPad(workspaceGm_[GetSortLen<float>(offsetPerCore)], sortedValueLocal, copyParams);

tmpBuffer 大小计算

GetSortTmpSize(计算 Sort tmpBuffer 大小):

uint32_t sortTmpSize = AscendC::GetSortTmpSize(platform, TILE_SIZE, 4); // 参数: // platform: 平台信息(SocVersion) // TILE_SIZE: 排序元素数 // 4: proposal 大小因子(固定值)

GetConcatTmpSize(计算 Concat tmpBuffer 大小):

uint32_t concatTmpSize = AscendC::GetConcatTmpSize(platform, TILE_SIZE, 4); // 参数: // platform: 平台信息(SocVersion) // TILE_SIZE: 合并元素数 // 4: proposal 大小因子(固定值)

教训:调用高阶 API 前,必须查阅 API 文档确认 tmp buffer 需求,使用文档提供的接口获取大小,并在 UB 空间分配中预留相应空间。


场景2:4 路归并(MrgSort)

高阶 API vs 基础 API

API函数签名消耗计数获取推荐
MrgSort<T, true>6 参数高阶sortedNums[4]直接返回⭐⭐⭐⭐⭐
MrgSort3 参数基础GetMrgSortResult⭐⭐⭐
MrgSort43 参数基础GetMrgSortResult禁止

高阶 API 签名:

AscendC::MrgSort<T, true>(dst, srcList, elementCountList, sortedNums, validBitTail, exhaustion); // 参数: // dst: 输出 LocalTensor(proposal 格式) // srcList: 4 路输入源列表(MrgSortSrcList) // elementCountList: 4 路长度数组(uint16_t[4]) // sortedNums: 输出参数,有效路消耗计数(uint32_t[4]) // validBitTail: 有效路掩码(uint8_t) // exhaustion: exhaustion 模式(通常为 1)

sortedNums 返回值说明

关键约束:sortedNums数组仅填充有效路对应的元素,按有效路顺序紧凑排列:

活跃路数sortedNums 有效元素索引访问方式
4 路sortedNums[0..3]全部有效按 validBitTail 顺序访问
3 路sortedNums[0..2]有效,sortedNums[3]未填充仅访问前 3 个
2 路sortedNums[0..1]有效仅访问前 2 个
1 路sortedNums[0]有效仅访问第 1 个

注意: 无效路对应的sortedNums元素不一定为 0,不应直接用sortedNums[i]按 4 路索引访问,应配合有效路计数器j紧凑访问。

validBitTail 设置

活跃路数validBitTailMrgSortSrcList 构建
4 路0b1111MrgSortSrcList(src0, src1, src2, src3)
3 路0b0111MrgSortSrcList(src0, src1, src2, src0)
2 路0b0011MrgSortSrcList(src0, src1, src0, src0)
1 路0b0001MrgSortSrcList(src0, src0, src0, src0)

状态更新

// 每次 MrgSort 后,更新 offsets_ 和剩余计数 curLoopSortedNum_ = 0; for (int64_t i = 0, j = 0; i < 4; i++) { if (dealLengths_[i] > 0) { offsets_[i] += GetSortOffset<float>(sortedNums[j]); // 累加偏移 listRemainElements_[i] -= sortedNums[j]; // 扣减剩余 allRemainElements_ -= sortedNums[j]; // 总量扣减 curLoopSortedNum_ += sortedNums[j]; j++; } }

完整归并循环示例

以下为多路归并的通用循环结构,适用于 Phase 2/3/4 中每个 group 的归并过程。

核心循环结构
// 每轮归并中,遍历所有 group,每个 group 执行多批次归并 uint32_t totalGroups = (listNum_ + TOPK_LIST_MAX - 1) / TOPK_LIST_MAX; for (uint32_t g = 0; g < totalGroups; g++) { // ─── 步骤 1:初始化当前 group ─── InitGroup(g, /* context-specific params */); // 设置: offsets_[0..3], listRemainElements_[0..3], allRemainElements_ // allRemainElements_ = 该 group 4 路输入的总 proposal 数 // ─── 步骤 2:批次归并循环 ─── int64_t stopThreshold = allRemainElements_ - kVal_; for (; allRemainElements_ > stopThreshold;) { CopyInMultiCore(); // 从 workspace 搬入 UB(每路最多 onceMaxElements_) UpdateMrgParam(); // 设置 validBitTail_ + 清零无效路 DealingMergeSort(); // 执行 MrgSort 或单路 DataCopy UpdateSortInfo(); // ★ 必须调用:更新 offsets_ / remain / allRemain CopyOutMultiCore(); // 搬出归并结果到 workspace } // ─── 步骤 3:清理当前 group 状态 ─── ClearCache(); }

循环退出条件allRemainElements_ > stopThreshold,即当剩余输入总量降至 stopThreshold 以下时退出。这意味着该 group 的归并输出大于 K 个元素后停止归并。

截断归并变体

当需要严格限制输出不超过 K 时,增加outputCount控制:

// 截断归并:输出量受 outputCount 限制 InitGroup(g, baseOffset); int64_t outputCount = 0; // 退出条件:无剩余元素 OR 已输出 ≥ K for (; allRemainElements_ > 0 && outputCount < static_cast<int64_t>(kVal_);) { CopyInMultiCore(); UpdateMrgParam(); DealingMergeSort(); UpdateSortInfo(); CopyOutMultiCore(); outputCount += curLoopSortedNum_; // 累加本轮输出 } ClearCache();
关键子函数详解

1. InitGroup — 设置当前 group 的读取偏移和长度

// 通用 Init 逻辑(Phase 2 核内版本): // 根据 truncationFlag_(上一轮是否截断)决定本轮有效读取长度 __aicore__ inline void InitGroup(uint32_t groupIdx, int64_t baseOffset) { // effectiveLength: 上一轮输出正常 → 读完整 currentElements_ // 上一轮已截断 → 读 min(currentElements_, K) int64_t effectiveLength = truncationFlag_ ? min(currentElements_, static_cast<int64_t>(kVal_)) : currentElements_; int64_t effectiveTailLength = truncationFlag_ ? min(currentTailElements_, static_cast<int64_t>(kVal_)) : currentTailElements_; for (int64_t i = 0; i < TOPK_LIST_MAX; i++) { uint32_t blockNum = groupIdx * TOPK_LIST_MAX + i; if (blockNum < listNum_ - 1) { // 非尾块:使用 effectiveLength listRemainElements_[i] = effectiveLength; offsets_[i] = baseOffset + GetSortOffset<float>(blockNum * currentElements_); } else if (blockNum == listNum_ - 1) { // 尾块:使用 effectiveTailLength(可能 < effectiveLength) listRemainElements_[i] = effectiveTailLength; offsets_[i] = baseOffset + GetSortOffset<float>(blockNum * currentElements_); } else { listRemainElements_[i] = 0; // 无效路 } } }

2. CopyInMultiCore — 从 workspace 搬入 UB

// 将 1-4 路数据从 workspace 搬入 copyInQueue_ // 关键变量: // onceMaxElements_: 单路单次最大搬入量(UB 容量决定) // dealLengths_[i]: 本路本次实际搬入量 = min(onceMaxElements_, listRemainElements_[i]) // elementCountList_: 紧凑索引(j)填充,仅有效路被填充 // remainListNum_: 实际有效路数(1-4) __aicore__ inline void CopyInMultiCore() { LocalTensor<float> ubInput = copyInQueue_.AllocTensor<float>(); remainListNum_ = 0; for (int64_t i = 0, j = 0; i < TOPK_LIST_MAX; i++) { dealLengths_[i] = (onceMaxElements_ > listRemainElements_[i]) ? listRemainElements_[i] : onceMaxElements_; if (dealLengths_[i] > 0) { // 从 workspace 读取,无需 padding DataCopyPad(ubInput[GetSortLen<float>(onceMaxElements_) * i], workspaceInput_[offsets_[i]], copyParams, padParams); elementCountList_[j] = static_cast<uint16_t>(dealLengths_[i]); remainListNum_++; j++; // 紧凑索引:仅有效路递增 } } copyInQueue_.EnQue(ubInput); }

3. UpdateMrgParam — 设置 validBitTail_ + 清零无效路

// 根据 remainListNum_(1-4)设置 validBitTail_ 并清零无效路的 elementCountList_ // validBitTail_ 告诉 MrgSort API 哪些路是有效的 __aicore__ inline void UpdateMrgParam() { if (remainListNum_ == 4) { validBitTail_ = 0b1111; } else if (remainListNum_ == 3) { elementCountList_[3] = 0; validBitTail_ = 0b0111; } else if (remainListNum_ == 2) { elementCountList_[2] = 0; elementCountList_[3] = 0; validBitTail_ = 0b0011; } else { // remainListNum_ == 1 elementCountList_[1] = 0; elementCountList_[2] = 0; elementCountList_[3] = 0; validBitTail_ = 0b0001; } }

4. DealingMergeSort — 执行 MrgSort 或单路 DataCopy

// remainListNum_ == 1 时无需归并,直接 DataCopy // 无效路用第 0 路填充 MrgSortSrcList(MrgSort 要求固定 4 路输入) __aicore__ inline void DealingMergeSort() { LocalTensor<float> sortBuffer = sortedQueue_.AllocTensor<float>(); LocalTensor<float> ubInput = copyInQueue_.DeQue<float>(); // 按紧凑索引 j 组织有效路 LocalTensor<float> tmpUbInputs[4]; for (int64_t i = 0, j = 0; i < TOPK_LIST_MAX; i++) { if (dealLengths_[i] > 0) { tmpUbInputs[j] = ubInput[GetSortLen<float>(onceMaxElements_) * i]; j++; } } if (remainListNum_ == 4) { MrgSortSrcList<float> sl(tmpUbInputs[0], tmpUbInputs[1], tmpUbInputs[2], tmpUbInputs[3]); MrgSort<float, true>(sortBuffer, sl, elementCountList_, listSortedNums_, validBitTail_, 1); } else if (remainListNum_ == 3) { MrgSortSrcList<float> sl(tmpUbInputs[0], tmpUbInputs[1], tmpUbInputs[2], tmpUbInputs[0]); MrgSort<float, true>(sortBuffer, sl, elementCountList_, listSortedNums_, validBitTail_, 1); } else if (remainListNum_ == 2) { MrgSortSrcList<float> sl(tmpUbInputs[0], tmpUbInputs[1], tmpUbInputs[0], tmpUbInputs[0]); MrgSort<float, true>(sortBuffer, sl, elementCountList_, listSortedNums_, validBitTail_, 1); } else { // remainListNum_ == 1: 无归并操作,直接拷贝 DataCopy(sortBuffer, tmpUbInputs[0], static_cast<uint32_t>(TopkAlign( GetSortLen<float>(elementCountList_[0]), sizeof(float)))); listSortedNums_[0] = elementCountList_[0]; // 手动设置消耗计数 } sortedQueue_.EnQue(sortBuffer); copyInQueue_.FreeTensor(ubInput); }

5. UpdateSortInfo — 更新偏移和剩余计数(★ 必须调用)

// ★ 关键:每个归并循环都必须调用,否则 allRemainElements_ 不更新 → 死循环 // sortedNums 按紧凑索引 j 访问,仅有效路有值 __aicore__ inline void UpdateSortInfo() { curLoopSortedNum_ = 0; for (int64_t i = 0, j = 0; i < TOPK_LIST_MAX; i++) { if (dealLengths_[i] > 0) { listRemainElements_[i] -= listSortedNums_[j]; // 扣减该路剩余 allRemainElements_ -= listSortedNums_[j]; // 扣减总量 offsets_[i] += GetSortOffset<float>(listSortedNums_[j]); // 推进读取偏移 curLoopSortedNum_ += listSortedNums_[j]; // 累计本轮输出 j++; } } }

6. CopyOutMultiCore — 搬出归并结果到 workspace

// 将 sortedQueue_ 中的归并结果搬出到 workspace 当前 group 的输出位置 __aicore__ inline void CopyOutMultiCore() { LocalTensor<float> sortBuffer = sortedQueue_.DeQue<float>(); DataCopyParams copyParams; copyParams.blockCount = 1; copyParams.blockLen = GetSortLen<float>(curLoopSortedNum_) * sizeof(float); copyParams.srcStride = 0; copyParams.dstStride = 0; DataCopyPad(workspaceOutput_[wsOutOffset_], sortBuffer, copyParams); wsOutOffset_ += GetSortLen<float>(curLoopSortedNum_); sortedQueue_.FreeTensor(sortBuffer); }

7. ClearCache — 归并组结束后状态重置

// 每个 group 归并完成后调用,为下一个 group 准备干净状态 __aicore__ inline void ClearCache() { allRemainElements_ = 0; wsOutOffset_ = 0; gmOutOffset_ = 0; remainListNum_ = 0; for (int64_t i = 0; i < TOPK_LIST_MAX; i++) { offsets_[i] = 0; listRemainElements_[i] = 0; elementCountList_[i] = 0; } }
调用顺序约束
InitGroup → (循环){CopyInMultiCore → UpdateMrgParam → DealingMergeSort → UpdateSortInfo → CopyOutMultiCore} → ClearCache

必须严格按此顺序调用,跳步或乱序将导致: | 错误 | 后果 | |------|------| | 缺少UpdateSortInfo()|allRemainElements_不更新 →死循环| | 缺少ClearCache()| 状态残留 → 下一 group offset 错误 →越界| |UpdateMrgParamCopyIn之前 |elementCountList_未初始化 → MrgSort 入参错误 | |DealingMergeSortUpdateMrgParam之前 |validBitTail_未设置 → 归并结果异常 |


场景3:proposal 分离(Extract)

// Extract: 从 proposal 格式分离 value 和 index LocalTensor<float> castValue = castValueQueue_.AllocTensor<float>(); LocalTensor<uint32_t> castIndex = castIndexQueue_.AllocTensor<uint32_t>(); LocalTensor<float> sortTempBuffer = sortedQueue_.DeQue<float>(); uint32_t extractRepeatTimes = (curLoopSortedNum_ + 32 - 1) / 32; Extract(castValue, castIndex, sortTempBuffer, extractRepeatTimes); // Cast 回原类型(如 float → bf16) Cast(ubOutput1, castValue, AscendC::RoundMode::CAST_RINT, sortedValueAlign); // CopyOut 到 GM DataCopyPad(outValueGm_[outOffset_], ubOutput1, copyParamsValue); DataCopyPad(outIndexGm_[outOffset_], ubOutput2, copyParamsIndex);

proposal 格式详解

格式结构

proposal 格式: 每个 proposal 占8 字节

| 字段 | 偏移 | 大小 | |------|------|------| | value (float) | +0 | 4B | | index (uint32) | +4 | 4B |

GetSortLen / GetSortOffset 单位

函数返回值单位用途
GetSortLen<T>(n)float 数= n × 2proposal 数量转为 float 索引
GetSortOffset<T>(n)float 数= n × 2proposal 偏移转为 float 索引

正确使用

// ✅ 正确:使用 GetSortOffset int64_t floatOffset = GetSortOffset<float>(proposalIdx); // = proposalIdx × 2 // ✅ 正确:直接 × 2 int64_t floatOffset = proposalIdx * 2;

排序稳定性说明

实现稳定性索引行为
AscendCSort<T, true>稳定排序相同 value 时保留原始索引顺序
AscendCMrgSort<T, true>稳定排序归并排序天然是稳定排序
PyTorchtorch.topk非稳定排序相等元素索引顺序不确定
NumPynumpy.argsort稳定排序默认稳定

精度测试建议

  • value 精度是核心指标,必须 100%
  • index 精度在有重复值时不应严格比对(稳定/非稳定差异)
  • 如需严格 index 精度,需确认输入无重复值或使用稳定排序参考实现

常见错误

错误原因解决方案
死循环归并循环缺少UpdateSortInfo()每个归并循环都必须调用
VEC_ERROR使用MrgSort4(910b 禁用)使用MrgSortMrgSort<T, true>
归并结果错误输入 4 路数据不是各自有序正确计算地址偏移
越界崩溃proposal 偏移用 4B使用GetSortOffset<T>(n)× 2
偏移理解错误buf[proposalIdx * 4]混淆了 proposal 大小(8B)和 float 大小(4B),误将 proposal 偏移等同于 4B 的 float 偏移proposal 偏移应使用GetSortOffset<float>(proposalIdx)(= proposalIdx × 2 个 float),而非proposalIdx × 4
队列类型错误copyInQueue_定义为VECOUT定义为TQue<QuePosition::VECIN, ...>
使用自定义函数用自定义函数替代GetSortLen/GetSortOffset使用 AscendC 内置 API
缺少事件同步ArithProgression 前未等待 MTE2→V添加WaitFlag<HardEvent::MTE2_V>

错误示例

// ❌ 错误:使用 MrgSort4 MrgSort4(ubDst, srcList, info); // 910b 触发 VEC_ERROR // ✅ 正确:使用 MrgSort MrgSort(ubDst, srcList, info); MrgSort<float, true>(dst, srcList, elementCountList, sortedNums, validBit, 1);
// ❌ 错误:地址偏移计算导致某一路输入内部无序 int64_t offset = blockIdx * kVal_; // 假设每路长度为 K // 实际上 workspace 中每路间隔可能是 addressStride_ × 4 // ✅ 正确:地址间隔与有效长度分开计算 int64_t addressStride = addressStride_ * 4; // 地址间隔 int64_t effectiveLength = min(addressStride_, kVal_); // 有效长度 offsets[i] = baseOffset + blockIdx * addressStride;
// ❌ 错误:proposal 偏移用 4B float* valuePtr = workspace + proposalIdx * 4; // ✅ 正确:proposal 偏移用 × 2(每个 proposal = 2 个 float) int64_t floatOffset = GetSortOffset<float>(proposalIdx); // = proposalIdx × 2
// ❌ 错误:使用自定义函数替代内置 API __aicore__ inline int64_t GetSortLenFloat(int64_t n) { return n * 2; } int64_t offset = GetSortLenFloat(proposalIdx); // ✅ 正确:使用 AscendC 内置 API int64_t offset = GetSortLen<float>(proposalIdx);
// ❌ 错误:索引初始化缺少事件同步 LocalTensor<int32_t> tempIndexLocal = sortedValueIndexLocal.ReinterpretCast<int32_t>(); ArithProgression<int32_t>(tempIndexLocal, offset, 1, tileNum); // ✅ 正确:DataCopyPad(MTE2 操作)后 SetFlag,ArithProgression(V 操作)前 WaitFlag // ... DataCopyPad 等 MTE2 操作在此执行 ... event_t eventId = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V)); SetFlag<HardEvent::MTE2_V>(eventId); WaitFlag<HardEvent::MTE2_V>(eventId); LocalTensor<int32_t> tempIndexLocal = sortedValueIndexLocal.ReinterpretCast<int32_t>(); ArithProgression<int32_t>(tempIndexLocal, offset, 1, tileNum);

检查清单

使用排序/归并 API 时,确保:

API 选择

  • 使用Sort<T, true>高阶 API 进行 tile 内排序
  • 使用MrgSort<T, true>MrgSort基础 API(910b)
  • 禁止使用MrgSort4(910b 触发 VEC_ERROR)
  • 禁止使用自定义函数替代GetSortLen/GetSortOffset

proposal 格式

  • 每个 proposal = 8B(value 4B + index 4B)
  • GetSortLen/GetSortOffset返回 float 数(= n × 2)
  • LocalTensor[]是元素偏移,不是字节偏移
  • 使用 AscendC 内置GetSortLen<float>GetSortOffset<float>

MrgSort 归并

  • 输入 4 路数据必须各自有序
  • 正确设置validBitTail(4路=0b1111, 3路=0b0111, 2路=0b0011)
  • sortedNums仅填充有效路元素,需配合有效路计数器j紧凑访问
  • 状态更新使用sortedNums返回的消耗计数
  • 每个归并循环都必须调用UpdateSortInfo()(否则死循环)

队列类型

  • 归并输入队列copyInQueue_类型为VECIN(不是VECOUT
  • 归并输出队列sortedQueue_类型为VECOUT

事件同步

  • 索引初始化前等待MTE2_V事件
  • CopyOut 前等待V_MTE3事件

【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/30 15:56:15

基于Arduino与L293D的仿真汽车模型:从H桥驱动到红外遥控的嵌入式实践

1. 项目概述与核心思路几年前&#xff0c;我为了给一个电子兴趣小组做演示&#xff0c;捣鼓出了一个用Arduino控制的小车。当时它只能前进后退&#xff0c;简陋得很。后来我总想着&#xff0c;能不能做一个更像“真车”的东西&#xff1f;不是那种满地乱跑的玩具&#xff0c;而…

作者头像 李华
网站建设 2026/5/30 15:55:12

电路设计入门:从核心原理到PCB实战的创客必修课

1. 项目概述&#xff1a;为什么电路设计是每个创客的必修课 如果你对电子世界充满好奇&#xff0c;看着手机、电脑或者智能家居设备&#xff0c;总想拆开看看里面那些密密麻麻的线路和芯片到底是怎么工作的&#xff1b;或者你有一个绝妙的创意&#xff0c;想亲手做一个会发光的…

作者头像 李华
网站建设 2026/5/30 15:53:46

图像转3D建模:ImageToSTL项目5步实现立体浮雕自动化生成

图像转3D建模&#xff1a;ImageToSTL项目5步实现立体浮雕自动化生成 【免费下载链接】ImageToSTL This tool allows you to easily convert any image into a 3D print-ready STL model. The surface of the model will display the image when illuminated from the left side…

作者头像 李华
网站建设 2026/5/30 15:52:57

OnionShare终极安全指南:Tor网络如何彻底保护你的隐私

OnionShare终极安全指南&#xff1a;Tor网络如何彻底保护你的隐私 OnionShare是一款基于Tor网络的开源工具&#xff0c;让你能够安全匿名地分享文件、托管网站和与朋友聊天。在当今数字监控日益严重的时代&#xff0c;OnionShare通过Tor网络的洋葱路由技术为你的隐私提供终极保…

作者头像 李华