CANN 性能剖析实战:从原始事件到交互式火焰图

CANN 性能剖析实战:从原始事件到交互式火焰图

cann组织链接:https://atomgit.com/cann

ops-nn仓库链接:https://atomgit.com/cann/ops-nn

🎯 目标

  • 利用 aclprof 采集 NPU kernel 执行事件
  • 转换为 Chrome Trace Event 格式
  • 渲染为 可缩放、可搜索的火焰图
  • 嵌入 WebUI,支持按请求 ID 过滤
  • 定位瓶颈:如 Int4Gemm 占比过高、PagedAttention 内存带宽受限

✅ 全流程自动化,无需手动导出 .json


一、整体数据流

启动 Profiling
CANN-LLM Engine
aclprof API
原始事件: kernel_name, start, duration
TraceEventConverter
trace.json
WebUI FlameGraph 组件
用户交互:缩放/搜索/聚焦
关联到具体请求 ID


二、后端实现:Profiling + 转换

1. 启用 CANN Profiling

cpp 复制代码
// profiler_manager.cpp
#include "acl/acl_prof.h"

void ProfilerManager::start_profiling() {
    // 配置 profiling 类型:ACL_PROF_TASK_TIME(kernel 级)
    aclprofConfig *config = aclprofCreateConfig(
        nullptr, 0,                    // device list (nullptr = all)
        ACL_PROF_TASK_TIME,            // type
        nullptr, 0                     // data type (default)
    );

    aclprofStart(config);
}

std::vector<KernelEvent> ProfilerManager::stop_and_collect() {
    aclprofStop();
    
    // 获取原始数据
    size_t data_size = 0;
    void* data = aclprofGetProfileData(ACL_PROF_TASK_TIME, &data_size);
    
    // 解析为结构化事件
    return parse_aclprof_data(data, data_size);
}

🔑 aclprofGetProfileData 返回二进制 blob,需按 aclprofTaskTimeInfo 结构解析


2. 转换为 Trace Event 格式

Chrome Trace 格式示例:

json 复制代码
[
  {
    "name": "Int4Gemm",
    "cat": "NPU",
    "ph": "X",
    "ts": 123456789000,
    "dur": 1500,
    "pid": 0,
    "tid": 1,
    "args": {"request_id": "req-abc123"}
  },
  ...
]

转换逻辑:

cpp 复制代码
// trace_converter.cpp
nlohmann::json convert_to_trace_format(
    const std::vector<KernelEvent>& events,
    const std::unordered_map<uint64_t, std::string>& stream_to_req_id) {

    nlohmann::json trace;
    for (const auto& e : events) {
        // 通过 stream ID 关联 request_id(需在 launch kernel 时记录映射)
        std::string req_id = stream_to_req_id.at(e.stream_id);

        trace.push_back({
            {"name", e.kernel_name},
            {"cat", "NPU"},
            {"ph", "X"}, // Complete event
            {"ts", e.start_ns / 1000}, // microseconds
            {"dur", e.duration_ns / 1000},
            {"pid", 0},
            {"tid", static_cast<int>(e.stream_id)},
            {"args", {{"request_id", req_id}}}
        });
    }
    return trace;
}

💡 关键技巧 :在调用 ge::Session::Run() 前,将当前 request_idaclrtStream 绑定


3. 按请求 ID 过滤(WebUI 需求)

提供 REST API:

cpp 复制代码
// GET /api/profile?request_id=req-abc123
std::string get_trace_for_request(const std::string& req_id) {
    auto all_events = profiler_.collect_recent_events();
    auto filtered = filter_by_request_id(all_events, req_id);
    return convert_to_trace_format(filtered).dump();
}

三、前端集成:嵌入火焰图

使用开源库 perfetto 或轻量级 d3-flame-graph

在 WebUI 中添加 Tab

vue 复制代码
<template>
  <div v-if="activeTab === 'flamegraph'">
    <input v-model="filterRequestId" placeholder="Filter by Request ID" />
    <button @click="loadFlameGraph">Load</button>
    <div id="flamegraph-container"></div>
  </div>
</template>

<script>
import 'd3-flame-graph/dist/d3-flamegraph.css';
import flamegraph from 'd3-flame-graph';

