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 流水线(训练→量化→部署→监控)
相关推荐
开开心心就好2 小时前
发票合并打印工具,多页布局设置实时预览
linux·运维·服务器·windows·pdf·harmonyos·1024程序员节
獨枭2 小时前
PyCharm 跑通 SAM 全流程实战
windows
仙剑魔尊重楼3 小时前
音乐制作电子软件FL Studio2025.2.4.5242中文版新功能介绍
windows·音频·录屏·音乐·fl studio
PHP小志3 小时前
Windows 服务器怎么修改密码和用户名?账户被系统锁定如何解锁
windows
专注VB编程开发20年5 小时前
vb.net datatable新增数据时改用数组缓存
java·linux·windows
仙剑魔尊重楼5 小时前
专业音乐制作软件fl Studio 2025.2.4.5242中文版新功能
windows·音乐·fl studio
rjc_lihui6 小时前
Windows 运程共享linux系统的方法
windows
失忆爆表症6 小时前
01_项目搭建指南:从零开始的 Windows 开发环境配置
windows·postgresql·fastapi·milvus
穿过锁扣的风6 小时前
一文搞懂 SQL 五大分类:DQL/DML/DDL/DCL/TCL
数据库·microsoft·oracle