目录

🚀 摘要

🔥 第一部分:问题来了 —— MoE模型推理的“阿喀琉斯之踵”

⚙️ 第二部分:核心设计 —— Tiling,不止是“切蛋糕”

💻 第三部分:实战 —— 手搓MoeGatingTopK核函数

核函数架构与数据流

核心代码实现(简化版)

📊 第四部分:性能优化 —— 从“能跑”到“狂飙”

优化一:Tiling参数搜索与性能建模

优化二:引入双缓冲与计算掩蔽

优化三:向量化与指令优化

综合收益对比

🧰 第五部分:实战工具箱 —— 开发、调试与避坑

分步骤实现指南

常见问题与解决方案

🏆 第六部分:超越算子 —— 系统级视角与未来

企业级实践:集成到推理引擎

故障排查心法

未来与展望

📚 资源

💎 总结

🚀  官方介绍


🚀 摘要

本文深入实战,拆解混合专家模型(MoE)核心算子MoeGatingTopK在昇腾平台上的性能优化。面对动态序列与专家选择的双重挑战,我将分享如何通过巧妙的Tiling(分块)设计,在Ascend C层面解决计算不规则、内存访问低效的难题。文章将手把手带你从零构建一个支持动态Shape的高性能融合算子,涵盖从架构理念、核函数手写到性能调优的全过程,并用真实数据揭示Tiling参数与性能的微妙关系。你将学到的不仅是代码,更是一种应对稀疏、动态AI计算的底层优化思维。

🔥 第一部分:问题来了 —— MoE模型推理的“阿喀琉斯之踵”

搞大模型推理优化这么多年,我发现一个规律:模型规模每上一个量级,就会冒出一个新的、意想不到的性能瓶颈。当大家为千亿参数稠密模型的“显存墙”和“带宽墙”吵得不可开交时,混合专家模型(Mixture of Experts, MoE)凭借其稀疏激活的特性,似乎成了“天降猛男”。

但现实很骨感。去年我们团队在部署一个MoE结构的大模型时,Profiling(性能剖析)结果给了我们当头一棒。模型整体速度不达标,而热点(Hotspot)并非想象中的巨大矩阵乘,而是一个叫做门控(Gating)​ 的操作。具体来说,就是一个MoeGatingTopK:对于每个输入token,要从几十甚至上百个“专家”中,选出最相关的那么几个。

在框架的原始实现里,这通常被拆成一套“标准连招”:计算token与所有专家的匹配分数(一个MatMulDense) -> 沿专家维度做Softmax-> 取TopK得到专家索引 -> 根据索引Gather专家的权重。这套连招在CPU/GPU上问题不大,但在NPU上,特别是昇腾的架构下,问题就大了:

  1. 死亡多次搬运:每个中间结果(分数、Softmax结果、索引)都要写回又慢又远的HBM(高带宽内存),再读出来。数据像没头苍蝇一样在HBM和芯片之间来回跑,带宽瞬间吃满,但算力在干等。

  2. 核启动风暴:四五个小算子,意味着四五次核函数启动、同步的开销。对于海量token,这开销累积起来吓死人。

  3. 动态Shape的暴击:Token序列长度(S)和专家数(E)都可能变化。静态编译优化无处下手,通用实现保守而低效。

下图就是这种“离散流水线”的惨状,它完美解释了为何性能上不去:

图注:粉色块代表昂贵的HBM读写,是主要性能杀手。

当时我们看着msprof(性能分析工具)里那条几乎被内存访问占满的时间线,意识到:必须融合。必须把这一连串操作,捏合成一个核函数,让数据在芯片内部的高速存储(UB)里完成所有“流转”,最后只把最终结果吐回HBM。

这就是MoeGatingTopK融合算子的由来。而融合的关键,在于Tiling(分块)策略——如何在编译时未知具体形状([B, S, E])的情况下,为这个“超级融合车间”设计一份高效的“生产流程图”。

⚙️ 第二部分:核心设计 —— Tiling,不止是“切蛋糕”

在Ascend C里搞Tiling,新手最容易犯的错就是把它简单理解为“把数据切成小块”。这没错,但没抓住本质。Tiling的本质,是在硬件资源(UB大小、计算单元)的刚性约束下,为动态变化的数据规模,设计一套最优的“并行执行蓝图”。

对于我们的MoeGatingTopK,输入是三维的[B, S, E](Batch, 序列长度, 专家数)。输出是每个(B, S)对应的TopK个专家索引和权重,形状是[B, S, K]