export default {
  methods: {
    async loadFlameGraph() {
      // 1. 获取 trace.json
      const resp = await fetch(`/api/profile?request_id=${this.filterRequestId}`);
      const trace = await resp.json();

      // 2. 转换为 d3-flame-graph 所需的树形结构
      const tree = this.traceToTree(trace);

      // 3. 渲染
      const chart = flamegraph()
        .width(800)
        .cellHeight(18)
        .transitionDuration(750)
        .minFrameSize(5)
        .onClick(d => console.log('Clicked:', d));

      d3.select("#flamegraph-container")
        .datum(tree)
        .call(chart);
    },

    traceToTree(traceEvents) {
      // 将 flat events 聚合成 call stack tree
      // (简化:按时间排序后模拟栈)
      const stacks = group_events_into_stacks(traceEvents);
      return build_tree_from_stacks(stacks);
    }
  }
}
</script>

✅ 用户输入 req-abc123,即可看到该请求的完整 kernel 调用栈耗时分布


四、典型性能问题诊断示例

场景 1:INT4 GEMM 成为瓶颈

  • 现象 :火焰图中 Int4Gemm 占据 70%+ 高度
  • 根因:group_size 过小 → scale/zeros 访问频繁
  • 优化:增大 group_size 至 256,重力量化模型

场景 2:PagedAttention 内存带宽受限

  • 现象SparseFusedAttention 中大量小 block 加载
  • 根因:block_size=8 太小,导致随机访存
  • 优化:调整 block_size=32,提升缓存命中率

场景 3:调度开销过高

  • 现象select_batch / prepare_inputs CPU 时间长
  • 根因:队列锁竞争激烈
  • 优化 :改用无锁队列(如 moodycamel::ConcurrentQueue

五、自动化性能回归测试

在 CI 中集成:

yaml 复制代码
# .gitlab-ci.yml
performance_test:
  script:
    - ./llm_server --profile --test-prompt="..." &
    - sleep 10
    - curl http://localhost:8080/api/profile > trace.json
    - python tools/analyze_flamegraph.py trace.json --threshold=50ms
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

analyze_flamegraph.py 可检测:

  • 是否出现预期 kernel(如 Int4Gemm
  • 最大 kernel 耗时是否超阈值
  • 吞吐是否下降 >5%

六、结语:让性能瓶颈无所遁形

通过将 CANN Profiling → Trace Event → 交互式火焰图 全链路打通,我们实现了:

"所见即所得"的性能分析体验------无需专家知识,也能快速定位瓶颈。

这不仅提升了开发效率,更强化了 CANN-LLM 作为生产级推理引擎的可靠性与可维护性。


🔜 下一步建议:

  • 支持 多卡 profiling 聚合视图
  • 实现 自动性能优化建议生成(AI Copilot for Profiling)
  • 构建 完整的 MLOps 流水线(训练→量化→部署→监控)
相关推荐
明湖起风了40 分钟前
mqtt消费堆积
java·jvm·windows
私人珍藏库1 小时前
[Windows] 【灵犀Claw生成】酷狗加密音乐批量转换工具 Xy Music Converter
windows·工具·软件·多功能
handsomestWei3 小时前
claude-code在win环境安装使用
windows·ai编程·claude·安装配置·cc-switch
秦时明月之君临天下4 小时前
Windows如何删除任务管理器中的某个服务?
windows
麦田里的守望者_zhg4 小时前
Windows 执行 wsl --update 报错 1603:注册表权限导致 WSL 安装损坏问题排查与修复
windows
❆VE❆6 小时前
Claude Code 安装与配置完整指南(Windows)
windows·claude code
航Hang*6 小时前
VMware vSphere 云平台运维与管理基础——第5章:VMware vSphere 5.5 高级特性
运维·服务器·开发语言·windows·学习·虚拟化
Mapleay6 小时前
Ubuntu 源的重要性!之 libgmp-dev 无法安装
linux·服务器·windows
humors2217 小时前
微软工具包下载网址
windows·microsoft·微软·office·工具包·sysintervals
寺中人7 小时前
硬盘提示初始化的损坏,手动恢复MBR及EBR分区教程
windows·工具·硬盘修复