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 流水线(训练→量化→部署→监控)
相关推荐
埃博拉酱4 天前
VS Code Remote SSH 连接 Windows 服务器卡在"下载 VS Code 服务器":prcdn DNS 解析失败的诊断与 BITS 断点续传
windows·ssh·visual studio code
唐宋元明清21884 天前
.NET 本地Db数据库-技术方案选型
windows·c#
加号34 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
tryCbest4 天前
Windows环境下配置pip镜像源
windows·pip
呉師傅4 天前
火狐浏览器报错配置文件缺失如何解决#操作技巧#
运维·网络·windows·电脑
百事牛科技4 天前
保护文档安全:PDF限制功能详解与实操
windows·pdf
一个人旅程~4 天前
如何用命令行把win10/win11设置为长期暂停更新?
linux·windows·经验分享·电脑
一个假的前端男4 天前
[特殊字符] Flutter 安装完整指南 Windows—— 2026最新版
windows·flutter
科技前瞻观察4 天前
腾讯控股下的销售易,如何重塑中国CRM格局?
microsoft
倚肆5 天前
在 Windows Docker 中安装并配置 Nginx (映射 Windows 端口与路径)
windows·nginx·docker