Skip to content

性能基准测试:使用 hyperfinebenchmark.js 对比 Lodash

在开发一个工具函数库时,功能正确性只是基础,性能表现才是其能否在生产环境中替代 Lodash、Ramda 等成熟库的关键。

通过 性能基准测试(Benchmarking),我们可以:

  • 量化函数执行速度
  • 识别性能瓶颈
  • 与 Lodash 等库进行公平对比
  • 证明“轻量替代”的可行性

以下是使用 hyperfine(命令行)和 benchmark.js(JavaScript 内)进行基准测试的完整实践。

1. 为什么需要性能基准测试?

常见误区

  • “我的函数更短,所以更快”
  • “没有循环,所以性能更好”
  • “V8 优化了,不需要测”

真实场景需求

  • 高频调用函数(如 isString, pipe)的微小开销会累积成显著延迟。
  • 在数据量大的场景(如 chunk, deepGet),算法复杂度直接影响用户体验。

对比目标:Lodash

Lodash 是事实标准,但包含大量未使用的功能。我们的目标是:

  • 功能对等
  • 体积更小
  • 性能相当或更优

2. 工具选择:hyperfine vs benchmark.js

工具类型优点缺点适用场景
hyperfine命令行工具⚡ 启动快,进程隔离,结果稳定需编译为可执行文件CLI 脚本、Node.js 入口
benchmark.jsJS 库🧪 直接在代码中测试,可测内部函数受 Node.js 启动时间影响开发时快速验证、CI 集成

推荐组合

  • 开发时用 benchmark.js 快速迭代
  • 发布前用 hyperfine 做最终对比

3. 使用 benchmark.js 进行 JavaScript 内测试

安装

bash
npm install benchmark lodash

示例:对比 pipe 函数

ts
// benchmarks/pipe.benchmark.ts
import { Suite } from 'benchmark'
import _ from 'lodash'
import { pipe } from '../src/function/pipe'

// 测试函数
const add1 = (x: number) => x + 1
const mul2 = (x: number) => x * 2
const sub3 = (x: number) => x - 3

// 构造测试套件
const suite = new Suite('pipe performance')

// 测试自研 pipe
suite.add('custom pipe', () => {
  const fn = pipe(add1, mul2, sub3)
  fn(10)
})

// 测试 Lodash flow(等价于 pipe)
suite.add('lodash flow', () => {
  const fn = _.flow(add1, mul2, sub3)
  fn(10)
})

// 运行
suite
  .on('cycle', (event: any) => {
    console.log(String(event.target))
  })
  .on('complete', function (this: any) {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  .run({ async: false })

输出示例

text
custom pipe x 5,234,567 ops/sec ±1.23% (89 runs sampled)
lodash flow x 4,876,123 ops/sec ±0.98% (92 runs sampled)
Fastest is custom pipe

示例:对比 omit 函数

ts
// benchmarks/omit.benchmark.ts
import { Suite } from 'benchmark'
import _ from 'lodash'
import { omit } from '../src/object/omit'

const obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }

suite.add('custom omit', () => {
  omit(obj, ['b', 'd'])
})

suite.add('lodash omit', () => {
  _.omit(obj, ['b', 'd'])
})

// ... 运行逻辑同上

4. 使用 hyperfine 进行系统级基准测试

hyperfine 通过多次运行命令并统计时间,提供更接近真实环境的性能数据。

安装 hyperfine

bash
# macOS
brew install hyperfine

# Ubuntu/Debian
sudo apt install hyperfine

# Cargo
cargo install hyperfine

准备 CLI 测试脚本

ts
// cli/pipe-test.ts
import { pipe } from '../src/function/pipe'

const add1 = (x: number) => x + 1
const mul2 = (x: number) => x * 2
const sub3 = (x: number) => x - 3

// 高频调用模拟
for (let i = 0; i < 10000; i++) {
  pipe(add1, mul2, sub3)(i)
}

运行 hyperfine

text
# 编译并测试
hyperfine \
  'node cli/pipe-test.js' \
  'node cli/lodash-pipe-test.js'

# 输出
Benchmark 1: node cli/pipe-test.js
  Time (mean ± σ):      85.2 ms ±   3.1 ms    [User: 78.5 ms, System: 6.2 ms]
  Range (min … max):    80.1 ms …  95.3 ms    34 runs

Benchmark 2: node cli/lodash-pipe-test.js
  Time (mean ± σ):     102.4 ms ±   4.8 ms    [User: 95.1 ms, System: 7.0 ms]
  Range (min … max):    95.6 ms … 115.2 ms    29 runs

Summary
  'node cli/pipe-test.js' was 1.20x faster than 'node cli/lodash-pipe-test.js'

5. 优化建议:如何提升性能

通用优化策略

问题优化方案
多次 Object.keys()缓存结果
频繁 array.slice()使用索引迭代
深层嵌套对象访问避免 try/catch
类型检查开销typeof 优于 toString.call()(若安全)

示例:优化 deepGet

text
// 优化前:每次调用都 split
const deepGet = (obj, path) => {
  const paths = path.split('.')
  // ...
}

// 优化后:缓存 split 结果
const deepGet = (() => {
  const cache = new Map<string, string[]>()
  return (obj, path: string) => {
    let paths = cache.get(path)
    if (!paths) {
      paths = path.split('.')
      cache.set(path, paths)
    }
    // ...
  }
})()

6. CI/CD 集成:自动化性能监控

在 GitHub Actions 中集成基准测试:

yaml
# .GitHub/workflows/benchmark.yml
name: Benchmark
on: [push, pull_request]

jobs:
  benchmark:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm install
      - run: npm run build
      - run: npm install -g hyperfine
      - run: hyperfine 'node dist/pipe-test.js' 'node dist/lodash-pipe-test.js'

注意:CI 环境性能波动大,建议多次运行取平均值。

7. 性能报告:可视化对比

使用 benchmarks/report.js 生成 HTML 报告:

js
// 可结合 benchmark.js + chart.js 生成图表
// 或使用 hyperfine --export-json results.json

结语:性能是工具库的“硬通货”

功能完整只是入场券,性能卓越才是脱颖而出的关键。

通过 benchmark.jshyperfine,我们不仅能证明:

  • 我们的 pipe 比 Lodash flow 快 1.2x
  • omit 内存占用更少
  • debounce 响应更及时

更重要的是,我们建立了一套可量化、可重复、可验证的性能保障体系。

在微优化的时代,每一微秒的节省,都是对开发者体验的尊重

当你能自信地说:“我们的轻量库在性能上不输 Lodash”,你才真正拥有了替代它的资格。