--- title: "CPU 性能分析" description: "使用内置 CPU 分析器对 Deno 程序进行性能分析:收集分析文件、Markdown 和 flamegraph 报告、Chrome DevTools 分析以及分析技巧。" --- Deno 内置支持 V8 CPU 性能分析,可帮助你识别代码中的性能瓶颈。使用 `--cpu-prof` 标志在程序执行期间捕获 CPU 分析文件: ```sh 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 分析标志 | Flag | 描述 | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | | `--cpu-prof` | 启用 CPU 分析。退出时将分析文件写入磁盘。 | | `--cpu-prof-dir=` | CPU 分析文件的写入目录。默认为当前目录。会隐式启用 `--cpu-prof`。 | | `--cpu-prof-name=` | CPU 分析文件的文件名。默认为 `CPU...cpuprofile`。 | | `--cpu-prof-interval=` | 采样间隔,单位为微秒。默认值为 `1000`(1ms)。更低的值会提供更多细节,但文件也会更大。 | | `--cpu-prof-md` | 生成一个人类可读的 Markdown 报告,与 `.cpuprofile` 文件并排生成。 | | `--cpu-prof-flamegraph` | 生成一个交互式 SVG flamegraph,与 `.cpuprofile` 文件并排生成。 | :::note CPU 分析会报告转译后的 JavaScript 代码行号,而不是原始 TypeScript 源码。这是 V8 分析器的限制。对于 TypeScript 文件,报告中的行号可能不会与源代码直接对应。 ::: ## 自定义分析输出 默认情况下,分析文件会写入当前目录,并使用自动生成的文件名。你可以控制分析文件的保存位置和方式: ```sh # 将分析文件保存到特定目录 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 中分析 要分析 `.cpuprofile` 文件: 1. 打开 Chrome DevTools(F12) 2. 转到 **Performance** 选项卡 3. 点击 **Load profile** 按钮(向上箭头图标) 4. 选择你的 `.cpuprofile` 文件 DevTools 将显示 flame chart,以及应用中时间耗费位置的详细分解。 ## 示例:Markdown 报告 `--cpu-prof-md` 标志会生成一个易于阅读的 Markdown 摘要,无需将分析文件加载到 DevTools 中: ```sh deno run -A --cpu-prof --cpu-prof-md server.js ``` 这会同时创建一个 `.cpuprofile` 文件和一个 `.md` 文件,报告如下: ```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 `--cpu-prof-flamegraph` 标志会生成一个自包含、交互式的 SVG flamegraph,你可以直接在浏览器中打开,无需外部工具: ```sh 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` 一起使用: ```sh deno eval --cpu-prof --cpu-prof-flamegraph "for (let i = 0; i < 1e8; i++) {}" ``` ## 性能分析技巧 - **分析具有代表性的工作负载**:对于 HTTP 服务器,在停止之前向服务器发送真实流量——分析文件只会捕获程序运行期间发生的内容。 - **使用自身耗时与总耗时**:在分析报告中,_自身耗时_ 是函数自身代码中花费的时间,而 _总耗时_ 包括其调用的函数所花费的时间。较高的自身耗时指向真正的瓶颈;总耗时高但自身耗时低,则表示该函数把工作委托给了某个昂贵的部分。 - **前后对比**:使用描述性的 `--cpu-prof-name` 值保存分析文件(例如 `before-optimization.cpuprofile`),这样你就可以在修改后在 DevTools 中并排比较分析结果。 - **组合输出格式**:你可以将 `--cpu-prof-md` 和 `--cpu-prof-flamegraph` 结合使用,在一次运行中获得全部三种输出(`.cpuprofile`、`.md` 和 `.svg`): ```sh deno run --cpu-prof --cpu-prof-md --cpu-prof-flamegraph your_script.ts ``` - **过滤噪声**:短生命周期程序可能会显示启动开销(模块加载、JIT 编译)主导分析结果。为了获得更准确的结果,请确保你想分析的代码运行足够长,以收集有意义的样本。