Skip to content

Commit 90fab1e

Browse files
committed
feat(benchmark): add fast-bench script
1 parent 7a907d5 commit 90fab1e

File tree

4 files changed

+286
-27
lines changed

4 files changed

+286
-27
lines changed

Diff for: packages-private/benchmark/fast-bench.js

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
const DEV = !!process.env.VSCODE_INSPECTOR_OPTIONS
2+
const repeat = arr => arr.flatMap(v => [v, v, v])
3+
4+
await polyfill()
5+
await build()
6+
await bench()
7+
8+
async function polyfill() {
9+
const { JSDOM } = await import('jsdom')
10+
11+
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>')
12+
globalThis.document = dom.window.document
13+
globalThis.Node = dom.window.Node
14+
}
15+
16+
async function build() {
17+
const { compile } = await import(
18+
'../../packages/compiler-vapor/dist/compiler-vapor.esm-browser.js'
19+
)
20+
21+
const code = compile(
22+
`
23+
<table class="table table-hover table-striped test-data">
24+
<tbody>
25+
<tr
26+
v-for="row of rows"
27+
:key="row.id"
28+
:class="selected === row.id ? 'danger' : ''"
29+
>
30+
<td class="col-md-1">{{ row.id }}</td>
31+
<td class="col-md-4">
32+
<a @click="select(row.id)">{{ row.label.value }}</a>
33+
</td>
34+
<td class="col-md-1">
35+
<a @click="remove(row.id)">
36+
<span class="glyphicon glyphicon-remove" aria-hidden="true">x</span>
37+
</a>
38+
</td>
39+
<td class="col-md-6"></td>
40+
</tr>
41+
</tbody>
42+
</table>
43+
`,
44+
{
45+
mode: 'module',
46+
prefixIdentifiers: true,
47+
},
48+
)
49+
.code.replace(
50+
` from 'vue'`,
51+
` from '../../../packages/vue/dist/vue.runtime-with-vapor.esm-browser${!DEV ? '.prod' : ''}.js'`,
52+
)
53+
.replaceAll(`_delegateEvents(`, `// _delegateEvents(`)
54+
55+
const { writeFileSync, existsSync, mkdirSync } = await import('node:fs')
56+
const { dirname, resolve } = await import('node:path')
57+
const { fileURLToPath } = await import('node:url')
58+
59+
const __filename = fileURLToPath(import.meta.url)
60+
const __dirname = dirname(__filename)
61+
const outPath = resolve(__dirname, 'dist', 'component.js')
62+
63+
if (!existsSync(dirname(outPath))) {
64+
mkdirSync(dirname(outPath))
65+
}
66+
writeFileSync(outPath, code)
67+
}
68+
69+
async function bench() {
70+
let ID = 1
71+
72+
const { render } = await import('./dist/component.js')
73+
74+
const {
75+
shallowRef,
76+
reactive,
77+
VaporComponentInstance,
78+
simpleSetCurrentInstance,
79+
nextTick,
80+
} = await import(
81+
`../../packages/vue/dist/vue.runtime-with-vapor.esm-browser${!DEV ? '.prod' : ''}.js`
82+
)
83+
84+
const ctx = reactive({
85+
selected: null,
86+
rows: [],
87+
})
88+
89+
simpleSetCurrentInstance(new VaporComponentInstance({}, {}, null))
90+
render(ctx)
91+
92+
const { run, bench, boxplot } = await import('mitata')
93+
94+
boxplot(() => {
95+
for (const shouldRender of [false, true]) {
96+
const subfix = shouldRender ? ' (render)' : ''
97+
98+
// bench('append: $n' + subfix, async function* (state) {
99+
// const n = state.get('n')
100+
// ctx.rows = []
101+
// await nextTick()
102+
// yield () => {
103+
// ctx.rows = ctx.rows.concat(buildData(n))
104+
// if (shouldRender) {
105+
// await nextTick()
106+
// }
107+
// }
108+
// }).args('n', [1])
109+
110+
bench('select: $n' + subfix, async function* (state) {
111+
const n = state.get('n')
112+
ctx.rows = buildData(n)
113+
await nextTick()
114+
const ids = ctx.rows.map(d => d.id)
115+
let i = 0
116+
yield shouldRender
117+
? async () => {
118+
ctx.selected = ids[i++ % ids.length]
119+
await nextTick()
120+
}
121+
: () => {
122+
ctx.selected = ids[i++ % ids.length]
123+
}
124+
}).args('n', repeat([100]))
125+
126+
bench('full-update: $n' + subfix, async function* (state) {
127+
const n = state.get('n')
128+
ctx.rows = buildData(n)
129+
await nextTick()
130+
yield shouldRender
131+
? async () => {
132+
for (const row of ctx.rows) {
133+
row.label += ' !!!'
134+
}
135+
await nextTick()
136+
}
137+
: () => {
138+
for (const row of ctx.rows) {
139+
row.label += ' !!!'
140+
}
141+
}
142+
}).args('n', repeat([100]))
143+
144+
bench('partial-update: $n' + subfix, async function* (state) {
145+
const n = state.get('n')
146+
ctx.rows = buildData(n)
147+
await nextTick()
148+
const toUpdate = []
149+
for (let i = 0; i < ctx.rows.length; i += 10) {
150+
toUpdate.push(ctx.rows[i])
151+
}
152+
yield shouldRender
153+
? async () => {
154+
for (const row of toUpdate) {
155+
row.label += ' !!!'
156+
}
157+
await nextTick()
158+
}
159+
: () => {
160+
for (const row of toUpdate) {
161+
row.label += ' !!!'
162+
}
163+
}
164+
}).args('n', repeat([1000]))
165+
}
166+
})
167+
168+
run({ format: 'markdown' })
169+
170+
function _random(max) {
171+
return Math.round(Math.random() * 1000) % max
172+
}
173+
174+
function buildData(count = 1000) {
175+
const adjectives = [
176+
'pretty',
177+
'large',
178+
'big',
179+
'small',
180+
'tall',
181+
'short',
182+
'long',
183+
'handsome',
184+
'plain',
185+
'quaint',
186+
'clean',
187+
'elegant',
188+
'easy',
189+
'angry',
190+
'crazy',
191+
'helpful',
192+
'mushy',
193+
'odd',
194+
'unsightly',
195+
'adorable',
196+
'important',
197+
'inexpensive',
198+
'cheap',
199+
'expensive',
200+
'fancy',
201+
]
202+
const colours = [
203+
'red',
204+
'yellow',
205+
'blue',
206+
'green',
207+
'pink',
208+
'brown',
209+
'purple',
210+
'brown',
211+
'white',
212+
'black',
213+
'orange',
214+
]
215+
const nouns = [
216+
'table',
217+
'chair',
218+
'house',
219+
'bbq',
220+
'desk',
221+
'car',
222+
'pony',
223+
'cookie',
224+
'sandwich',
225+
'burger',
226+
'pizza',
227+
'mouse',
228+
'keyboard',
229+
]
230+
const data = []
231+
for (let i = 0; i < count; i++)
232+
data.push({
233+
id: ID++,
234+
label: shallowRef(
235+
adjectives[_random(adjectives.length)] +
236+
' ' +
237+
colours[_random(colours.length)] +
238+
' ' +
239+
nouns[_random(nouns.length)],
240+
),
241+
})
242+
return data
243+
}
244+
}

