做招聘信息的网站有哪些方面,在线设计房屋布局软件,中企动力手机邮政登录,青岛博海建设网站训练营简介 2025年昇腾CANN训练营第二季#xff0c;基于CANN开源开放全场景#xff0c;推出0基础入门系列、码力全开特辑、开发者案例等专题课程#xff0c;助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证#xff0c;即可领取精美证书#xff0c;完成…训练营简介 2025年昇腾CANN训练营第二季基于CANN开源开放全场景推出0基础入门系列、码力全开特辑、开发者案例等专题课程助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证即可领取精美证书完成社区任务更有机会赢取华为手机平板、开发板等大奖。报名链接https://www.hiascend.com/developer/activities/cann20252#cann-camp-2502-intro前言在通用计算机科学中排序算法如 QuickSort, MergeSort通常依赖复杂的分支跳转Branching。CPU有强大的分支预测器适合处理逻辑判断。NPU (AI Core)是极其强悍的“向量吞吐机器”最讨厌逻辑判断。这就导致了一个悖论AI 模型越来越依赖 TopK如 LLM 的采样、搜索推荐但 AI 芯片却不擅长排序。为了解决这个问题我们需要换一种思维不要让硬件去“判断”该不该交换而是设计一个固定的“交换网络”让数据流过这个网络出来时自然就是有序的。这就是双调排序Bitonic Sort的核心思想。本期文章我们将利用 Ascend C 的 Vector 单元手写一个并行排序算子。一、 核心图解排序网格的魔法双调排序不依赖数据的值来决定控制流它的比较路径是固定的。二、 算法原理从双调序列到有序序列2.1 什么是双调序列 (Bitonic Sequence)一个序列如果是“先单调递增再单调递减”或者通过循环移位能变成这样它就是双调的。 例如[1, 3, 5, 9, 8, 6, 2]。2.2 排序步骤Bitonic Sort 的神奇之处在于构造把任意序列变成多个小的双调序列。合并把两个小的双调序列合并成一个大的双调序列。排序把一个双调序列变成有序序列。对于 Ascend C 开发者我们只需要关注最核心的操作Compare-and-Swap (CAS)。 对于长度为 $N$ 的向量我们需要进行 $\log N$ 个阶段的比较每个阶段包含若干次跨步比较。三、 实战Ascend C 实现 TopK虽然 Ascend C 提供了内置的Sort指令基于硬件加速但它通常有长度限制如 UB 大小或特定元素个数。对于超长序列如 32K 词表的 TopK我们需要分治 归并。3.1 Kernel 类定义假设我们要实现一个行级 TopK从[Batch, N]中选出前 K 个。class KernelTopK { public: __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, uint32_t K, uint32_t N) { // ... Init ... // Tiling: 每次处理一行 this-K K; this-N N; } __aicore__ inline void Process() { for (int i 0; i batchSize; i) { Compute(i); } } };3.2 核心排序逻辑 (Bitonic Merge)假设 N 很大无法一次性 Sort。我们采用分块排序 归并策略。__aicore__ inline void Compute(int32_t i) { // 1. CopyIn // 假设 N4096我们可以一次性搬进 UB (如果 UB 够大) // 如果放不下需要多轮归并类似归并排序 DataCopy(dataLoc, xGm[offset], N); // 2. Local Sort (利用硬件指令) // Ascend C 的 Sort 指令通常支持对较短序列的全排序 // Sort(dst, src, len, repeat) // 假设硬件支持最大 128 长度的 Sort // 我们先把数据切成 32 个 128 的块分别排序 // 伪代码Block Sort // for (int j0; jN/128; j) Sort(dataLoc[j*128], dataLoc[j*128], 128); // 3. Bitonic Merge (手写归并网络) // 现在我们有 32 个有序小块。需要两两合并。 // Merge 两个有序序列 A (升序) 和 B (降序) 成为一个双调序列再排序 // 这里演示最核心的 Compare-and-Swap 步骤 // 假设我们要合并两个长度为 L 的有序块 // 步长 stride 从 L/2 递减到 1 for (uint32_t stride L/2; stride 0; stride / 2) { // 构造比较对data[i] 和 data[istride] // 技巧利用 Vector 指令的地址偏移 // vec1 data[0 ... N-stride] // vec2 data[stride ... N] // maxVec Max(vec1, vec2) // minVec Min(vec1, vec2) // 此时需要根据排序方向将 min/max 写回正确的位置 // 这通常需要 Muls (mask) 或者 Select 指令 // Ascend C 提供了 MrgSort (MergeSort) 高阶指令来加速这一步 // MrgSort(dst, src, mrg_list) } // 4. Extract TopK // 排序完成后取前 K 个即可 DataCopy(yGm[offset], dataLoc, K); }3.3 索引追踪 (ArgMax 问题)TopK 最大的麻烦在于我们不仅要 Value还要 Index。 但在 Vector 排序时Index 会丢失。黑魔法Value-Index Packing我们可以把 Value 和 Index 打包成一个 struct或者更 trick 的做法利用高位存 Value低位存 Index。假设 Value 是 FP16Index 是 Uint16。 我们可以把它们拼成一个Uint32。高 16 位Value (如果不全是正数需要做 Flip 符号位处理保证整数比较逻辑与浮点一致)。低 16 位Index。这样直接对 Uint32 数组进行排序自然就是按 Value 排序且 Index 紧随其后__aicore__ inline void PackAndSort() { // 1. Pack // value_int32 (reinterpret_castuint16(value_fp16) 16) | index_uint16 // 需要用到 Vector 的 Shift 和 Or 指令 // 2. Sort (Uint32) Sort(packedLoc, packedLoc, N); // 3. Unpack // topk_val packed 16 // topk_idx packed 0xFFFF }四、 性能优化的“胜负手”排序算子是Compute Bound的比较次数极多。利用Sort指令尽可能利用硬件内置的排序指令处理小块而不是手写比较网络。利用MrgSort指令硬件加速的归并指令比手动 Compare-Swap 快得多。只排必要的数据如果 $K1$直接ReduceMax。如果 $K$ 很小如 Top5且 $N$ 很大可以先分块求 TopK最后再汇总求 TopKMap-Reduce 思想避免对全量数据排序。五、 总结在 AI Core 上实现排序是对 Ascend C 编程能力的极致考验。算法选择放弃快排拥抱双调Bitonic。数据技巧利用 Packing 技术解决 ArgMax 索引追踪问题。硬件协同混合使用Sort小块和MrgSort合并指令。掌握了 TopK你就打通了 LLM 解码阶段的最后一道关卡让模型生成的每一个 Token 都既快又准。