You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
5.0 KiB
TypeScript
161 lines
5.0 KiB
TypeScript
4 years ago
|
import 'core-js/stable/array/fill.js'
|
||
5 years ago
|
import faker from 'faker'
|
||
5 years ago
|
import { VNode } from '../../package/vnode'
|
||
5 years ago
|
import { h } from '../../package/h'
|
||
5 years ago
|
import { init as curInit } from '../../package/init'
|
||
5 years ago
|
import { init as refInit } from 'latest-snabbdom-release/init'
|
||
5 years ago
|
import { assert } from 'chai'
|
||
|
import pReduce from 'p-reduce'
|
||
|
import pMapSeries from 'p-map-series'
|
||
|
import { std, mean } from 'mathjs'
|
||
|
|
||
|
const RUNS = 5
|
||
|
const PATCHES_PER_RUN = 100
|
||
|
const WARM_UP_RUNS = 1
|
||
|
const REQUEST_ANIMATION_FRAME_EVERY_N_PATCHES = 1
|
||
|
const BENCHMARK_TIMEOUT_MINUTES = 10
|
||
|
const REQUIRED_PRECISION = 0.02
|
||
5 years ago
|
|
||
5 years ago
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||
5 years ago
|
declare global {
|
||
5 years ago
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
5 years ago
|
const __karma__: {
|
||
|
info(info: unknown): void
|
||
5 years ago
|
}
|
||
5 years ago
|
}
|
||
5 years ago
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||
5 years ago
|
|
||
5 years ago
|
const ALLOWED_REGRESSION = 0.03
|
||
5 years ago
|
describe('core benchmark', () => {
|
||
|
it('does not regress', async function Benchmark () {
|
||
5 years ago
|
this.timeout(BENCHMARK_TIMEOUT_MINUTES * 1000 * 60)
|
||
5 years ago
|
|
||
5 years ago
|
faker.seed(0)
|
||
5 years ago
|
const inputs = Array(PATCHES_PER_RUN).fill(null).map(() => {
|
||
|
return new Array(faker.random.number(20))
|
||
|
.fill(null)
|
||
|
.map(() => ({
|
||
|
name: faker.company.companyName(),
|
||
|
catchPhrase: faker.company.catchPhrase(),
|
||
|
suffix: faker.company.companySuffix(),
|
||
|
products: Array(faker.random.number(3))
|
||
|
.fill(null)
|
||
|
.map(() => ({
|
||
|
name: faker.commerce.productName(),
|
||
|
color: faker.commerce.color(),
|
||
|
price: faker.commerce.price() + faker.finance.currencySymbol(),
|
||
|
})),
|
||
|
founded: faker.date.past()
|
||
5 years ago
|
}))
|
||
|
})
|
||
5 years ago
|
|
||
5 years ago
|
type Input = (typeof inputs)[0]
|
||
5 years ago
|
|
||
|
const view = (companies: Input): VNode => h('table', [
|
||
|
h('caption', ['Companies']),
|
||
|
h('thead', [
|
||
|
h('tr', [
|
||
|
'Details',
|
||
|
'Products',
|
||
|
].map((th) => h('th', [th])))
|
||
|
]),
|
||
|
h('tbody', companies.map(function companyView (company) {
|
||
|
return h('tr', [
|
||
|
h('td', [
|
||
|
h('div', [
|
||
|
h('b', [company.name]),
|
||
|
company.suffix && `\xa0${company.suffix}`
|
||
|
]),
|
||
|
h('div', h('i', [company.catchPhrase])),
|
||
|
h('td', [
|
||
|
h('dt', ['Founded']),
|
||
|
h('dd', [company.founded.toLocaleDateString()])
|
||
|
])
|
||
|
]),
|
||
|
h('td', [h('ul', company.products.map(function productView (product) {
|
||
|
return h('li', [h('dl', [
|
||
|
h('dt', ['Name']),
|
||
|
h('dd', [product.name]),
|
||
|
h('dt', ['Color']),
|
||
|
h('dd', [product.color]),
|
||
|
h('dt', ['Price']),
|
||
|
h('dd', [product.price]),
|
||
5 years ago
|
])])
|
||
5 years ago
|
}))])
|
||
5 years ago
|
])
|
||
5 years ago
|
}))
|
||
5 years ago
|
])
|
||
5 years ago
|
|
||
5 years ago
|
type Patcher = ReturnType<typeof refInit | typeof curInit>
|
||
5 years ago
|
|
||
|
interface SingleRunResult {
|
||
|
i: number
|
||
|
cur: number
|
||
|
ref: number
|
||
|
}
|
||
|
|
||
|
const subjectToResult = async (subject: Patcher, subjectId: string): Promise<number> => {
|
||
|
await new Promise((resolve) => {
|
||
5 years ago
|
requestAnimationFrame(resolve)
|
||
|
})
|
||
|
const markName = `mark:${subjectId}`
|
||
|
const measureName = `measure:${subjectId}`
|
||
|
performance.mark(markName)
|
||
5 years ago
|
const lastVnode = await pReduce(
|
||
|
inputs,
|
||
|
async function subjectToResultReducer (acc: HTMLElement | VNode, input, i) {
|
||
5 years ago
|
const vnode = view(input)
|
||
|
subject(acc, vnode)
|
||
5 years ago
|
if (i % REQUEST_ANIMATION_FRAME_EVERY_N_PATCHES === 0) {
|
||
|
await new Promise((resolve) => {
|
||
5 years ago
|
requestAnimationFrame(resolve)
|
||
|
})
|
||
5 years ago
|
}
|
||
5 years ago
|
return vnode
|
||
5 years ago
|
},
|
||
|
document.body.appendChild(document.createElement('section')),
|
||
5 years ago
|
)
|
||
|
performance.measure(measureName, markName)
|
||
|
if (!('elm' in lastVnode)) throw new Error()
|
||
|
if (!lastVnode.elm) throw new Error()
|
||
|
document.body.removeChild(lastVnode.elm)
|
||
|
const measure = performance.getEntriesByName(measureName)[0]
|
||
|
performance.clearMarks(markName)
|
||
|
performance.clearMeasures(measureName)
|
||
|
return measure.duration
|
||
|
}
|
||
5 years ago
|
|
||
|
const singleRun = async (_: null, runI: number): Promise<SingleRunResult> => {
|
||
5 years ago
|
const cur = await subjectToResult(curInit([]), `cur:${runI}`)
|
||
|
const ref = await subjectToResult(refInit([]), `ref:${runI}`)
|
||
5 years ago
|
|
||
5 years ago
|
return { i: runI, cur, ref }
|
||
|
}
|
||
5 years ago
|
|
||
|
const runResults = (await pMapSeries(Array(RUNS + WARM_UP_RUNS).fill(null), singleRun))
|
||
5 years ago
|
.slice(WARM_UP_RUNS)
|
||
5 years ago
|
|
||
5 years ago
|
__karma__.info({ benchmark: runResults })
|
||
5 years ago
|
|
||
|
const results = {
|
||
|
ref: runResults.map((result) => result.ref),
|
||
|
cur: runResults.map((result) => result.cur),
|
||
5 years ago
|
}
|
||
5 years ago
|
const means = {
|
||
|
ref: mean(results.ref),
|
||
|
cur: mean(results.cur),
|
||
5 years ago
|
}
|
||
5 years ago
|
const stds = {
|
||
|
ref: std(results.ref, 'uncorrected'),
|
||
|
cur: std(results.cur, 'uncorrected'),
|
||
|
}
|
||
|
|
||
|
;(['ref', 'cur'] as const).forEach((subject) => {
|
||
5 years ago
|
const stdRatio = stds[subject] / means[subject]
|
||
|
assert.isAtMost(stdRatio, REQUIRED_PRECISION, `${subject} not precise enough`)
|
||
|
})
|
||
5 years ago
|
|
||
5 years ago
|
assert.isAtMost(means.cur, means.ref * (1 + ALLOWED_REGRESSION))
|
||
|
})
|
||
|
})
|