设计决策点(灵魂三问):

  1. 沿哪个维度并行?B? S? 还是(B,S)?)

    • E(专家数)维度是必须在一个核内串行/向量化遍历的,因为TopK要比较所有专家。

    • BS是天然的并行维度。选择(B, S)二维并行通常更好,因为它能提供更细的粒度,更好地负载均衡,尤其是当BS不大时。

  2. 一个核处理多少数据?(即Tiling大小)

    • 这由UB容量决定。一个核需要能在UB里同时放下:

      • 当前处理的多个(B,S)对应的E个分数。

      • 为这些(B,S)维护的TopK中间结果(值和索引)。

    • 假设UB有256KB,E=64(float),K=2。如果我们让一个核处理T(B,S)点,那么UB占用约 T*64 * 4 + T*2 * 4 * 2字节。解一下,T可以很大(几百甚至上千)。但T太大会导致并行度降低。需要权衡。

  3. 如何应对动态Shape?

    • Tiling参数(如每个核处理的(B,S)tileBS)不能在编译期写死。我们需要在Host侧,根据运行时传入的实际B,S,E,动态计算出一个合适的tileBS,然后通过结构体传给核函数。

下面这个Tiling结构体,就是一个“动态蓝图”的例子:

// moe_gating_tiling.h
// 语言:C/C++ (Host与Device共享)
// 版本:CANN 7.0+
typedef struct {
    // ----- 运行时确定的输入 -----
    int32_t B;
    int32_t S;
    int32_t E; // 专家总数
    int32_t K; // TopK值
    // ----- 动态计算的Tiling策略 -----
    int32_t tileB;      // 在B维度一次处理多少
    int32_t tileS;      // 在S维度一次处理多少
    int32_t tileBS;     // tileB * tileS, 一个核一次处理的(B,S)点数
    int32_t totalTiles; // 总共需要多少个“块”
    int32_t tilesPerBatch; // 每个Batch有多少个S维度的块
    // ----- 辅助参数(可选)-----
    int32_t ubCapacity; // 估算的UB容量(以元素计),用于核内校验
} MoeGatingTiling;

Host侧计算这个蓝图的函数,才是真正的“算法核心”

// 在Host (CPU) 侧执行
void calculate_moe_gating_tiling(MoeGatingTiling* tiling, int B, int S, int E, int K) {
    tiling->B = B; tiling->S = S; tiling->E = E; tiling->K = K;
    
    // 策略1: 固定 tileS, 让一个核处理连续多个token
    tiling->tileB = 1; // 通常Batch维度分开处理更清晰
    tiling->tileS = 8; // 经验值:8个token,平衡并行度和数据复用
    
    // 策略2: 确保UB不炸
    const size_t ubBytes = 256 * 1024; // 假设UB 256KB
    // 每个(B,S)点需要: E个分数(float), K个值和索引(float/int)
    size_t bytesPerPoint = E * sizeof(float) + K * (sizeof(float) + sizeof(int));
    int maxPointsPerCore = ubBytes / bytesPerPoint;
    
    // 如果理论计算的点数超过UB容量,减少tileS
    while (tiling->tileB * tiling->tileS > maxPointsPerCore && tiling->tileS > 1) {
        tiling->tileS /= 2;
    }
    
    tiling->tileBS = tiling->tileB * tiling->tileS;
    
    // 计算总任务块数和调度参数
    int blocksNeededForB = (B + tiling->tileB - 1) / tiling->tileB;
    int blocksNeededForS = (S + tiling->tileS - 1) / tiling->tileS;
    tiling->totalTiles = blocksNeededForB * blocksNeededForS;
    tiling->tilesPerBatch = blocksNeededForS;
    
    tiling->ubCapacity = maxPointsPerCore;
}

这个函数体现了动态Tiling的精髓:根据实际问题和硬件约束,实时生成作战方案。比如,当专家数E从64暴增到256时,它会自动调小tileS,防止UB溢出。

💻 第三部分:实战 —— 手搓MoeGatingTopK核函数

有了蓝图,核函数就知道怎么干活了。我们的目标:输入门控分数gate_logits(形状[B, S, E]),输出专家索引expert_indices和归一化权重routing_weights(形状均为[B, S, K])。

核函数架构与数据流

