170 lines
7.7 KiB
JavaScript
170 lines
7.7 KiB
JavaScript
|
|
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)}%`)
|
|||
|
|
}
|