
CANN SHMEM 算子性能采集指南msprof、Cycle Profiling 与带宽指标计算实践【免费下载链接】shmemCANN SHMEM 是面向昇腾平台的多机多卡内存通信库基于OpenSHMEM 标准协议实现跨设备的高效内存访问与数据同步。项目地址: https://gitcode.com/cann/shmem本篇指南面向在 CANN SHMEM 仓库中为自研算子做性能评估的开发者系统讲解可用的三类性能/检查工具——msprofMindStudio Profiler推荐用于 kernel 级宏观计时、SHMEM 内置 Device 侧 Cycle Profiling用于 kernel 内逐段细粒度打点以及 mssanitizer内存越界检查并给出统一的性能指标计算规则logical_payload_bytes、algo_bandwidth、bus_factor、bandwidth_utilization与采集规范warmup、多卡平均、结果记录格式。读完本文你将能够独立完成一个 SHMEM 算子从打点插桩—采集导出—指标计算—结果记录的完整性能评估流程并能基于 frame 级数据定位 kernel 内部的时间瓶颈。1. 采集工具总览SHMEM 算子的性能采集按粒度由粗到细分为三个层次工具层次用途推荐度msprofkernel 级Host 调度视角采集task_time、通信时间与硬件计数器获得 kernel 维度执行时间推荐作为算子级端到端计时与 baseline 对比的主口径SHMEM Cycle Profilingkernel 内片段级Device 视角通过SHMEMI_PROF_START/END宏对逐 block、逐 frame 统计 cycle 开销定位通信/计算/同步各段占比定位内部瓶颈时的首选mssanitizer内存检查检测越界访问排除因越界导致的性能异常或结果错误验证性工具不直接测性能三者的分工与协作关系是先用 msprof 拿到算子的宏观时间e2e_us、kernel_us与 HCCL baseline 对比再借助 Cycle Profiling 回答时间到底花在 copy、remote put/get、signal/wait 还是 local compute 上最后用 mssanitizer 排除越界这类虚假异常。完整的分工说明还可在 device-profiling-guide.md 的概述章节查阅。2. msprofkernel 级时间采集推荐2.1 使用方式msprof 是昇腾官方性能采集工具。推荐的用法是在scripts/run.sh中包裹被测可执行程序将采集输出落盘到指定目录msprof --application${EXEC_BIN} ${ARGS} --output./output/其中EXEC_BIN与ARGS为被测算子可执行程序及其参数。--output指定 PROF 数据的输出根目录。2.2 产出文件采集完成后在输出目录下会生成PROF_*/子目录其中 kernel 维度的执行时间位于output/PROF_*/mindstudio_profiler_output/task_time_*.csv该 CSV 的关键字段为字段含义kernel_typekernel 类型名SHMEM 算子通常以CORE相关的字符串标识task_time(us)该 kernel 的执行耗时微秒2.3 解析方法参考 data_statistic.py仓库中 examples/allgather/scripts/data_statistic.py 给出了标准解析模式核心逻辑如下df pd.read_csv(task_time_file) df df[df[kernel_type].str.contains(CORE, naFalse)] # 跳过 warmup 轮取 perf 轮平均 avg_time df.iloc[warmup:warmupperf_cycles][task_time(us)].mean()结合该脚本的完整实现有几点实战经验值得注意kernel 过滤get_time_data中实际使用了df[kernel_type].astype(str).str.contains(CORE|AIV, naFalse)即同时保留COREAIV/AIC 计算核与AIV类型的行避免把调度/搬移类 kernel 混入统计。warmup 与 perf 轮脚本顶部定义了WARM_UP_TIMES 10与PERF_TEST_CYCLE_TIMES 40即前 10 次 kernel 调用作为预热剔除之后每 40 轮取一组均值多组 tiling 配置之间通过start_row i * data_rows WARM_UP_TIMES定位各自的测量起点。多卡平均get_pref_path会遍历所有PROF_*目录将每个 PE 的时间数据累加后除以 PE 数perf_output perf_output / len(pref_file_list)得到所有卡的均值。结果落盘最终将均值写入tiling_df[Time(us)]并输出result.csv与 tiling 参数表合并成可复现的性能结果。如果你采用的是手动 repeat 而非 msprof则 warmup/统计轮次的约定见下文 第 5 节。3. SHMEM Cycle Profilingkernel 内片段级打点3.1 一句话理解msprof 提供的是 Host 侧调度的宏观 timelinetask_time、launch latency 等而 SHMEM 内置的 Cycle Profiling 提供kernel 内部逐 block、逐 frame 的 cycle 级细分用于回答kernel 的时间花在了哪一段代码上。3.2 使用方式与宏对在 kernel 代码中引入头文件并插入打点#include utils/prof/shmemi_prof.h SHMEMI_PROF_START(0); // 开始计时 frame 0 // ... 通信或计算代码 ... SHMEMI_PROF_END(0); // 结束计时 frame 0Host 侧在 stream 同步后导出数据aclrtSynchronizeStream(stream); aclshmemx_get_prof(nullptr, true);其中aclshmemx_get_prof的两参数形式为公开接口旧接口aclshmemx_show_prof()已标记为 deprecated应统一使用aclshmemx_get_prof(nullptr, true)替代两者行为等价。仓库中的 examples/allgather/main.cpp、examples/shmem_perftest/mte_perftest/main.cpp 以及 examples/hccs_sio_link/main.cpp 均有现成的调用示范。3.3 宏实现原理宏的源码位于 src/device/utils/prof/shmemi_prof.h其核心逻辑可还原为#define SHMEMI_PROF_START(pf_id) \ if ((pf_id) ACLSHMEM_CYCLE_PROF_FRAME_CNT AscendC::GetBlockIdx() ACLSHMEM_CYCLE_PROF_MAX_BLOCK) { \ __gm__ aclshmem_device_host_state_t *device_state aclshmemi_get_state(); \ if (device_state-profs ! nullptr device_state-profs-pe_id shmem_my_pe()) { \ pipe_barrier(PIPE_ALL); \ auto cycle AscendC::GetSystemCycle(); \ device_state-profs-block_prof[AscendC::GetBlockIdx()].cycles[pf_id] - cycle; \ } \ } #define SHMEMI_PROF_END(pf_id) \ if ((pf_id) ACLSHMEM_CYCLE_PROF_FRAME_CNT AscendC::GetBlockIdx() ACLSHMEM_CYCLE_PROF_MAX_BLOCK) { \ __gm__ aclshmem_device_host_state_t *device_state aclshmemi_get_state(); \ if (device_state-profs ! nullptr device_state-profs-pe_id shmem_my_pe()) { \ pipe_barrier(PIPE_ALL); \ auto cycle AscendC::GetSystemCycle(); \ device_state-profs-block_prof[AscendC::GetBlockIdx()].cycles[pf_id] cycle; \ device_state-profs-block_prof[AscendC::GetBlockIdx()].ccount[pf_id] 1; \ } \ }关键行为可概括为下表行为说明START减去当前 cycle使后续END的 cycle自然得到两次读数之差即该 frame 的 cycle 开销END累加 count同一 frame 在一轮 kernel 调用中可能被执行多次如循环体内打点ccount记录执行次数pipe_barrier(PIPE_ALL)每次打点前后插入全流水 barrier 以保证 cycle 读数准确这会引入额外同步开销影响绝对数值但不影响相同条件下的对比结论仅在指定 PE 采集通过device_state-profs-pe_id shmem_my_pe()门控其他 PE 不受影响block 数量限制仅GetBlockIdx() ACLSHMEM_CYCLE_PROF_MAX_BLOCK的 block 参与采集3.4 数据结构与环境变量采集使用的数据结构定义在 include/host_device/shmem_common_types.h#define ACLSHMEM_CYCLE_PROF_MAX_BLOCK 64 // 最大采集 block 数 #define ACLSHMEM_CYCLE_PROF_FRAME_CNT 1024 // 最大 frame 数 typedef struct { int64_t ccount[ACLSHMEM_CYCLE_PROF_FRAME_CNT]; // frame 被调用的次数 int64_t cycles[ACLSHMEM_CYCLE_PROF_FRAME_CNT]; // frame 累计 cycle 数 } aclshmem_prof_block_t; typedef struct { int32_t pe_id; aclshmem_prof_block_t block_prof[ACLSHMEM_CYCLE_PROF_MAX_BLOCK]; } aclshmem_prof_pe_t;启动前必须设置环境变量SHMEM_CYCLE_PROF_PE指定采集 PEexport SHMEM_CYCLE_PROF_PE0 # 在 PE 0 上采集Host 侧初始化逻辑位于 src/host/utils/prof/prof_util.cpp 的prof_util_init未设置SHMEM_CYCLE_PROF_PE时profs-pe_id置为 -1global_state-profs保持 nullptrkernel 内所有SHMEMI_PROF_START/END宏自动跳过采集逻辑当前 PE 与指定 PE 不匹配时同样跳过初始化匹配时才通过aclrtMalloc在 Device 端分配aclshmem_prof_pe_t并拷贝到 Device赋值给global_state-profs。由此可知采集仅发生在指定 PE 上且 prof 功能在初始化时自动判断、无需单独编译选项。3.5 Host 侧导出与 cycle2us 换算aclshmemx_get_prof(nullptr, true)的实现src/host/init/shmem_init.cpp最终调用prof_data_printsrc/host/utils/prof/prof_util.cpp输出固定格式表格 BlockID FrameID Cycles Count AvgTime(us) ------------------------------------------------------------ 0 0 12345678 4 61.728 0 1 8765432 4 43.827 0 2 23456789 4 117.284 各列含义列含义BlockID采集的 block 编号0 ~ min(block_dim, 64) - 1FrameIDkernel 中指定的frame_idCycles该 block 上该 frame 的累计 cycle 数Count该 block 上该 frame 的执行次数通常等于测量轮数 × 每次调用中的循环次数AvgTime(us)平均单次耗时 Cycles / Count / cycle2uscycle 到微秒的换算由prof_data_print内部根据aclrtGetSocName()自动完成Ascend910B 系列cycle2us 50Ascend950 系列cycle2us 1000另外aclshmemx_get_prof(out_profs, false)的非 verbose 形式可将 profiling 数据深拷贝到 Host 内存供程序自行落盘分析——examples/shmem_perftest/mte_perftest/main.cpp 中的collect_prof_data_to_csv_v2即采用该模式将 frame 数据导出为 CSV。3.6 Kernel 侧打点规范5 类 frame为保证报告可对比device-profiling-guide.md 规定 kernel 打点需覆盖 5 类执行阶段并使用稳定的frame_id#frame 类别说明典型打点位置1copy_in/copy_outGM、UB、symmetric buffer 之间的搬运DataCopyIn / DataCopyOut 前后2remote_put_getMTE/SDMA/RDMA 的跨 PE put/get 或 collective transportMTE put/get 调用循环前后3signal_or_barrier_waitsignal wait、barrier、quiet、handle wait 等同步signal_wait_until / barrier 之前4local_computematmul、reduce、layout transform、packing/unpacking计算核心循环前后5finalize写回 output、metadata 写入、tail/final reduce最后写回/收尾代码段前后不要求每个算子都同时使用全部 5 个 frame_id但性能报告中缺失某 frame 时必须说明该阶段不存在的理由例如纯通信算子无local_compute阶段。典型声明方式#include utils/prof/shmemi_prof.h constexpr int32_t kProfCopyIn 0; // copy_in: GM/UB/symmetric buffer 搬运 constexpr int32_t kProfRemotePutGet 1; // remote_put_get: MTE/SDMA/RDMA put/get 通信 constexpr int32_t kProfSignalWait 2; // signal_or_barrier_wait: 同步等待 constexpr int32_t kProfLocalCompute 3; // local_compute: matmul/reduce/layout 变换 constexpr int32_t kProfFinalize 4; // finalize: 写回/metadata/收尾通信算子常见的信号-搬运分离打点形态如下root 搬运并通知、非 root 等待后读取外层总耗时与内层 signal_wait 子阶段嵌套分离const int my_pe aclshmem_my_pe(); // frame 0: root 的 copy_in —— 将 input 搬运到 symmetric buffer if (my_pe root_pe) { SHMEMI_PROF_START(0); int64_t done 0; while (done local_len) { uint32_t copy_elems std::min((uint32_t)(local_len - done), ub_elems); aclshmemx_mte_put_nbi(gva_data start done, input start done, tmp_buf, ub_size, copy_elems, root_pe, EVENT_ID0); SetFlagHardEvent::MTE3_S(EVENT_ID0); WaitFlagHardEvent::MTE3_S(EVENT_ID0); aclshmemx_signal_op(gva_sync flag_offset, magic chunk_seq, ACLSHMEM_SIGNAL_SET, root_pe); done copy_elems; } SHMEMI_PROF_END(0); } // frame 2: read finalize外层包裹 // frame 1: signal_wait内层嵌套仅包裹同步等待 SHMEMI_PROF_START(2); int64_t done 0; while (done local_len) { SHMEMI_PROF_START(1); aclshmem_signal_wait_until(source_signal, ACLSHMEM_CMP_GE, magic chunk_seq); SHMEMI_PROF_END(1); uint32_t copy_elems std::min((uint32_t)(local_len - done), ub_elems); aclshmemx_mte_get_nbi(output start done, gva_data start done, tmp_buf, ub_size, copy_elems, root_pe, EVENT_ID0); SetFlagHardEvent::MTE3_MTE2(EVENT_ID0); WaitFlagHardEvent::MTE3_MTE2(EVENT_ID0); done copy_elems; } SHMEMI_PROF_END(2);3.7 打点注意事项与限制打点实践要点打点放在待计时代码段两端不要放在循环/条件之外循环内打点时同一 frame_id 会被执行多次END自动累加 cycles 和 count最终avg_us cycles / countpipe_barrier(PIPE_ALL)在SetFlag/WaitFlag密集的代码段前后会引入流水线效率损失建议打点放在较大的逻辑块边界而非逐条指令非性能采集路径的编译不要常驻包含SHMEMI_PROF宏避免静态开销。限制汇总限制详情单 PE 采集仅SHMEM_CYCLE_PROF_PE指定的 PE 参与采集其他 PE 无数据block 上限只采集block_idx ACLSHMEM_CYCLE_PROF_MAX_BLOCK64的 blockframe 上限frame_id ACLSHMEM_CYCLE_PROF_FRAME_CNT1024barrier 开销每次 START/END 包含pipe_barrier(PIPE_ALL)影响绝对值但不影响对比编译依赖prof 功能在 SHMEM 初始化时自动判断无需单独编译选项如果环境不支持 prof 采集如未设置SHMEM_CYCLE_PROF_PE、构建环境无 prof 支持性能报告中需说明缺失原因瓶颈分析可退化为基于 e2e/kernel event 时间结合代码路径反推。4. mssanitizer内存越界检查mssanitizer 是内存访问检查工具不直接用于性能采集但可用于排除因越界访问导致的性能异常或结果错误。用法是在被测程序前包裹mssanitizer --log-levelerror ${EXEC_BIN} ${ARGS}建议在两类场景下使用性能数据出现异常波动、明显偏离同量级配置时先用 mssanitizer 排除越界写入相邻内存导致的假慢或假快结果校验失败、怀疑对称内存访问越界时作为验证手段与结果正确性检查配合使用。5. 性能指标计算指标定义与公式的权威来源见 timing-and-metrics-standard.md本节补充工具特有的注意事项与核心公式。5.1 latency 的获取口径msprof 采集时latency_us从task_time(us)CSV 字段直接获取多 PE 运行时延迟取最大值性能指标平均值除外见 第 6 节。手动 Event 计时场景下e2e_us与kernel_us的起点/终点边界取决于数据放置方式做法 AMTE/SDMA/UDMA 在 kernel 内搬运下e2e_us ≈ kernel_us是正常现象做法 BRDMA 需 Host 侧aclrtMemcpy预置对称内存下e2e_us必须大于kernel_us差值即 kernel 外的搬运时间。公平对比 HCCL baseline 时必须使用e2e_latency_us。5.2 带宽相关指标统一按单 PE 语义数据量计算logical_payload_bytes示例all-to-all 为n_pes × shard_elems × sizeof(dtype)然后依次推导algo_bandwidth_GBps logical_payload_bytes / latency_s / 1e9 kernel_bus_bandwidth_GBps algo_bandwidth_GBps(kernel) × bus_factor bandwidth_utilization_percent kernel_bus_bandwidth_GBps / peak_bandwidth_GBps × 100bus_factor按算子类型取值本表为唯一参照源禁止在其他文件中定义偏差值算子bus_factor推导AllReduce2 * (n_pes - 1) / n_pesReduceScatter AllGather 两阶段ReduceScatter(n_pes - 1) / n_pes每步搬运 1/n 数据共 (n-1) 步AllGather(n_pes - 1) / n_pes同上AllToAll / Shuffle(n_pes - 1) / n_pes每个 PE 发送 (n-1)/n 数据Broadcast1单源广播P2P1点对点无放大因子dispatch / combine(n_pes - 1) / n_pes近似均匀路由假设路由不均匀时按实际远端搬运量推导peak_bandwidth_GBps须记录来源SoC、链路类型、PE 数、拓扑典型取值P2P 单条 HCCS 链路单向为 28 GB/s8 PE full-mesh 集合通信聚合为 196 GB/s 7 × 28dispatch/combine 稀疏路由按 P2P 口径取 28 GB/s。utilization 超过 100% 时应在报告中注释可能原因bus_factor 高估、链路实际带宽超出标称值。达标判断必须使用kernel_bus_bandwidth_GBps与 baseline 对比有 baseline 时默认 ≥ baseline 的 80%无 baseline 时通信算子带宽利用率 ≥ 20%e2e_bus_bandwidth_GBps与e2e_us仅作参考不可单独用于 PASS/FAIL 判定。6. 采集规范6.1 Warmupmsprof 采集前 10 次 kernel 调用为 warmup不计入统计手动 repeat前 10 轮为 warmup后续 40 轮取平均与 data_statistic.py 中的WARM_UP_TIMES 10、PERF_TEST_CYCLE_TIMES 40保持一致。6.2 多卡平均多 PE 运行时每个 PE 产出独立的 profiling 数据。性能指标取所有 PE 的平均值延迟取最大值。6.3 结果记录格式性能结果按以下固定格式记录便于对比与复现op_name: allgather dtype: int32 shape: M1024, N8 n_pes: 2 data_bytes: 32768 latency_us: 45.2 algo_bandwidth_GBps: 0.725 kernel_bus_bandwidth_GBps: 0.363 baseline: HCCL AllGather baseline_latency_us: 52.1 ratio: 115.3%其中ratio为 SHMEM 相对 baseline 的时延比值baseline_latency_us / latency_us × 100%大于 100% 表示快于 baseline。此外基于aclshmemx_get_prof的 frame 数据还应记录每个 frame 的cycles、count、avg_us、max_core_us、占端到端时间比例及长尾 block/PE优化优先从占比最大或长尾最明显的 frame 入手。7. 完整工作流与仓库参考一次规范的 SHMEM 算子性能评估可按下述流程执行插桩kernel 内按 5 类 frame 规范插入SHMEMI_PROF_START/END参考 shmemi_prof.h 与 device-profiling-guide.md配置export SHMEM_CYCLE_PROF_PE0在scripts/run.sh中用msprof --application${EXEC_BIN} ${ARGS} --output./output/包裹执行采集与导出kernel 执行完成后调用aclshmemx_get_prof(nullptr, true)打印 frame 表格或用aclshmemx_get_prof(out_profs, false)落盘 CSV参考 mte_perftest/main.cpp解析参照 data_statistic.py 过滤CORE|AIVkernel、跳过 warmup、多卡平均得到latency_us计算按 第 5 节 推导 algo/bus 带宽与利用率用kernel_bus_bandwidth_GBps与 baseline 对比判定达标记录按 6.3 节 的固定格式输出必要时配合mssanitizer --log-levelerror排除越界干扰。涉及的关键仓库文件速查打点宏源码src/device/utils/prof/shmemi_prof.hHost 侧导出实现src/host/utils/prof/prof_util.cpp、src/host/init/shmem_init.cpp数据结构与宏常量include/host_device/shmem_common_types.h解析脚本examples/allgather/scripts/data_statistic.py采集调用示例examples/allgather/main.cpp、examples/shmem_perftest/mte_perftest/main.cpp、examples/hccs_sio_link/main.cpp指标标准与打点规范timing-and-metrics-standard.md、device-profiling-guide.md【免费下载链接】shmemCANN SHMEM 是面向昇腾平台的多机多卡内存通信库基于OpenSHMEM 标准协议实现跨设备的高效内存访问与数据同步。项目地址: https://gitcode.com/cann/shmem创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考