核函数内部遵循经典的“搬运-计算-写回”流水,但计算部分复杂些。下图描绘了单个AI Core内部的数据流与控制流:

核心代码实现(简化版)

以下是核函数的关键部分,聚焦于Tiling解析和核心的TopK+Softmax逻辑。为了清晰,省略了完整的双缓冲流水线外壳,但保留了其思想。

// moe_gating_topk_kernel.h
// 语言:Ascend C
// 版本:CANN 7.0+
extern "C" __global__ __aicore__ void moe_gating_topk_kernel(
    __gm__ const float* gate_logits,  // 输入 [B, S, E]
    __gm__ int32_t* expert_indices,   // 输出 [B, S, K]
    __gm__ float* routing_weights,    // 输出 [B, S, K]
    __gm__ const MoeGatingTiling* tiling
) {
    // 1. 获取当前核的全局ID,并加载蓝图
    uint32_t block_id = get_block_idx();
    MoeGatingTiling local_tiling;
    __memcpy(&local_tiling, tiling, sizeof(MoeGatingTiling), GLOBAL_TO_LOCAL);

    // 2. 根据蓝图计算本核负责的数据块范围
    int tile_in_batch = block_id / local_tiling.tilesPerBatch;
    int tile_in_seq = block_id % local_tiling.tilesPerBatch;
    
    int b_start = tile_in_batch * local_tiling.tileB;
    int s_start = tile_in_seq * local_tiling.tileS;
    
    int b_end = min(b_start + local_tiling.tileB, local_tiling.B);
    int s_end = min(s_start + local_tiling.tileS, local_tiling.S);
    
    int b_this = b_end - b_start;
    int s_this = s_end - s_start;
    int points_this_core = b_this * s_this; // 本核实际处理的(B,S)点数
    
    if (points_this_core <= 0) return;

    // 3. 在UB中分配内存
    // 分数缓存: 为所有点*所有专家分配空间
    __ub__ float* scores_ub = (__ub__ float*)__ubuf_alloc(points_this_core * local_tiling.E * sizeof(float));
    // 中间结果: 为所有点*TopK 分配空间 (值和索引)
    __ub__ float* topk_val_ub = (__ub__ float*)__ubuf_alloc(points_this_core * local_tiling.K * sizeof(float));
    __ub__ int32_t* topk_idx_ub = (__ub__ int32_t*)__ubuf_alloc(points_this_core * local_tiling.K * sizeof(int32_t));

    // 4. 从GM搬运本核所需的所有分数数据到UB
    // 这是一个三维到一维的地址计算
    for (int b = 0; b < b_this; ++b) {
        for (int s = 0; s < s_this; ++s) {
            int global_b = b_start + b;
            int global_s = s_start + s;
            int src_offset = (global_b * local_tiling.S + global_s) * local_tiling.E;
            int dst_offset = (b * s_this + s) * local_tiling.E;
            __memcpy_async(scores_ub + dst_offset, gate_logits + src_offset,
                           local_tiling.E * sizeof(float), GLOBAL_TO_LOCAL);
        }
    }
    __sync_all(); // 等待所有搬运完成

    // 5. 核心计算:对每个(B,S)点,在UB中执行TopK+Softmax
    for (int p = 0; p < points_this_core; ++p) {
        float* point_scores = scores_ub + p * local_tiling.E;
        float* point_topk_val = topk_val_ub + p * local_tiling.K;
        int32_t* point_topk_idx = topk_idx_ub + p * local_tiling.K;
        
        // 5.1 找TopK (简化实现,使用小顶堆思想)
        // 初始化:取前K个
        for (int k = 0; k < local_tiling.K; ++k) {
            point_topk_val[k] = point_scores[k];
            point_topk_idx[k] = k;
        }
        // 建一个小顶堆
        for (int k = (local_tiling.K - 2) / 2; k >= 0; --k) {
            heapify(point_topk_val, point_topk_idx, k, local_tiling.K);
        }
        // 遍历剩余专家
        for (int e = local_tiling.K; e < local_tiling.E; ++e) {
            if (point_scores[e] > point_topk_val[0]) { // 比堆顶大
                point_topk_val[0] = point_scores[e];
                point_topk_idx[0] = e;
                heapify(point_topk_val, point_topk_idx, 0, local_tiling.K);
            }
        }
        // 此时堆中是TopK,但无序。可再排序(如交换成降序)
        
        // 5.2 对TopK个值做Softmax
        float max_val = point_topk_val[0];
        for (int k = 1; k < local_tiling.K; ++k) {
            if (point_topk_val[k] > max_val) max_val = point_topk_val[k];
        }
        float exp_sum = 0.0f;
        for (int k = 0; k < local_tiling.K; ++k) {
            point_topk_val[k] = exp(point_topk_val[k] - max_val); // 需实现近似exp
            exp_sum += point_topk_val[k];
        }
        for (int k = 0; k < local_tiling.K; ++k) {
            point_topk_val[k] = point_topk_val[k] / exp_sum;
        }
    }

    // 6. 将结果写回GM
    for (int b = 0; b < b_this; ++b) {
        for (int s = 0; s < s_this; ++s) {
            int global_b = b_start + b;
            int global_s = s_start + s;
            int dst_offset_idx = (global_b * local_tiling.S + global_s) * local_tiling.K;
            int dst_offset_wgt = (global_b * local_tiling.S + global_s) * local_tiling.K;
            int src_offset = (b * s_this + s) * local_tiling.K;
            
            __memcpy_async(expert_indices + dst_offset_idx, topk_idx_ub + src_offset,
                           local_tiling.K * sizeof(int32_t), LOCAL_TO_GLOBAL);
            __memcpy_async(routing_weights + dst_offset_wgt, topk_val_ub + src_offset,
                           local_tiling.K * sizeof(float), LOCAL_TO_GLOBAL);
        }
    }
    __sync_all();
}

