forked from lukejacksonn/perflink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
128 lines (118 loc) · 3.83 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import {
html,
preact,
uid,
pSeries,
average,
fetchWorkerScript,
startTesting,
latestLocalStorage,
updateProgress,
extractValidSuites,
decodeState,
} from './utils.js'
import Tests from './components/tests.js'
import Archive from './components/archive.js'
import Results from './components/results.js'
const { render, useReducer, useEffect } = preact
const defaults = {
started: false,
dialog: true,
aside: 'results',
suites: extractValidSuites(localStorage),
runs: 100,
duration: 1,
progress: 0,
id: uid(),
searchTerm: '',
title: 'Finding numbers in an array of 1000',
before: `const data = [...Array(1000).keys()]`,
tests: [
{ name: 'Find item 100', code: 'data.find(x => x == 100)', ops: 203360 },
{ name: 'Find item 200', code: 'data.find(x => x == 200)', ops: 99560 },
{ name: 'Find item 400', code: 'data.find(x => x == 400)', ops: 55350 },
{ name: 'Find item 800', code: 'data.find(x => x == 800)', ops: 27660 },
],
}
const init = location.hash
? {
...defaults,
...decodeState(location.hash.slice(1)),
}
: defaults
const reducer = (state, update) => ({
...state,
...(typeof update === 'function' ? update(state) : update),
})
const app = () => {
const [state, dispatch] = useReducer(reducer, init)
const { before, started, tests, runs, title, id, suites, aside } = state
useEffect(() => {
if (started) {
setTimeout(() => {
;(async () => {
const checkScript = await fetchWorkerScript(before, 'check')
const runScript = await fetchWorkerScript(before, 'run')
const duration = await Promise.all(
tests.map(
(test) =>
new Promise((resolve) => {
const worker = new Worker(checkScript, { type: 'module' })
worker.onmessage = (e) => {
resolve(e.data)
worker.terminate()
}
worker.postMessage([test])
})
)
)
const bench = (test) =>
new Promise((resolve) => {
const worker = new Worker(runScript, { type: 'module' })
worker.onmessage = (e) => {
resolve({ ...test, ops: e.data })
worker.terminate()
}
worker.postMessage([test, Math.max(...duration)])
})
const tasks = () => () => {
dispatch(updateProgress)
return Promise.all(tests.map(bench))
}
pSeries(Array.from({ length: runs }, tasks)).then((results) =>
dispatch({ tests: average(results.flat()), started: false })
)
})()
}, 300)
}
}, [started, before, tests])
useEffect(() => {
const x = JSON.stringify({ id, title, before, tests, updated: new Date() })
history.replaceState(null, null, `#${encodeURIComponent(btoa(x))}`)
if (Object.fromEntries(suites)[id]) {
localStorage.setItem(id, x)
dispatch(latestLocalStorage)
}
}, [id, title, before, tests])
useEffect(() => {
const alt = (e) => (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)
const hotKeys = (e) => {
if (e.keyCode == 27) e.preventDefault() || dispatch({ aside: 'results' })
if (alt(e) && e.keyCode == 13)
e.preventDefault() || dispatch(startTesting)
if (alt(e) && e.keyCode == 80)
e.preventDefault() ||
dispatch({ aside: aside === 'archive' ? 'results' : 'archive' })
}
addEventListener('keydown', hotKeys)
return () => removeEventListener('keydown', hotKeys)
}, [aside])
return html`
<main className="app">
<${Tests} state=${state} dispatch=${dispatch} />
<${Results} state=${state} dispatch=${dispatch} />
<${Archive} state=${state} dispatch=${dispatch} />
</main>
`
}
render(html` <${app} /> `, document.body)