Mojo 内核自动调优结果分析:kprofile 与 tuning_codegen 实战指南
【免费下载链接】mojoThe Modular Platform (includes MAX & Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo
kprofile与tuning_codegen是 Mojo/MAX 仓库中max/kernels/benchmarks/autotune目录下的两个配套工具:前者用于回顾、筛选并从kbench基准测试生成的pkl结果文件中提取洞察,后者负责把筛选出的配置以 YAML 清单(manifest)为输入,确定性生成检入(checked-in)到 Mojo 源码中的调优表区域。读完本文,你将掌握如何用一条命令解析调优结果、如何挑选最优配置,以及如何把选中的配置稳定地渲染进tuning_configs.mojo并跑通新鲜度测试,从而形成"基准测试 → 结果分析 → 配置落库"的完整闭环。
本文以 README_kprofile.md 为主体,结合同目录下的 kprofile.py、tuning_codegen.py、BUILD.bazel 以及真实的 SM100 调优清单与测试用例展开讲解。
一、kprofile 是什么
kprofile是一个 Python 实现的命令行工具,作用是"剖析kbench输出的 pickle 文件"。kbench会在一个参数网格(grid)上构建并执行 Mojo 内核基准测试,把每次运行的 spec、指标与元数据(如 git revision、datetime)打包进pkl文件;kprofile则负责把这些二进制结果变成可读、可筛选、可下钻的视图,并进一步输出可供后续代码生成使用的 YAML 调优清单。
你可以给kprofile同时传入多个pkl文件,它会依次展示每个文件的输出,并按 shape(形状,即一组固定的维度参数组合)对结果分组,同时允许你选择不同的调优参数视角。从源码结构看,其核心数据流位于 kprofile.py 中的KbenchPKL类:
- 加载 pickle 后取出
merged_df,并丢弃name、iters两列; - 根据指定的
metric(默认met (ms))做前缀模糊匹配,找到合法的指标列; - 按指标的好坏方向排序(
ascending_sort字典定义:met (ms)越低越好,其余如吞吐量、带宽类指标越高越好),生成tune_df; - 计算每条记录相对最优记录的
ratio(当前值 / 最优值),作为后续简化表格展示的基础。
可用的命令行选项
kprofile基于click实现,完整参数定义见 kprofile.py 中的 CLI 装饰器。常用选项汇总如下:
| 选项 | 短选项 | 默认值 | 说明 |
|---|---|---|---|
--output | -o | output.mojo | 输出文件路径(生成 YAML 时用于写盘) |
--top | -t | 0.0 | 从结果前 top% 中为每个参数统计出现最频繁的值,形成新 spec |
--ratio | -r | false(flag) | 打印每个条目相对最优条目的运行时间比值表 |
--head | 无 | -1 | 按运行时间排序后,打印前 N 条 |
--tail | 无 | -1 | 按运行时间排序后,打印后 N 条 |
--diff | -d | false(flag) | 对比多个 pkl 的最优运行时间,第一个作为基线 |
--metric | -m | met (ms) | 指定剖析指标,如吞吐量、带宽、GFLOPS 等 |
--pivots | -p | [] | 指定 pivot(分组维度)来选择值,支持[M] [N]或[M,N]写法 |
--sort-pivots | 无 | [] | 指定用于排序的 pivot |
--merge | 无 | false(flag) | 将多个 YAML 按 pivot 合并(此时输入必须都是.yaml) |
--query | 无 | None | 对合并结果执行查询,如'M>64 and K<1024' |
--correlation | -c | false(flag) | 对多个 pkl 做 Kendall 相关性分析并输出热力图 |
--verbose | -v | false(flag) | 打印所有(未排序的)条目 |
其中--pivots使用了自定义的ComplexParamList选项类型(见 kprofile.py),因此--pivot=[M] --pivot=[N] --pivot=[K]与--pivot=[M,N,K]完全等价。--top的实现逻辑是:阈值 =(top_percentage + min/max) * max,选取低于该阈值的记录(见top_idx函数,kprofile.py),随后用value_counts().idxmax()为每个参数取出现次数最多的值,合成一个"常见参数" spec。
二、回顾与筛选 kbench 结果
原文档给出了五种最常用的结果查看姿势,以下逐一展开说明其输出含义与适用场景。
1. 直接打印最优结果
kprofile output.pkl默认行为:加载output.pkl,按指标排序后打印条目总数、最优条目的mesh_idx、最差与最优的指标值、最差/最优比值,以及该最优 spec 对应的 shape(由非 pivot 列组成,形如M=256/N=20480/K=5376),最后给出最佳 spec 的完整参数。若 pkl 无效会打印Invalid pkl [...]并跳过(见profile_results,kprofile.py)。
2. 提取 top 5% 的共性参数
kprofile output.pkl --top 0.05--top 0.05表示取最优的 5% 结果,统计每个参数在这些结果中出现频率最高的取值,合成一个"共性 spec"。这在自动调优中非常实用:当大量近似最优的配置共享某些参数值时,说明这些值对性能的稳健性更好,可以作为后续人工筛选或默认配置的依据。加-v还可以打印出这 5% 子集的完整明细。
3. 简化比值表
kprofile sample.pkl -r-r/--ratio会打印一张简化表格,展示每条记录相对最优记录的运行时间比值(当前值 ÷ 最优值),比值列以绿色加粗高亮(见df_to_console_table的col_style,kprofile.py)。比值越接近 1 说明与最优配置越接近,是快速评估参数敏感性的直观方式。
4. 打印最好与最差两端
kprofile sample.pkl --head 10 --tail 10--head 10打印按指标排序后的前 10 条(最好),--tail 10打印后 10 条(最差)。两段各输出一张独立表格,方便对比"好配置"与"差配置"之间的参数差异,找出导致性能滑坡的关键参数。
5. 多文件分组对比
kprofile file*.pk --head 2当一次传入多个 pkl(例如file1.pk、file2.pk……)时,kprofile会依次展示每个文件的结果并按 shape 分组,--head 2让每个文件只显示最优的两条。这在比较同一内核在不同分支、不同日期或不同硬件上的调优产出时非常有用。若想量化比较多个 pkl 的最优值,可以使用--diff:
kprofile base.pkl new.pkl --diff --head 5--diff以第一个文件为基线,逐 shape 输出当前文件最优值相对基线的比值,并将结果写入shape 对应的 <shape>.csv(见diff_baseline,kprofile.py)。
进阶:YAML 合并、查询与相关性分析
除 pkl 外,kprofile也接受 YAML 输入:--merge模式要求输入全部为.yaml,按 pivot 做 outer join 合并后可用--query过滤(如--query 'M>64 and K<1024'),随后把满足条件的 spec 直接输出为新的 YAML(走codegen_yaml流程)。--correlation则对多个 pkl 合并后的数值列计算 Kendall 相关性,输出按指标绝对相关性排序的表与correlation.png热力图(见correlation_analysis,kprofile.py)。
三、从 YAML 清单生成检入的调优表
3.1 清单(manifest)的角色
kprofile分析完基准测试后,会把选中的配置写入 YAML。对于检入的分发表(dispatch table)来说,这个 YAML 就是manifest——一份需要出现在 Mojo 代码中的、有序的配置列表。真实的 manifest 示例见 tuning_table_sm100_bf16.yaml:文件顶部通过defaults: &defaults锚点集中声明共享参数(如MMA_K: 16、CTA_GROUP: 2、SWAP_AB: false、各流水线阶段数等),params列表中的每个条目用<<: *defaults合并锚点,再覆盖该条目的独有参数(M、N、K、MMA_M、MMA_N、CLUSTER_X等)。这种结构让清单既紧凑又易读:共享配置只维护一份,条目之间只需比较差异。
3.2 生成机制:snippet + 占位符 + 标记
tuning_codegen是把 manifest 变成检入 Mojo 的受支持路径(supported path)。它的核心机制分为三步(对应 tuning_codegen.py 中的函数):
- 渲染(render):
render_snippet读取一个 snippet 模板,将其中所有[@NAME]形式的占位符替换为 manifest 条目中的对应值。若 snippet 中存在 manifest 未提供的占位符,会直接报错Missing snippet parameters: ...,避免生成残缺代码;布尔值按True/False输出,None输出为None(见 tuning_codegen.py)。 - 区域生成(render_region):每个条目渲染结果前会追加两行溯源注释:
# Automatically generated from [source_label]与# index: [N],默认以 8 个空格缩进(默认indent=" "),并以逗号结尾(见 tuning_codegen.py)。 - 区域替换(replace_generated_region):在目标 Mojo 文件中找到唯一的
begin_marker与end_marker行,替换两者之间的全部内容。标记必须恰好出现一次且 begin 先于 end,否则抛错,从机制上杜绝误替换(见 tuning_codegen.py)。
每个 manifest 都有一个主 snippet(primary snippet)。当一张表内混有多种 Mojo 构造器形态时,条目可以通过_template字段指定使用某个 snippet 变体;若所有条目形态一致,则只用主 snippet 即可。--snippet-variant NAME=PATH可重复传入,为每个变体注册模板文件;render_region中若_template引用了未注册的变体,会报unknown template错误。
以 SM100 BF16 为例,其 snippet 模板 tuning_sm100.mojo.snippet 形如:
TuningConfigSM100( M=[@M], M_end=[@M_END], N=[@N], K=[@K], mma_shape=Index([@MMA_M], [@MMA_N], [@MMA_K]), cta_group=[@CTA_GROUP], cluster_shape=Index([@CLUSTER_X], [@CLUSTER_Y], [@CLUSTER_Z]), block_swizzle_size=[@BLOCK_SWIZZLE_SIZE], rasterize_order=RasterOrder([@RASTERIZE_ORDER]), swapAB=[@SWAP_AB], k_group_size=[@K_GROUP_SIZE], num_accum_pipeline_stages=[@NUM_ACCUM_PIPELINE_STAGES], num_clc_pipeline_stages=[@NUM_CLC_PIPELINE_STAGES], num_split_k=[@NUM_SPLIT_K], num_pipeline_stages=[@NUM_PIPELINE_STAGES], is_small_bn=[@IS_SMALL_BN], batch_size=[@BATCH_SIZE], )清单中的MMA_K、CTA_GROUP等参数会与 snippet 中的[@...]占位符一一对应;render_snippet也支持#注释行,因此模板中可保留被注释掉的字段作为文档。单元测试 tests/test_tuning_codegen.py 验证了render_snippet的格式化(布尔输出为True)与缺失参数报错行为,并验证了 manifest 保序、重复条目报错、标记唯一性约束、_template变体选择以及端到端的generate_target渲染结果。
四、完整的调优表工作流
原文档给出了从结果分析到代码落库的五步标准流程,此处结合真实文件路径逐步展开。
步骤 1:在 kbench 输出上运行 kprofile 生成/更新 YAML manifest
kprofile output.pkl -o tuning_table_sm100_bf16kprofile会把选中配置写入tuning_table_sm100_bf16.yaml(--output指定文件名,实际写盘时会以.yaml后缀落盘,见codegen_yaml,kprofile.py),同时打印 common 列、pivot 列与条目数。若已存在旧 manifest,可直接覆盖更新,也可以结合--top、--head等筛选条件控制写入的配置集。
步骤 2:审阅选中的配置
打开生成的 YAML,核对每个条目的参数是否合理:共享参数是否收敛、params顺序是否符合预期(顺序会被原样保留进 Mojo 表)、是否存在冗余或重复条目(tuning_codegen的load_manifest会对重复条目直接报错,见 tuning_codegen.py)。
步骤 3:运行 tuning_codegen 更新被标记的 Mojo 区域
以 SM100 BF16 表为例,完整命令如下:
./bazelw run //max/kernels/benchmarks/autotune:tuning_codegen -- \ --manifest max/kernels/src/linalg/matmul/gpu/sm100_structured/default/tuning_table_sm100_bf16.yaml \ --snippet max/kernels/src/linalg/matmul/gpu/sm100_structured/default/tuning_sm100.mojo.snippet \ --target max/kernels/src/linalg/matmul/gpu/sm100_structured/default/tuning_configs.mojo \ --begin-marker BEGIN-TUNING-LIST-SM100-BF16 \ --end-marker END-TUNING-LIST-SM100-BF16 \ --source-label tuning_table_sm100_bf16.yaml各参数含义:
| 参数 | 说明 |
|---|---|
--manifest | YAML 清单路径,即上一步 kprofile 生成的文件 |
--snippet | 主 snippet 路径,逐条目展开的 Mojo 模板 |
--target | 检入的 Mojo 目标文件,其中包含待替换的标记区域 |
--begin-marker/--end-marker | 标记区域的开头/结尾唯一文本,二者之间会被整体替换 |
--source-label | 写入生成注释的清单标签,用于溯源 |
--snippet-variant | 可选,NAME=PATH形式,可重复,配合条目_template使用 |
--check | 校验模式:只报告 diff 不写盘,若区域过期则以非零退出码失败 |
目标文件 tuning_configs.mojo 内以BEGIN-TUNING-LIST-SM100-BF16/END-TUNING-LIST-SM100-BF16圈定的区域会被重新生成,每个条目渲染结果形如:
# Automatically generated from [tuning_table_sm100_bf16.yaml] # index: [0] TuningConfigSM100(M=256, M_end=320, N=20480, K=5376, ...),--check模式的实现位于 tuning_codegen.py:它先在内存中生成期望内容,与当前文件内容比对,不一致时通过difflib.unified_diff输出差异并触发SystemExit,使 CI 能够捕获"检入区域过期"的问题。
步骤 4:格式化
./bazelw run //:format代码生成后统一跑一次仓库级格式化,确保生成的区域与手写代码风格一致、diff 干净。
步骤 5:运行该表的新鲜度测试
./bazelw test \ //max/kernels/benchmarks/autotune:sm100_bf16_tuning_codegen_test该测试以--check方式运行tuning_codegen,在内存中重新生成区域并对比检入的 Mojo,若不一致即失败。其 Bazel 定义见 BUILD.bazel,除了 BF16 表,仓库还为 FP8、NVFP4、MXFP4、MXFP8、batched、small-MN 以及 SM90 等多张表分别注册了sm100_*_tuning_codegen_test、sm90_bf16_tuning_codegen_test等测试,其中涉及多种构造器形态的表会通过--snippet-variant传入多个模板(如 SM90 的custom、custom_grid、custom_scaled、custom_scaled_grid,见 BUILD.bazel)。调优清单与测试数据的依赖统一由tuning_codegen_table_datafilegroup 声明,任何新增表格只需按同样模式补充 manifest、snippet 与测试目标即可接入这套流水线。
五、小结
kprofile与tuning_codegen的组合,为 Mojo/MAX 的内核调优提供了一条可审计、可复现、可自动化的流水线:kbench产出原始pkl→kprofile用多种视角(top%、比值表、head/tail、diff、相关性、YAML 合并查询)筛选出最优配置并固化为 YAML manifest →tuning_codegen依据 manifest 与 snippet 模板,在唯一标记之间确定性渲染出带溯源注释的 Mojo 调优表 → 格式化与--check新鲜度测试守护检入区域的同步。整个过程以 YAML 清单为唯一事实来源(single source of truth),生成的代码可随时重新生成、可 diff、可测试,非常适合在多 GPU 架构(SM90、SM100)与多数据类型(BF16、FP8、MXFP4 等)间持续维护大规模调优配置。
【免费下载链接】mojoThe Modular Platform (includes MAX & Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考