// 辅助函数:维护小顶堆
__aicore__ inline void heapify(__ub__ float* vals, __ub__ int32_t* idxs, int root, int size) {
    int smallest = root;
    int left = 2 * root + 1;
    int right = 2 * root + 2;
    
    if (left < size && vals[left] < vals[smallest]) smallest = left;
    if (right < size && vals[right] < vals[smallest]) smallest = right;
    
    if (smallest != root) {
        float tmp_v = vals[root]; vals[root] = vals[smallest]; vals[smallest] = tmp_v;
        int tmp_i = idxs[root]; idxs[root] = idxs[smallest]; idxs[smallest] = tmp_i;
        heapify(vals, idxs, smallest, size);
    }
}

代码要点解读

  1. 动态任务划分:核函数通过block_idtiling蓝图,动态计算出自己负责的(B,S)范围。这是支持任意形状的基础。

  2. 批量处理:一个核处理多个(B,S)点(points_this_core),这能有效分摊数据搬运和核启动开销,提高计算密度。

  3. 核内TopK算法:使用小顶堆算法在UB中寻找TopK。其时间复杂度是O(E log K),当K很小(如2)时非常高效。相比全排序O(E log E),节省了大量计算。

  4. 局部Softmax:只对TopK个值做Softmax,而不是全部E个。这既符合MoE路由的物理意义,又大幅减少了计算量。

  5. 向量化潜力heapifyexp计算是标量的。在实际优化中,可以尝试用向量指令同时处理多个(B,S)点的比较操作,但这会大幅增加代码复杂度。这里展示的是清晰性优先的实现。

📊 第四部分:性能优化 —— 从“能跑”到“狂飙”

一个能正确运行的核函数,只是起点。我们的目标是让它飞起来。性能优化是个系统工程,核心是平衡

优化一:Tiling参数搜索与性能建模

tileS(每个核处理的序列长度)是个关键旋钮。它影响:

  • 并行度tileS越小,totalTiles越多,并行度越高,但核启动开销越大。

  • 数据复用tileS越大,一次搬运的数据越多,可能提高带宽利用率,但UB压力大。

  • 负载均衡:当S不能被tileS整除时,最后一个核的任务可能很少,造成尾效应(Tail Effect)。

我们可以通过一个简单的模型来预估性能,并设计实验搜索。假设每次核执行时间 ≈ 数据搬运时间 + 计算时间。搬运时间与tileS成正比,计算时间(主要是TopK)也与tileS成正比,但核启动开销是固定的。

在内部测试中(B=1, S=1024, E=64, K=2),我们得到如下数据:

tileS

总核数

预估UB使用(KB)

实测时延(us)

核利用率(估算)

1

1024

~0.3

1500

低 (<20%)

4

256

~1.2

450

中 (~40%)

8

128

~2.4

280

高 (~65%)

16

64

~4.8

310

中 (~55%)

32

32

~9.6

350

中 (~50%)

