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_id与aclrtStream绑定
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_inputsCPU 时间长 - 根因:队列锁竞争激烈
- 优化 :改用无锁队列(如
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 流水线(训练→量化→部署→监控)