JavaScript代码性能分析:寻找瓶颈
当您的Node.js项目开始变慢时,第一个念头可能是“得用Rust重写所有代码”。但别急!问题通常隐藏在几行效率低下的代码中。本文将讲解如何使用简单的性能分析工具来发现这些“瓶颈”。您将学习如何测量性能、构建火焰图以及了解CPU时间消耗在哪里。
为什么要进行代码性能分析?
假设您的服务器每秒处理100个请求,但需要处理500个。您可以尝试优化所有内容,但这需要数周时间。或者,您可以找到那个占用80%执行时间的函数,并在一个小时内修复它。性能分析正是用来显示时间消耗在哪里的工具。
以下是需要进行性能分析的主要场景:
- API响应延迟高
- 内存泄漏
- 无明显原因的CPU高负载
- 处理大型数据数组速度慢
工具1:Node.js内置分析器 (--prof)
最简单的方法是使用--prof标志运行Node.js。它会收集执行统计信息并保存到isolate-*.log文件中。
让我们创建一个包含瓶颈的测试脚本:
// slow.jsfunction heavyComputation() { let sum = 0; for (let i = 0; i < 1e7; i++) { sum += Math.sqrt(i) * Math.sin(i); } return sum;}
function fastFunction() { return 42;}
console.time('total');heavyComputation();fastFunction();console.timeEnd('total');运行性能分析:
node --prof slow.js执行后,会生成一个isolate-0x...-v8.log文件。要读取它,请使用node --prof-process工具:
node --prof-process isolate-0x...-v8.log > profile.txt打开profile.txt,您将看到一个包含时间百分比表格:
[Summary]: ticks total nonlib name 785 78.5% 85.3% JavaScript 215 21.5% 14.7% C++ ...[Bottom up (heavy) profile]: ticks parent name 500 50.0% LazyCompile: *heavyComputation slow.js:2:17 100 10.0% Builtin: MathSqrt 50 5.0% Builtin: MathSin我们可以看到,50%的时间花费在heavyComputation函数上。现在我们知道应该优先优化哪里了。
工具2:Chrome DevTools(检查器)
为了进行更直观的分析,我们可以连接Chrome DevTools。使用--inspect标志运行Node.js:
node --inspect slow.js打开Chrome并访问chrome://inspect。点击“Open dedicated DevTools for Node”。选择“Profiler”标签页 → “Start” → 运行脚本 → “Stop”。
您将看到一个火焰图,其中块的宽度表示执行时间。块越宽,函数越“热”。我们的heavyComputation将显示为一个巨大的矩形。
提示:在函数上使用“Focus selected”过滤器,以查看其内部调用。例如,您可能会注意到Math.sqrt被调用了1000万次——也许可以预先计算平方根。
工具3:perf_hooks——代码中的精确测量
有时您需要测量代码的特定部分,而无需启动外部工具。这时可以使用内置的perf_hooks模块。
测量函数执行时间的示例:
const { performance } = require('perf_hooks');
function processData(items) { let result = []; for (let item of items) { // 模拟复杂处理 for (let j = 0; j < 1000; j++) { result.push(item * j); }