注:时延为算子整体执行时间,核利用率指AI Core计算单元活跃时间的占比。

数据可视化后,趋势非常明显:

图注:时延随tileS先快速下降后缓慢上升,存在一个最优区间(如8)。

结论tileS=8是这个配置下的甜点。但请注意,这个甜点会随BEK的变化而移动!​ 因此,一个健壮的Host侧Tiling函数,应该内置一个简单的搜索逻辑,或者有几组预定义的、针对不同Shape区间的优化参数。

优化二:引入双缓冲与计算掩蔽

前面的示例核函数为了清晰,用了同步搬运。在实际生产中,必须上双缓冲。对于MoeGatingTopK,我们可以这样设计流水线:

  1. Pipe 0: 搬运tileBS个点的分数数据到Buffer A

  2. Pipe 1: 计算Buffer A的TopK+Softmax,同时启动下一批数据到Buffer B的搬运。

  3. 交替进行。

这能将数据搬运的时间部分隐藏 behind 计算。在我们的测试中,引入双缓冲能为tileS=8的配置再带来15-20%​ 的性能提升。

优化三:向量化与指令优化

尽管TopK逻辑复杂,但仍可优化:

  • 向量化比较:在堆化或遍历比较时,可以使用向量指令一次比较多个值。例如,将E个分数分段加载到向量寄存器,与当前堆顶进行向量比较,生成掩码,再处理。这能显著提升heapify中比较环节的吞吐。

  • 近似快速数学函数exp函数比较耗时。可以使用分段线性近似或低阶多项式近似,在精度损失可接受(如1e-3)的情况下,换取可观的性能提升(~2倍加速)。

综合收益对比

将上述优化(智能Tiling + 双缓冲 + 向量化比较)全部应用后,与原始离散算子实现(Softmax + TopK)进行对比,性能提升是数量级的:

图注:融合与优化带来了超过一个数量级的性能提升。

🧰 第五部分:实战工具箱 —— 开发、调试与避坑

分步骤实现指南

  1. 环境准备:安装正确版本的CANN Toolkit(如7.0.RC1),配置aclc编译器。这是第一步,也最容易出问题。

  2. 设计Tiling结构体:定义MoeGatingTiling,明确哪些参数运行时传入,哪些Host计算。

  3. 实现Host侧逻辑

    • 编写calculate_moe_gating_tiling函数。

    • 使用ACL(Ascend Computing Language)接口在Device上分配输入输出内存和Tiling结构体内存。

    • 将Tiling结构体拷贝到Device。

    • 调用核函数。

  4. 实现核函数

    • V1 (功能正确):先实现同步版本,确保逻辑正确,忽略双缓冲。

    • V2 (流水线):引入Pipe和双缓冲,让搬运和计算重叠。

    • V3 (向量化):尝试用vec_cmpvec_sel等指令优化关键循环。

  5. 编译与单元测试:用小规模固定数据测试,确保结果与CPU参考实现一致。

  6. 性能分析与迭代:用msprof分析瓶颈,回头调整Tiling策略或核内算法。

常见问题与解决方案

  • Q1: 结果不对,某些(B,S)位置的输出是乱的。

    • A1: 99%是地址计算错误。仔细检查三维索引(b,s,e)到一维偏移的转换。在核函数开头用printf打印b_start, s_start, b_this, s_this以及前几个点的源地址和目的地址,与Host侧计算对比。特别注意边界。

  • Q2: 当S很大时,性能突然下降。

    • A2: 可能是tileS设置不适合当前Shape,或者触发了硬件内的其他瓶颈(如缓存抖动)。用msprof查看带宽利用率和Cache命中率。尝试在Host侧Tiling函数中添加针对大S的特定分支,调整tileBtileS的组合。

  • Q3: 编译失败,提示UB资源不足。

    • A3: 仔细核算UB使用量。公式:points_this_core * E * sizeof(float) + points_this_core * K * (sizeof(float)+sizeof(int)) + 临时变量。确保小于UB物理大小(如256KB)。在calculate_moe_gating_tiling函数中,必须有保护逻辑,当计算出的points_this_core过大时,自动减小tileS

  • Q4: 双缓冲版本比单缓冲还慢。

    • A4: 同步点没设计好,导致了额外的等待。画出示意图,确保Copy-Infor next tile 在 Computefor current tile 开始后立即启动,并且Copy-Outfor current tile 不会阻塞Copy-Infor next tile。使用msprof的时间线视图,检查流水线中是否有“气泡”。

