mirror of https://github.com/vuejs/core.git
feat(playground): add benchmark
This commit is contained in:
parent
95b08e8fc6
commit
670109e287
|
@ -0,0 +1,116 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { buildData } from './data'
|
||||
import { defer, wrap } from './profiling'
|
||||
|
||||
const selected = ref<number>()
|
||||
const rows = shallowRef<
|
||||
{
|
||||
id: number
|
||||
label: string
|
||||
}[]
|
||||
>([])
|
||||
|
||||
function setRows(update = rows.value.slice()) {
|
||||
rows.value = update
|
||||
}
|
||||
|
||||
const add = wrap('add', () => {
|
||||
rows.value = rows.value.concat(buildData(1000))
|
||||
})
|
||||
|
||||
const remove = wrap('remove', (id: number) => {
|
||||
rows.value.splice(
|
||||
rows.value.findIndex(d => d.id === id),
|
||||
1,
|
||||
)
|
||||
setRows()
|
||||
})
|
||||
|
||||
const select = wrap('select', (id: number) => {
|
||||
selected.value = id
|
||||
})
|
||||
|
||||
const run = wrap('run', () => {
|
||||
setRows(buildData())
|
||||
selected.value = undefined
|
||||
})
|
||||
|
||||
const update = wrap('update', () => {
|
||||
const _rows = rows.value
|
||||
for (let i = 0; i < _rows.length; i += 10) {
|
||||
_rows[i].label += ' !!!'
|
||||
}
|
||||
setRows()
|
||||
})
|
||||
|
||||
const runLots = wrap('runLots', () => {
|
||||
setRows(buildData(10000))
|
||||
selected.value = undefined
|
||||
})
|
||||
|
||||
const clear = wrap('clear', () => {
|
||||
setRows([])
|
||||
selected.value = undefined
|
||||
})
|
||||
|
||||
const swapRows = wrap('swap', () => {
|
||||
const _rows = rows.value
|
||||
if (_rows.length > 998) {
|
||||
const d1 = _rows[1]
|
||||
const d998 = _rows[998]
|
||||
_rows[1] = d998
|
||||
_rows[998] = d1
|
||||
setRows()
|
||||
}
|
||||
})
|
||||
|
||||
async function bench() {
|
||||
for (let i = 0; i < 30; i++) {
|
||||
rows.value = []
|
||||
await defer()
|
||||
await runLots()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>Vue.js Vapor Benchmark</h1>
|
||||
<div
|
||||
id="control"
|
||||
style="display: flex; flex-direction: column; width: fit-content; gap: 6px"
|
||||
>
|
||||
<button @click="bench">Benchmark mounting</button>
|
||||
<button id="run" @click="run">Create 1,000 rows</button>
|
||||
<button id="runlots" @click="runLots">Create 10,000 rows</button>
|
||||
<button id="add" @click="add">Append 1,000 rows</button>
|
||||
<button id="update" @click="update">Update every 10th row</button>
|
||||
<button id="clear" @click="clear">Clear</button>
|
||||
<button id="swaprows" @click="swapRows">Swap Rows</button>
|
||||
</div>
|
||||
<div id="time"></div>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="row of rows"
|
||||
:key="row.id"
|
||||
:class="{ danger: row.id === selected }"
|
||||
>
|
||||
<td>{{ row.id }}</td>
|
||||
<td>
|
||||
<a @click="select(row.id)">{{ row.label }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<button @click="remove(row.id)">x</button>
|
||||
</td>
|
||||
<td class="col-md-6"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.danger {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
let ID = 1
|
||||
|
||||
function _random(max: number) {
|
||||
return Math.round(Math.random() * 1000) % max
|
||||
}
|
||||
|
||||
export function buildData(count = 1000) {
|
||||
const adjectives = [
|
||||
'pretty',
|
||||
'large',
|
||||
'big',
|
||||
'small',
|
||||
'tall',
|
||||
'short',
|
||||
'long',
|
||||
'handsome',
|
||||
'plain',
|
||||
'quaint',
|
||||
'clean',
|
||||
'elegant',
|
||||
'easy',
|
||||
'angry',
|
||||
'crazy',
|
||||
'helpful',
|
||||
'mushy',
|
||||
'odd',
|
||||
'unsightly',
|
||||
'adorable',
|
||||
'important',
|
||||
'inexpensive',
|
||||
'cheap',
|
||||
'expensive',
|
||||
'fancy',
|
||||
]
|
||||
const colours = [
|
||||
'red',
|
||||
'yellow',
|
||||
'blue',
|
||||
'green',
|
||||
'pink',
|
||||
'brown',
|
||||
'purple',
|
||||
'brown',
|
||||
'white',
|
||||
'black',
|
||||
'orange',
|
||||
]
|
||||
const nouns = [
|
||||
'table',
|
||||
'chair',
|
||||
'house',
|
||||
'bbq',
|
||||
'desk',
|
||||
'car',
|
||||
'pony',
|
||||
'cookie',
|
||||
'sandwich',
|
||||
'burger',
|
||||
'pizza',
|
||||
'mouse',
|
||||
'keyboard',
|
||||
]
|
||||
const data = []
|
||||
for (let i = 0; i < count; i++)
|
||||
data.push({
|
||||
id: ID++,
|
||||
label:
|
||||
adjectives[_random(adjectives.length)] +
|
||||
' ' +
|
||||
colours[_random(colours.length)] +
|
||||
' ' +
|
||||
nouns[_random(nouns.length)],
|
||||
})
|
||||
return data
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// @ts-expect-error
|
||||
globalThis.doProfile = false
|
||||
// const defer = nextTick
|
||||
const ric =
|
||||
typeof requestIdleCallback === 'undefined' ? setTimeout : requestIdleCallback
|
||||
export const defer = () => new Promise(r => ric(r))
|
||||
|
||||
const times: Record<string, number[]> = {}
|
||||
|
||||
export const wrap = (
|
||||
id: string,
|
||||
fn: (...args: any[]) => any,
|
||||
): ((...args: any[]) => Promise<void>) => {
|
||||
if (import.meta.env.PROD) return fn
|
||||
return async (...args) => {
|
||||
const btns = Array.from(
|
||||
document.querySelectorAll<HTMLButtonElement>('#control button'),
|
||||
)
|
||||
for (const node of btns) {
|
||||
node.disabled = true
|
||||
}
|
||||
const doProfile = (globalThis as any).doProfile
|
||||
await defer()
|
||||
|
||||
doProfile && console.profile(id)
|
||||
const start = performance.now()
|
||||
fn(...args)
|
||||
await defer()
|
||||
const time = performance.now() - start
|
||||
doProfile && console.profileEnd(id)
|
||||
const prevTimes = times[id] || (times[id] = [])
|
||||
prevTimes.push(time)
|
||||
const median = prevTimes.slice().sort((a, b) => a - b)[
|
||||
Math.floor(prevTimes.length / 2)
|
||||
]
|
||||
const mean = prevTimes.reduce((a, b) => a + b, 0) / prevTimes.length
|
||||
const msg =
|
||||
`${id}: min: ${Math.min(...prevTimes).toFixed(2)} / ` +
|
||||
`max: ${Math.max(...prevTimes).toFixed(2)} / ` +
|
||||
`median: ${median.toFixed(2)}ms / ` +
|
||||
`mean: ${mean.toFixed(2)}ms / ` +
|
||||
`time: ${time.toFixed(2)}ms / ` +
|
||||
`std: ${getStandardDeviation(prevTimes).toFixed(2)} ` +
|
||||
`: ${getStandardDeviation(prevTimes).toFixed(2)} ` +
|
||||
`over ${prevTimes.length} runs`
|
||||
console.log(msg)
|
||||
document.getElementById('time')!.textContent = msg
|
||||
|
||||
for (const node of btns) {
|
||||
node.disabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getStandardDeviation(array: number[]) {
|
||||
const n = array.length
|
||||
const mean = array.reduce((a, b) => a + b) / n
|
||||
return Math.sqrt(
|
||||
array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n,
|
||||
)
|
||||
}
|
|
@ -2,7 +2,7 @@ import { render, unmountComponent } from 'vue/vapor'
|
|||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
|
||||
const modules = import.meta.glob<any>('./*.(vue|js)')
|
||||
const modules = import.meta.glob<any>('./**/*.(vue|js)')
|
||||
const mod = (modules['.' + location.pathname] || modules['./App.vue'])()
|
||||
|
||||
mod.then(({ default: mod }) => {
|
||||
|
|
Loading…
Reference in New Issue