CPU 性能分析
Deno 内置支持 V8 CPU 性能分析,可帮助你识别代码中的性能瓶颈。使用 --cpu-prof 标志在程序执行期间捕获 CPU 分析文件:
deno run --cpu-prof your_script.ts
# 或使用 deno eval
deno eval --cpu-prof "for (let i = 0; i < 1e8; i++) {}"
当程序退出时,Deno 会将一个 .cpuprofile 文件写入当前目录(例如 CPU.1769017882255.25986.cpuprofile)。该文件可以加载到 Chrome DevTools(Performance 选项卡)或其他 V8 分析查看器中进行分析。
CPU 分析标志 Jump to heading
| Flag | 描述 |
|---|---|
--cpu-prof |
启用 CPU 分析。退出时将分析文件写入磁盘。 |
--cpu-prof-dir=<DIR> |
CPU 分析文件的写入目录。默认为当前目录。会隐式启用 --cpu-prof。 |
--cpu-prof-name=<NAME> |
CPU 分析文件的文件名。默认为 CPU.<timestamp>.<pid>.cpuprofile。 |
--cpu-prof-interval=<MICROSECONDS> |
采样间隔,单位为微秒。默认值为 1000(1ms)。更低的值会提供更多细节,但文件也会更大。 |
--cpu-prof-md |
生成一个人类可读的 Markdown 报告,与 .cpuprofile 文件并排生成。 |
--cpu-prof-flamegraph |
生成一个交互式 SVG flamegraph,与 .cpuprofile 文件并排生成。 |
CPU 分析会报告转译后的 JavaScript 代码行号,而不是原始 TypeScript 源码。这是 V8 分析器的限制。对于 TypeScript 文件,报告中的行号可能不会与源代码直接对应。
自定义分析输出 Jump to heading
默认情况下,分析文件会写入当前目录,并使用自动生成的文件名。你可以控制分析文件的保存位置和方式:
# 将分析文件保存到特定目录
deno run --cpu-prof --cpu-prof-dir=./profiles your_script.ts
# 使用自定义文件名
deno run --cpu-prof --cpu-prof-name=my-profile.cpuprofile your_script.ts
# 提高采样频率以获得更多细节(默认:1000μs)
deno run --cpu-prof --cpu-prof-interval=100 your_script.ts
更低的 --cpu-prof-interval 会让每秒捕获更多样本,从而获得更细的粒度,但代价是分析文件更大。默认的 1000 微秒(1ms)对大多数使用场景来说是一个不错的平衡。对于你想要详细捕获的短生命周期函数,可以尝试 100(0.1ms)。
在 Chrome DevTools 中分析 Jump to heading
要分析 .cpuprofile 文件:
- 打开 Chrome DevTools(F12)
- 转到 Performance 选项卡
- 点击 Load profile 按钮(向上箭头图标)
- 选择你的
.cpuprofile文件
DevTools 将显示 flame chart,以及应用中时间耗费位置的详细分解。
示例:Markdown 报告 Jump to heading
--cpu-prof-md 标志会生成一个易于阅读的 Markdown 摘要,无需将分析文件加载到 DevTools 中:
deno run -A --cpu-prof --cpu-prof-md server.js
这会同时创建一个 .cpuprofile 文件和一个 .md 文件,报告如下:
# CPU Profile
| Duration | Samples | Interval | Functions |
| -------- | ------- | -------- | --------- |
| 833.06ms | 641 | 1000us | 10 |
**Top 10:** `op_crypto_get_random_values` 98.5%, `(garbage collector)` 0.7%,
`getRandomValues` 0.6%, `assertBranded` 0.2%
## Hot Functions (Self Time)
| Self% | Self | Total% | Total | Function | Location |
| ----: | -------: | -----: | -------: | ----------------------------- | ----------------- |
| 98.5% | 533.00ms | 98.5% | 533.00ms | `op_crypto_get_random_values` | [native code] |
| 0.7% | 4.00ms | 0.7% | 4.00ms | `(garbage collector)` | [native code] |
| 0.6% | 3.00ms | 0.6% | 3.00ms | `getRandomValues` | 00_crypto.js:5274 |
| 0.2% | 1.00ms | 0.2% | 1.00ms | `assertBranded` | 00_webidl.js:1149 |
## Call Tree (Total Time)
| Total% | Total | Self% | Self | Function | Location |
| -----: | -------: | ----: | -------: | ----------------------------- | ----------------- |
| 16.8% | 91.00ms | 16.8% | 91.00ms | `(anonymous)` | server.js:1 |
| 0.6% | 3.00ms | 0.6% | 3.00ms | `getRandomValues` | 00_crypto.js:5274 |
| 98.5% | 533.00ms | 98.5% | 533.00ms | `op_crypto_get_random_values` | [native code] |
## Function Details
## `op_crypto_get_random_values`
[native code] | 自身:98.5%(533.00ms) | 总计:98.5%(533.00ms) | 样本:533
该报告包括:
- 摘要:总持续时间、样本数量、采样间隔和函数数量
- Top 10:最耗时函数的快速概览
- Hot Functions:按自身耗时排序的函数(函数自身代码中耗费的时间,不包括被调用函数)
- Call Tree:显示调用栈和时间分布的层级视图
- Function Details:按函数细分的样本计数
示例:交互式 flamegraph Jump to heading
--cpu-prof-flamegraph 标志会生成一个自包含、交互式的 SVG flamegraph,你可以直接在浏览器中打开,无需外部工具:
deno run --cpu-prof --cpu-prof-flamegraph your_script.ts
这会同时创建一个 .cpuprofile 文件和一个 .svg 文件。在任意浏览器中打开 SVG 以交互式探索分析结果:
- Click 任意框以缩放到该子树
- Reset Zoom 按钮可恢复完整视图
- Ctrl+F 或 Search 按钮用于基于正则表达式的函数搜索,并带有高亮和匹配百分比
- Invert 复选框可切换为 icicle graph(根在顶部)
- Hover 任意框可查看函数名和样本计数
flamegraph 也可与 deno eval 一起使用:
deno eval --cpu-prof --cpu-prof-flamegraph "for (let i = 0; i < 1e8; i++) {}"
性能分析技巧 Jump to heading
- 分析具有代表性的工作负载:对于 HTTP 服务器,在停止之前向服务器发送真实流量——分析文件只会捕获程序运行期间发生的内容。
- 使用自身耗时与总耗时:在分析报告中,自身耗时 是函数自身代码中花费的时间,而 总耗时 包括其调用的函数所花费的时间。较高的自身耗时指向真正的瓶颈;总耗时高但自身耗时低,则表示该函数把工作委托给了某个昂贵的部分。
- 前后对比:使用描述性的
--cpu-prof-name值保存分析文件(例如before-optimization.cpuprofile),这样你就可以在修改后在 DevTools 中并排比较分析结果。 - 组合输出格式:你可以将
--cpu-prof-md和--cpu-prof-flamegraph结合使用,在一次运行中获得全部三种输出(.cpuprofile、.md和.svg):>_deno run --cpu-prof --cpu-prof-md --cpu-prof-flamegraph your_script.ts - 过滤噪声:短生命周期程序可能会显示启动开销(模块加载、JIT 编译)主导分析结果。为了获得更准确的结果,请确保你想分析的代码运行足够长,以收集有意义的样本。