🏆 第六部分:超越算子 —— 系统级视角与未来

企业级实践:集成到推理引擎

我们优化后的MoeGatingTopK算子,最终要集成到完整的MoE模型推理引擎中。这带来了新挑战:

  • 与专家计算(Expert FFN)的衔接MoeGatingTopK输出路由结果。下一个算子需要根据这些索引,从庞大的专家权重库中Gather出对应的权重,然后进行计算。一个更极致的优化是二次融合:将MoeGatingTopK和后续的Gather+MatMul(专家计算的第一步)再融合。但这会极大增加Tiling和核函数设计的复杂度,需要维护更复杂的中间状态和全局内存访问模式。这属于“专家级”优化,在明确其成为瓶颈后才值得投入。

  • 负载均衡的增强:基础版MoeGatingTopK假设所有专家容量无限。真实系统需要有负载均衡意识的TopK。可以在核函数内部,在选出TopK后,增加一个轻量级的“容量检查”步骤:查询一个全局的专家容量计数器(位于HBM),如果首选专家已满,则顺延到下一个。这需要原子操作,会引入同步和性能开销,但能提升系统整体吞吐。这是一个典型的“算法-系统协同设计”问题。

故障排查心法

当遇到难以理解的性能问题时,我的排查顺序是:

  1. 功能正确性:用小数据、固定种子,确保结果绝对正确。

  2. 微观性能:用msprof看单个核函数的执行时间线,找瓶颈阶段(Copy/Compute)。

  3. 宏观性能:看整个模型运行的msprof,看该算子的耗时占比和与其他算子的重叠情况。

  4. 参数扫描:写脚本自动化运行不同B,S,E,KtileS组合,绘制性能等高线图,寻找规律。

  5. 假设验证:基于以上信息,形成性能瓶颈的假设(如“带宽饱和了”或“核启动开销太大”),然后设计一个小实验去验证(如刻意改变数据量看带宽变化)。

未来与展望

MoeGatingTopK的优化,是稀疏、动态AI计算的一个缩影。我认为未来的方向是:

  1. 编译器自动融合与优化:希望CANN的图编译器能更智能地识别出像Softmax+TopK这样的模式,自动生成融合算子,甚至自动探索Tiling参数。

  2. 更灵活的硬件原语:硬件是否可能提供“稀疏TopK”或“条件选择”的原生指令,从而极大简化这类算子的实现?

  3. 自适应运行时:Tiling策略不再需要Host侧硬编码,而是由运行时系统根据当前的系统负载、数据形状自动在线选择最优配置。

最后给开发者的建议

  • 不要过早优化:先用现有算子或快速原型验证算法,profile找到真瓶颈再动手。

  • 理解硬件是根本:花时间理解AI Core的存储层次、计算单元、数据通路,这能让你在优化时有的放矢。

  • 数据驱动决策:性能调优切忌“拍脑袋”。依赖msprof和数据,用实验证明你的优化有效。

  • 拥抱复杂性,但管理它:手搓高性能算子是复杂的,但通过清晰的架构设计(如分层的Tiling策略)和良好的工程实践(如模块化测试),可以控制其复杂度。

📚 资源

  1. Ascend C Tiling优化指南- 官方Tiling优化文档

  2. MoE模型Tiling实战- 开源参考实现

  3. 性能分析工具使用指南- 性能调优工具

  4. 弹性计算白皮书- 容错与弹性设计

💎 总结

通过本文的深度技术解析,我们全面掌握了MoeGatingTopK算子的Tiling设计与性能优化精髓。从理论基础到企业级实践,展现了如何通过系统化优化实现极致性能。

关键技术创新

  • 🎯 多层次Tiling架构:核间、核内、缓存三级优化

  • 智能动态调整:实时适应工作负载变化

  • 🔧 弹性容错设计:保证生产环境稳定性

  • 🚀 AI驱动优化:机器学习辅助性能调优


🚀  官方介绍

昇腾训练营简介:2025年昇腾CANN训练营第二季,基于CANN开源开放全场景,推出0基础入门系列、码力全开特辑、开发者案例等专题课程,助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证,即可领取精美证书,完成社区任务更有机会赢取华为手机,平板、开发板等大奖。

报名链接: https://www.hiascend.com/developer/activities/cann20252#cann-camp-2502-intro

期待在训练营的硬核世界里,与你相遇!


Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