Diff for: packages-private/benchmark/package.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"type": "module",
77
"scripts": {
88
"dev": "pnpm start --noMinify --skipBench --vdom",
9-
"start": "node index.js"
9+
"start": "node index.js",
10+
"prefast-bench": "cd ../.. && npm run build",
11+
"fast-bench": "node fast-bench.js"
1012
},
1113
"dependencies": {
1214
"@vitejs/plugin-vue": "catalog:",
@@ -15,6 +17,9 @@
1517
"vite": "catalog:"
1618
},
1719
"devDependencies": {
18-
"@types/connect": "^3.4.38"
20+
"@types/connect": "^3.4.38",
21+
"@types/jsdom": "latest",
22+
"jsdom": "latest",
23+
"mitata": "latest"
1924
}
2025
}

Diff for: packages/runtime-vapor/src/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ export type { VaporDirective } from './directives/custom'
77
// compiler-use only
88
export { insert, prepend, remove, isFragment, VaporFragment } from './block'
99
export { setInsertionState } from './insertionState'
10-
export { createComponent, createComponentWithFallback } from './component'
10+
export {
11+
createComponent,
12+
createComponentWithFallback,
13+
VaporComponentInstance,
14+
} from './component'
1115
export { renderEffect } from './renderEffect'
1216
export { createSlot } from './componentSlots'
1317
export { template } from './dom/template'
@@ -42,3 +46,5 @@ export {
4246
applyDynamicModel,
4347
} from './directives/vModel'
4448
export { withVaporDirectives } from './directives/custom'
49+
50+
export { simpleSetCurrentInstance } from '@vue/runtime-core'

0 commit comments

Comments
 (0)