Files
Imhotep/packages/imhotep-bench/src/trace-bench.mjs
T

170 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { performance } from 'node:perf_hooks'
import {
evaluateLogic,
registerDefaultPredicates,
} from 'imhotep-solver'
import { adaptCanonicalWorldToSolver } from 'imhotep-core'
registerDefaultPredicates()
function buildWorld(n) {
const canonical = {
sceneId: 's',
snapshotId: 'sn',
env: {
viewportWidth: 1280,
viewportHeight: 800,
deviceScaleFactor: 1,
colorScheme: 'light',
pointer: 'fine',
hover: false,
reducedMotion: false,
locale: 'en',
writingMode: 'horizontal-tb',
},
strings: { values: [] },
subjects: {
ids: new Uint32Array(Array.from({length: n}, (_, i) => i)),
domNodeId: new Uint32Array(Array.from({length: n}, (_, i) => i + 10)),
subjectKind: new Uint8Array(Array.from({length: n}, () => 1)),
primaryBoxId: new Uint32Array(Array.from({length: n}, (_, i) => i)),
firstFragmentId: new Uint32Array(Array.from({length: n}, () => 0)),
fragmentCount: new Uint8Array(Array.from({length: n}, () => 1)),
},
boxes: {
boxId: new Uint32Array(Array.from({length: n}, (_, i) => i)),
subjectId: new Uint32Array(Array.from({length: n}, (_, i) => i)),
frameId: new Uint32Array(Array.from({length: n}, () => 0)),
borderLeft: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60)),
borderTop: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60)),
borderRight: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60 + 50)),
borderBottom: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60 + 50)),
paddingLeft: new Float64Array(Array.from({length: n}, () => 0)),
paddingTop: new Float64Array(Array.from({length: n}, () => 0)),
paddingRight: new Float64Array(Array.from({length: n}, () => 0)),
paddingBottom: new Float64Array(Array.from({length: n}, () => 0)),
contentLeft: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60)),
contentTop: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60)),
contentRight: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60 + 50)),
contentBottom: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60 + 50)),
},
visualBoxes: {
boxId: new Uint32Array(Array.from({length: n}, (_, i) => i)),
subjectId: new Uint32Array(Array.from({length: n}, (_, i) => i)),
frameId: new Uint32Array(Array.from({length: n}, () => 0)),
borderLeft: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60)),
borderTop: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60)),
borderRight: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60 + 50)),
borderBottom: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60 + 50)),
paddingLeft: new Float64Array(Array.from({length: n}, () => 0)),
paddingTop: new Float64Array(Array.from({length: n}, () => 0)),
paddingRight: new Float64Array(Array.from({length: n}, () => 0)),
paddingBottom: new Float64Array(Array.from({length: n}, () => 0)),
contentLeft: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60)),
contentTop: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60)),
contentRight: new Float64Array(Array.from({length: n}, (_, i) => (i % 10) * 60 + 50)),
contentBottom: new Float64Array(Array.from({length: n}, (_, i) => Math.floor(i / 10) * 60 + 50)),
},
dom: {
nodeId: new Uint32Array(Array.from({length: n}, (_, i) => i + 10)),
parentNodeId: new Uint32Array(Array.from({length: n}, () => 1)),
childCount: new Uint8Array(Array.from({length: n}, () => 0)),
tagNameStringId: new Uint16Array(Array.from({length: n}, () => 0)),
},
transforms: { transformId: new Uint32Array(0), subjectId: new Uint32Array(0), matrixStart: new Uint32Array(0), matrixLength: new Uint32Array(0), originX: new Float64Array(0), originY: new Float64Array(0) },
matrices: { values: new Float64Array(0) },
rects: { rectId: new Uint32Array(0), left: new Float64Array(0), top: new Float64Array(0), right: new Float64Array(0), bottom: new Float64Array(0) },
topology: {
containingBlockOf: new Uint32Array(Array.from({length: n}, () => 0)),
nearestPositionedAncestorOf: new Uint32Array(Array.from({length: n}, () => 0)),
scrollContainerOf: new Uint32Array(Array.from({length: n}, () => 0)),
stackingContextOf: new Uint32Array(Array.from({length: n}, () => 0)),
formattingContextOf: new Uint32Array(Array.from({length: n}, () => 0)),
clippingRootOf: new Uint32Array(Array.from({length: n}, () => 0)),
paintOrderBucket: new Uint8Array(Array.from({length: n}, () => 0)),
paintOrderIndex: new Uint32Array(Array.from({length: n}, (_, i) => i)),
},
scroll: { containerId: new Uint32Array(0), scrollLeft: new Float64Array(0), scrollTop: new Float64Array(0), scrollWidth: new Float64Array(0), scrollHeight: new Float64Array(0), clientWidth: new Float64Array(0), clientHeight: new Float64Array(0) },
clipping: { clipNodeId: new Uint32Array(0), subjectId: new Uint32Array(0), clipKind: new Uint16Array(0), clipLeft: new Float64Array(0), clipTop: new Float64Array(0), clipRight: new Float64Array(0), clipBottom: new Float64Array(0), parentClipNodeId: new Uint32Array(0) },
visibility: { subjectId: new Uint32Array(0), isRendered: new Uint8Array(0), isVisible: new Uint8Array(0), visibleArea: new Float64Array(0), clippedArea: new Float64Array(0) },
}
return adaptCanonicalWorldToSolver(canonical)
}
class SimpleResolver {
constructor() {
this.domains = new Map()
}
register(selector, ids) {
this.domains.set(selector, {
domainId: `dom_${selector}`,
subjectIds: new Uint32Array(ids),
provenance: `elements(${selector})`,
closed: true,
})
}
resolve(domain) {
return this.domains.get(domain.selector ?? domain.domain)
}
}
console.log('=== Evaluation With/Without Trace ===\n')
for (const n of [10, 50, 100]) {
const world = buildWorld(n)
const resolver = new SimpleResolver()
resolver.register('.a', Array.from({length: n}, (_, i) => i))
resolver.register('.b', Array.from({length: n}, (_, i) => i))
const formula = {
type: 'FormulaNode',
kind: 'forall',
bindings: [{
type: 'TupleBinding',
variables: ['$subject'],
domain: { type: 'DomainRef', domain: 'elements', selector: '.a' }
}],
body: {
type: 'FormulaNode',
kind: 'forall',
bindings: [{
type: 'TupleBinding',
variables: ['$reference'],
domain: { type: 'DomainRef', domain: 'elements', selector: '.b' }
}],
body: {
type: 'FormulaNode',
kind: 'predicate',
predicate: 'leftOf',
args: [
{ type: 'VariableRef', name: '$subject' },
{ type: 'VariableRef', name: '$reference' }
]
}
}
}
const timesWithTrace = []
const timesWithoutTrace = []
for (let i = 0; i < 100; i++) {
const start1 = performance.now()
evaluateLogic({ formula, world, resolver, options: { trace: true } })
timesWithTrace.push(performance.now() - start1)
const start2 = performance.now()
evaluateLogic({ formula, world, resolver, options: { trace: false } })
timesWithoutTrace.push(performance.now() - start2)
}
const mean = (arr) => arr.reduce((a,b) => a+b, 0) / arr.length
const withTrace = mean(timesWithTrace)
const withoutTrace = mean(timesWithoutTrace)
console.log(`${n}×${n} pairs:`)
console.log(` With trace: ${withTrace.toFixed(2)}ms`)
console.log(` Without trace: ${withoutTrace.toFixed(2)}ms`)
console.log(` Savings: ${((1 - withoutTrace/withTrace) * 100).toFixed(0)}%`)
}