Vitest 性能测试 #
性能测试概述 #
Vitest 内置了基准测试(Benchmark)功能,基于 Tinybench 实现,可以测量代码执行时间、比较不同实现的性能差异。
性能测试的作用 #
text
┌─────────────────────────────────────────────────────────────┐
│ 性能测试的作用 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 性能对比 │
│ 比较不同实现的执行效率 │
│ │
│ 2. 回归检测 │
│ 确保优化后性能没有下降 │
│ │
│ 3. 瓶颈识别 │
│ 找出性能瓶颈点 │
│ │
│ 4. 文档化 │
│ 为性能要求提供量化依据 │
│ │
└─────────────────────────────────────────────────────────────┘
基本用法 #
bench 函数 #
使用 bench 函数创建性能测试:
typescript
import { bench, describe } from 'vitest'
describe('sorting algorithms', () => {
bench('bubble sort', () => {
const arr = [5, 3, 8, 4, 2]
bubbleSort(arr)
})
bench('quick sort', () => {
const arr = [5, 3, 8, 4, 2]
quickSort(arr)
})
})
运行性能测试 #
bash
# 运行性能测试
vitest bench
# 运行特定文件
vitest bench sort.bench.ts
# Watch 模式
vitest bench --watch
输出示例 #
text
✓ sort.bench.ts (2) 1234ms
✓ bubble sort 1234.56 ns/iter 1000 iterations
✓ quick sort 456.78 ns/iter 1000 iterations
quick sort is 2.70x faster than bubble sort
性能测试结构 #
基本结构 #
typescript
import { bench, describe } from 'vitest'
describe('performance suite', () => {
bench('test name', () => {
// 要测试的代码
})
})
嵌套套件 #
typescript
import { bench, describe } from 'vitest'
describe('string operations', () => {
describe('concatenation', () => {
bench('using + operator', () => {
let result = ''
for (let i = 0; i < 100; i++) {
result += 'a'
}
})
bench('using array join', () => {
const arr = Array(100).fill('a')
arr.join('')
})
})
describe('search', () => {
bench('indexOf', () => {
'hello world'.indexOf('world')
})
bench('includes', () => {
'hello world'.includes('world')
})
})
})
测试选项 #
时间和迭代次数 #
typescript
import { bench, describe } from 'vitest'
describe('options', () => {
bench('with options', () => {
// 测试代码
}, {
time: 1000, // 运行时间(毫秒)
iterations: 100, // 最小迭代次数
warmupIterations: 5, // 预热迭代次数
warmupTime: 100, // 预热时间(毫秒)
})
})
配置默认选项 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
benchmark: {
include: ['**/*.bench.ts'],
exclude: ['node_modules'],
time: 1000,
iterations: 100,
},
},
})
准备和清理 #
setup 和 teardown #
typescript
import { bench, describe } from 'vitest'
describe('with setup', () => {
let data: number[]
// 每次迭代前执行
bench.beforeEach(() => {
data = Array(1000).fill(0).map(() => Math.random())
})
// 每次迭代后执行
bench.afterEach(() => {
data = []
})
bench('sort array', () => {
data.sort((a, b) => a - b)
})
})
全局 setup #
typescript
import { bench, describe, beforeAll, afterAll } from 'vitest'
describe('global setup', () => {
let database: Database
beforeAll(async () => {
database = await connectDatabase()
})
afterAll(async () => {
await database.disconnect()
})
bench('query', async () => {
await database.query('SELECT 1')
})
})
异步性能测试 #
测试异步代码 #
typescript
import { bench, describe } from 'vitest'
describe('async operations', () => {
bench('promise', async () => {
await Promise.resolve(42)
})
bench('fetch', async () => {
await fetch('/api/data')
})
bench('multiple promises', async () => {
await Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3),
])
})
})
性能对比示例 #
字符串操作对比 #
typescript
import { bench, describe } from 'vitest'
describe('string operations', () => {
const str = 'hello world, this is a test string'
bench('substring', () => {
str.substring(0, 10)
})
bench('slice', () => {
str.slice(0, 10)
})
bench('split + join', () => {
str.split(' ').join('-')
})
bench('replaceAll', () => {
str.replaceAll(' ', '-')
})
})
数组操作对比 #
typescript
import { bench, describe } from 'vitest'
describe('array operations', () => {
const arr = Array(1000).fill(0).map((_, i) => i)
bench('for loop', () => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
})
bench('for...of', () => {
let sum = 0
for (const num of arr) {
sum += num
}
})
bench('forEach', () => {
let sum = 0
arr.forEach(num => sum += num)
})
bench('reduce', () => {
arr.reduce((sum, num) => sum + num, 0)
})
})
对象操作对比 #
typescript
import { bench, describe } from 'vitest'
describe('object operations', () => {
const obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }
bench('Object.keys', () => {
Object.keys(obj)
})
bench('Object.values', () => {
Object.values(obj)
})
bench('Object.entries', () => {
Object.entries(obj)
})
bench('for...in', () => {
const keys: string[] = []
for (const key in obj) {
keys.push(key)
}
})
})
函数调用对比 #
typescript
import { bench, describe } from 'vitest'
describe('function calls', () => {
function regularFunction(x: number) {
return x * 2
}
const arrowFunction = (x: number) => x * 2
const obj = {
method(x: number) {
return x * 2
},
}
bench('regular function', () => {
regularFunction(42)
})
bench('arrow function', () => {
arrowFunction(42)
})
bench('method call', () => {
obj.method(42)
})
})
复杂数据结构测试 #
Map vs Object #
typescript
import { bench, describe } from 'vitest'
describe('Map vs Object', () => {
const obj: Record<string, number> = {}
const map = new Map<string, number>()
// 初始化数据
for (let i = 0; i < 1000; i++) {
obj[`key${i}`] = i
map.set(`key${i}`, i)
}
bench('Object get', () => {
obj['key500']
})
bench('Map get', () => {
map.get('key500')
})
bench('Object set', () => {
obj['newKey'] = 1
})
bench('Map set', () => {
map.set('newKey', 1)
})
bench('Object has', () => {
'key500' in obj
})
bench('Map has', () => {
map.has('key500')
})
})
Set vs Array #
typescript
import { bench, describe } from 'vitest'
describe('Set vs Array', () => {
const arr = Array(1000).fill(0).map((_, i) => i)
const set = new Set(arr)
bench('Array includes', () => {
arr.includes(500)
})
bench('Set has', () => {
set.has(500)
})
bench('Array push', () => {
arr.push(1001)
})
bench('Set add', () => {
set.add(1001)
})
})
算法性能测试 #
排序算法对比 #
typescript
import { bench, describe } from 'vitest'
function bubbleSort(arr: number[]) {
const result = [...arr]
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result.length - i - 1; j++) {
if (result[j] > result[j + 1]) {
[result[j], result[j + 1]] = [result[j + 1], result[j]]
}
}
}
return result
}
function quickSort(arr: number[]): number[] {
if (arr.length <= 1) return arr
const pivot = arr[0]
const left = arr.slice(1).filter(x => x < pivot)
const right = arr.slice(1).filter(x => x >= pivot)
return [...quickSort(left), pivot, ...quickSort(right)]
}
function nativeSort(arr: number[]) {
return [...arr].sort((a, b) => a - b)
}
describe('sorting algorithms', () => {
const smallArray = Array(10).fill(0).map(() => Math.random())
const mediumArray = Array(100).fill(0).map(() => Math.random())
const largeArray = Array(1000).fill(0).map(() => Math.random())
describe('small array (10 elements)', () => {
bench('bubble sort', () => bubbleSort(smallArray))
bench('quick sort', () => quickSort(smallArray))
bench('native sort', () => nativeSort(smallArray))
})
describe('medium array (100 elements)', () => {
bench('bubble sort', () => bubbleSort(mediumArray))
bench('quick sort', () => quickSort(mediumArray))
bench('native sort', () => nativeSort(mediumArray))
})
describe('large array (1000 elements)', () => {
bench('quick sort', () => quickSort(largeArray))
bench('native sort', () => nativeSort(largeArray))
// bubble sort 太慢,跳过
})
})
查找算法对比 #
typescript
import { bench, describe } from 'vitest'
function linearSearch(arr: number[], target: number): number {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) return i
}
return -1
}
function binarySearch(arr: number[], target: number): number {
let left = 0
let right = arr.length - 1
while (left <= right) {
const mid = Math.floor((left + right) / 2)
if (arr[mid] === target) return mid
if (arr[mid] < target) left = mid + 1
else right = mid - 1
}
return -1
}
describe('search algorithms', () => {
const arr = Array(10000).fill(0).map((_, i) => i)
const target = 5000
bench('linear search', () => {
linearSearch(arr, target)
})
bench('binary search', () => {
binarySearch(arr, target)
})
bench('indexOf', () => {
arr.indexOf(target)
})
})
性能测试最佳实践 #
1. 使用真实数据 #
typescript
import { bench, describe } from 'vitest'
// 好的做法:使用真实大小的数据
describe('realistic data', () => {
const realisticData = loadRealData() // 加载真实数据
bench('process data', () => {
processData(realisticData)
})
})
// 不好的做法:使用过小的数据
describe('unrealistic data', () => {
const smallData = [1, 2, 3] // 太小,无法反映真实性能
bench('process data', () => {
processData(smallData)
})
})
2. 避免优化干扰 #
typescript
import { bench, describe } from 'vitest'
// 好的做法:使用结果,防止被优化掉
describe('avoid optimization', () => {
bench('calculate', () => {
let result = 0
for (let i = 0; i < 1000; i++) {
result += i
}
// 使用结果
return result
})
})
3. 预热测试 #
typescript
import { bench, describe } from 'vitest'
describe('with warmup', () => {
bench('cached operation', () => {
// 第一次执行可能较慢(缓存未命中)
// 预热后测量更准确
expensiveOperation()
}, {
warmupIterations: 10,
warmupTime: 100,
})
})
4. 多次运行取平均 #
typescript
import { bench, describe } from 'vitest'
describe('multiple runs', () => {
bench('stable measurement', () => {
// Vitest 自动多次运行
operation()
}, {
time: 1000, // 运行 1 秒
iterations: 100, // 至少 100 次
})
})
5. 隔离测试环境 #
typescript
import { bench, describe } from 'vitest'
describe('isolated environment', () => {
let data: number[]
bench.beforeEach(() => {
// 每次迭代前重置数据
data = generateData()
})
bench('isolated test', () => {
processData(data)
})
})
性能测试报告 #
输出解读 #
text
✓ bench.bench.ts (2) 1234ms
✓ operation A 123.45 ns/iter 10000 iterations
✓ operation B 67.89 ns/iter 15000 iterations
operation B is 1.82x faster than operation A
| 指标 | 说明 |
|---|---|
| ns/iter | 每次迭代的平均时间(纳秒) |
| iterations | 总迭代次数 |
| x faster | 性能倍数对比 |
JSON 报告 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
benchmark: {
reporters: ['default', 'json'],
},
},
})
性能测试速查表 #
| 函数 | 说明 |
|---|---|
bench(name, fn) |
创建性能测试 |
bench.beforeEach(fn) |
每次迭代前执行 |
bench.afterEach(fn) |
每次迭代后执行 |
describe(name, fn) |
创建测试套件 |
| 选项 | 说明 |
|---|---|
time |
运行时间(毫秒) |
iterations |
最小迭代次数 |
warmupTime |
预热时间 |
warmupIterations |
预热迭代次数 |
下一步 #
现在你已经掌握了性能测试,接下来学习 高级特性 了解 Vitest 的高级功能!
最后更新:2026-03-28