-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
bench.ts
157 lines (132 loc) · 5.08 KB
/
bench.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import b from 'benny'
import format from 'benny/lib/internal/format'
import type { CaseResultWithDiff, Summary } from 'benny/lib/internal/common-types'
import kleur from 'kleur'
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { cpus } from 'node:os'
import { compileScript, compileTemplate, parse } from '@vue/compiler-sfc'
import { Compiler, FervidCompileOptions } from '../index'
// Increase libuv thread pool for a better async result.
// 4 threads is a default thread pool size.
const CPUS = cpus().length - 1
process.env.UV_THREADPOOL_SIZE = CPUS.toString()
const input = readFileSync(join(__dirname, '../../fervid/benches/fixtures/input.vue'), {
encoding: 'utf-8',
})
const options: FervidCompileOptions = {
filename: 'input.vue',
id: ''
}
async function run() {
const compiler = new Compiler()
await b.suite(
'compile sfc',
b.add('@vue/compiler-sfc', () => {
const descriptor = parse(input, { filename: 'input.vue' })
if (descriptor.descriptor.script || descriptor.descriptor.scriptSetup) {
compileScript(descriptor.descriptor, {
id: 'abc',
isProd: true,
inlineTemplate: true,
})
} else {
compileTemplate({
source: descriptor.descriptor.source,
filename: 'input.vue',
id: 'abc',
})
}
}),
b.add('@fervid/napi sync', () => {
compiler.compileSync(input, options)
}),
// The code below makes sure that async framework is not flawed.
// On my PC `sync promise` benches produce results close to a `sync` bench,
// which is expected, because `compileSync` is blocking.
// The `async` benches are properly multithreaded, thus they achieve much higher ops/sec.
// BEGIN
b.add('@fervid/napi sync promise (4 threads)', () => {
return Promise.allSettled(
Array.from({ length: 4 }, (_) => new Promise<void>((resolve) => (compiler.compileSync(input, options), resolve()))),
)
}),
b.add(`@fervid/napi sync promise (${CPUS} threads)`, () => {
return Promise.allSettled(
Array.from({ length: CPUS }, (_) => new Promise<void>((resolve) => (compiler.compileSync(input, options), resolve()))),
)
}),
// END
b.add('@fervid/napi async (4 threads)', () => {
return Promise.allSettled(Array.from({ length: 4 }, (_) => compiler.compileAsync(input, options)))
}),
b.add(`@fervid/napi async CPUS (${CPUS} threads)`, () => {
return Promise.allSettled(Array.from({ length: CPUS }, (_) => compiler.compileAsync(input, options)))
}),
// Custom cycle function to account for the async nature
// Copied from `benny` and adjusted
b.cycle((_, summary) => {
const allCompleted = summary.results.every((item) => item.samples > 0)
const fastestOps = format(summary.results[summary.fastest.index].ops)
const progress = Math.round(
(summary.results.filter((result) => result.samples !== 0).length / summary.results.length) * 100,
)
const progressInfo = `Progress: ${progress}%`
// Compensate for async
if (progress === 100) {
for (const result of summary.results) {
const match = result.name.match(/\((\d+) threads\)/)
if (!match || !match[1] || isNaN(+match[1])) continue
result.ops *= +match[1]
}
}
// Re-map fastest/slowest
const fastest = summary.results.reduce(
(prev, next, index) => {
return next.ops > prev.ops ? { ops: next.ops, index, name: next.name } : prev
},
{ ops: 0, index: 0, name: '' },
)
const slowest = summary.results.reduce(
(prev, next, index) => {
return next.ops < prev.ops ? { ops: next.ops, index, name: next.name } : prev
},
{ ops: Infinity, index: 0, name: '' },
)
summary.fastest = fastest
summary.slowest = slowest
summary.results.forEach((result, index) => {
result.percentSlower = index === fastest.index ? 0 : Number(((1 - result.ops / fastest.ops) * 100).toFixed(2))
})
const output = summary.results
.map((item, index) => {
const ops = format(item.ops)
const margin = item.margin.toFixed(2)
return item.samples
? kleur.cyan(`\n ${item.name}:\n`) +
` ${ops} ops/s, ±${margin}% ${allCompleted ? getStatus(item, index, summary, ops, fastestOps) : ''}`
: null
})
.filter((item) => item != null)
.join('\n')
return `${progressInfo}\n${output}`
}),
b.complete(),
)
}
run().catch((e) => {
console.error(e)
})
function getStatus(item: CaseResultWithDiff, index: number, summary: Summary, ops: string, fastestOps: string) {
const isFastest = index === summary.fastest.index
const isSlowest = index === summary.slowest.index
const statusShift = fastestOps.length - ops.length + 2
return (
' '.repeat(statusShift) +
(isFastest
? kleur.green('| fastest')
: isSlowest
? kleur.red(`| slowest, ${item.percentSlower}% slower`)
: kleur.yellow(`| ${item.percentSlower}% slower`))
)
}