| 名称 | warp-编译时间优化器 |
| 描述 | >- 当代码中使用了Warp并且编译时间或启动时间是问题时使用: 要求改进、优化或削减编译时间;应用程序启动缓慢或在第一次wp.launch时停滞; 在真正工作开始前编译数秒;每次运行或每次CI作业时JIT模块重新编译。仅当被优化代码使用Warp核时适用。 不适用于稳态核运行时间、内存、正确性、从源码构建Warp本身或nvcc/C++编译时间。 |
| 开源协议 | Apache-2.0 兼容性: 需要Python 3.10+和已安装的warp-lang包。需要CUDA设备来诊断特定于CUDA的机制。 allowed-tools: Bash, Read, Edit, Write, Glob, Grep, env metadata: 版本: “0.1.0” 作者: “Warp Team warp-python@nvidia.com” 标签: - warp - 编译 - 冷启动 - 启动延迟 - 内核缓存 - gpu |
Warp 冷启动编译时间
以一个可运行命令开始
探针在子进程中运行目标,仅写入临时目录,并且不需要网络、外部工具服务器或Warp检出版本。
| 脚本 | 目的 | 参数 |
|---|---|---|
scripts/warp_compile_probe.py |
测量隔离的冷/热编译和启动。 | measure [OPTIONS] -- COMMAND...;使用 --help。 |
支持则使用 run_script("scripts/warp_compile_probe.py", args=[...]);否则使用下面的Python命令。目标必须运行到完成。
编译模型
Warp编译模块,而不是单个核。模块的标识是:
(活动内核和函数集) x (模块选项) x (CUDA block_dim) x (泛型实例)
每个标识都需要为整个模块进行代码生成和本机编译。
冷启动成本大致为:
你接触的不同模块标识数量 x 每个模块的大小
通过两种方式减少:
- 停止标识搅动。加载后发生变化的模块会再次编译。
- 停止源代码重复。在三个块维度上使用的模块会将每个内核编译三次。
从仍然构建的模块中删除一个内核仅节省一次构建的一部分。移除不必要的模块标识节省完整构建。
当两者都不适用时,重叠独立的CUDA构建(CS-13)。这会改变工作发生的时间,而不是编译的总量,因此请根据经过的时间来判断。
当模块的选项固定时
选项有两个截止时间:
- 模块创建(Python导入)。 模块从
warp.config复制enable_backward、max_unroll、lineinfo、deterministic、deterministic_max_records和compile_time_trace。之后设置全局变量会被该模块静默忽略。default_grid_stride是例外。 - 首次加载。 在编译之前,使用
wp.set_module_options()或wp.get_module(name).options更改现有模块。加载后更改选项会创建新标识并重新构建模块(CS-3)。
| 错过的截止时间 | 症状 | 成本 |
|---|---|---|
导入后设置wp.config.* |
哈希不变,选项静默缺失 | 全部收益,不可见地消失 |
| 加载后设置模块选项 | 第二个哈希,模块构建两次 | 一次额外构建,在跟踪中可见 |
更改选项后,确认每个目标模块的哈希都已移动。哈希不变意味着选项从未生效。
保留行为
更改Warp编译代码的方式,而不是工作负载。
不要为了声称收益而删除或合并内核。看似冗余的阶段可能保留所有权、别名、保留输出、数值边界或API行为。在模块级别修复重复。
保留每次启动及其顺序、维度、dtypes、设备、块维度、梯度、数值模式、动态/插件行为和公共API签名。将定义移动到模块作用域时保持内核名称不变,因为日志、缓存工件和外部工具会暴露它们。
说明
1. 找出真实成本,并确认它是编译
询问用户实际等待的命令,然后冷测量它:
python scripts/warp_compile_probe.py measure --samples 3 \
--json baseline.json -- <用户命令>
探针为每个样本提供私有的WARP_CACHE_PATH、WARP_CACHE_ROOT和CUDA_CACHE_PATH目录,启用模块计时器,并记录启动。它在系统临时目录下创建这些目录并自行删除,因此隔离样本永远不需要向项目写入缓存或删除任何东西来重新测量。同样地隔离手工样本。绝不要使用wp.clear_kernel_cache()或wp.clear_lto_cache()清除活动缓存;清除不是隔离的,可能干扰其他进程。
在源代码之前读取探针输出。如果编译只是墙钟时间的一小部分,报告真正的瓶颈并停止。对于库和测试,使用编译工作负载模块的最小命令。
每个模块都编译一次,没有重复哈希、块维度变体或LTO,表明没有结构性搅动。这排除了冗余构建,但不能排除过大的构建;仍需检查缓存复用(CS-2)、反向代码生成(CS-10)、展开(CS-11)、预编译头文件(CS-12)以及当剩余多个CUDA模块时的重叠(CS-13)。
每个样本也会针对刚填充的缓存重新运行命令。如果热模块工作不是接近零,在更改模块结构之前诊断缓存复用(CS-2)。
2. 在需要时询问运行时权衡
无需询问即可应用排序修复、生命周期分组和选项提升。在更改fast_math、max_unroll或MathDx/磁贴实现之前询问:
其中一些旋钮减少编译时间,但可能使编译后的内核变慢或改变数值。您是在优化快速编辑-运行循环(较慢的内核通常没问题),还是优化生产启动(通常不是)?
如果用户不可用:
- 让改变数值的选项和实现交换保持不变。
- 在移除诸如反向代码生成之类的功能之前,测试它是否被使用;报告移除的内容、测量到的收益以及如何回滚。
- 在应用程序入口点设置全局
wp.config.*选项,而不是在库代码中。
在被拒绝的选项及其测量收益记录在第6步的账本中。
使更改范围与证据范围匹配
配置文件支持对被测应用程序的更改,而不是共享库的每个使用者。仓库搜索也会遗漏外部和未来的调用者。例如,仅前向的应用程序并不能证明在共享库内禁用梯度是合理的,因为另一个应用程序可能通过其进行微分。
将选项范围限定到被测进程,在导入库之前:
import warp as wp
wp.config.enable_backward = False # 必须位于库导入之前
import the_library
这也会到达应用程序加载的每个模块。CS-10涵盖了静默导入顺序陷阱。如果只有库更改有效,请将测量结果发送给维护者,让他们决定契约。
3. 基于测量而不是阅读源来诊断
探针打印每个编译模块标识,包括名称、哈希、设备和块维度,然后命名哪些模块构建了不止一次。将你看到的内容与此匹配:
| 探针显示 | 含义 | 查看位置 |
|---|---|---|
| 一个模块名,多个哈希 | 身份搅动:其内核集、选项或泛型实例在首次加载后改变 | CS-1, CS-3, CS-6 |
| 一个模块名,多个block_dim值 | 整个模块为每个块维度重新编译(CUDA) | CS-5 |
| 一个功能中许多单内核模块 | 重复的每模块固定成本 | CS-4 |
| 每个内核一个哈希命名模块 | 在稳定内核上使用module="unique" |
CS-9 |
模块时间和本机编译时间之间的大差距,加上.lto工件 |
MathDx/LTO设置 | CS-7 |
在应该热的情况下运行(compiled) |
缓存未被复用 | CS-2 |
| 模块加载,然后"Failed to find module" | 并发CPU JIT首次使用竞争 | CS-8 |
| 大量生成的源,无重建问题 | 展开预算 | CS-11 |
| 模块中含有伴随代码且无任何不同 | 反向代码生成 | CS-10 |
| 整体编译缓慢,或CUDA上几个小模块低于工具包13 | 预编译头文件被关闭,或未产生效益 | CS-12 |
多个独立模块,每个构建一次,overlap_factor接近1.0 |
构建按顺序运行;并行加载默认关闭 | CS-13 |
| 你设置的选项没有改变任何东西,且该模块的哈希不变 | 它在模块创建后被赋值,因此从未生效 | “当模块的选项固定时” |
| 上面没有触发 | 没有冗余构建;成本是构建本身的大小 | 第6步 |
references/mechanisms.md中的每个机制一节:如何确认、修复、限制和失败模式。仅阅读测量选定的部分。
4. 有意选择模块边界
仅在以下情况下将内核分组到一个模块中:
- 生命周期:它们一起定义、加载和失效;
- 选项集:它们需要相同的
fast_math、enable_backward、max_unroll和MathDx设置; - CUDA上稳定的块维度。
具有相同生命周期但不同稳定块维度的内核不应共享一个模块,因为每个都会编译两次。也要分隔生命周期独立的内核。
其块维度在运行时变化的内核(例如,根据输入大小选择)没有稳定的映射,因此将它们保留在自己的模块中,而不是将整个共享模块拖入额外的变体。
优先采用移除构建的最小侵入更改。排序修复和选项提升比重构模块布局更便宜、更安全;只有在每模块固定成本或块维度复制主导时才重新分组。
wp.set_module_options()针对其调用的Python模块,而不是显式module="pkg.name"的内核。要么使用真实的Python模块,要么在命名模块加载前更新它:
wp.set_module_options({"enable_backward": False}) # 在模块作用域
wp.get_module("pkg.name").options.update({"enable_backward": False})
不要将wp.get_module()传递给wp.set_module_options(module=...),也不要在没有module="unique"的情况下使用@wp.kernel(module_options={...})。每内核算子enable_backward=False具有磁贴模块例外,涵盖于CS-10。在每次选项更改后确认模块哈希。
5. 验证
python scripts/warp_compile_probe.py measure --samples 3 \
--json candidate.json -- <同一命令>
python scripts/warp_compile_probe.py compare baseline.json candidate.json
compare拒绝更改的启动拓扑,并将max(1% of baseline, 2 x baseline MAD)以内的结果视为不确定。
对于BUILDS OVERLAPPED,基于编译经过时间而不是模块计时器总和来判定调度更改。必需的暖通道提供了该时钟。参见references/measurement.md。
然后检查探针看不到的内容:
- 比较数值输出并运行项目的测试或入口点。
- 更改
enable_backward或边界后,验证梯度路径。 - 更改
fast_math、max_unroll、MathDx或实现后,基准测试稳态运行时间。 - 测试未覆盖的动态内核、dtypes、配置和模式。
6. 报告结果
阅读references/measurement.md中的"Reporting results"。报告:
用简单的语言描述每个优化:命名行为、证据和效果。例如,写"将模块选项移动到首次加载之前以避免冗余重建",而不是"应用了CS-3"。将CS-*标签视为内部导航辅助,而不是面向用户的解释。
| 选项 | 测量 | 为何未采用 | 要采用它 |
|---|---|---|---|
pkg.solver上的enable_backward=False |
−38%冷 | 活动的tape遍历这些内核 | 在入口点设置,然后重新检查伴随 |
max_unroll=4 |
−2%,在噪声内 | 为没有测量到的收益改变生成代码 | — |
- 前后中位数和样本数;
- 缩减和剩余成本,根据原始投诉大小调整;
- 修复、排除、测量并拒绝或未到达的机制;
- 未验证的权衡和行为;
- 上述被拒绝或未完全验证选项的账本;
- 放松任何阻止修复的约束的测量值。
只陈述证据支持的。“无结构性搅动"并不意味着"最优"或"不可归约”。在可行时测量被拒绝的杠杆;将任何估计标记为未测试。在报告没有可用修复之前,检查CS-13。对于可能的模块拆分,首先用相同选项测量单内核模块以建立重复的固定成本。
故障排除
在调试探针之前直接运行目标命令。缓存/噪声问题参见references/measurement.md,机制特定故障参见references/mechanisms.md。
限制
两条规则覆盖任何收益:
- 对于每个冷样本隔离Warp和CUDA缓存。
- 当加载可能针对CPU时,包括
device=None和混合设备列表,保持max_workers <= 1。并发CPU首次加载可能丢失内核;重试不会使其安全。仅CUDA加载不受影响。
测量结果因环境而异:冷时间随CPU、GPU、驱动程序、工具链和Warp版本变化。机制可转移;数字不可。在做出广泛声明之前,阅读references/mechanisms.md中的已知未知数。
参考文件
references/mechanisms.md:十三种编译时间机制,每种都有确认信号、修复、适用性限制和失败模式。阅读你的测量指向的部分。references/measurement.md:测量协议、每个指标的含义和限制、报告指南、日志示例和手动测量。