Improve state management
This commit is contained in:
parent
460dd7983b
commit
e7f3fe853b
18
benchmarking/package-lock.json
generated
18
benchmarking/package-lock.json
generated
@ -3545,11 +3545,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-standard": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA=="
|
|
||||||
},
|
|
||||||
"eslint-scope": {
|
"eslint-scope": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
|
||||||
@ -5952,6 +5947,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||||
},
|
},
|
||||||
|
"lodash.debounce": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
|
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||||
|
},
|
||||||
"lodash.sortby": {
|
"lodash.sortby": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||||
@ -7367,6 +7367,14 @@
|
|||||||
"prop-types": "15.7.2"
|
"prop-types": "15.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-debounce-render": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-debounce-render/-/react-debounce-render-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-3u4oUcZ1i9bOtENfnHW5f2x74b+01teKG24+NbuKSklCQhknWq/euyYiBnuRFhyJdaniVmtNqtLL66+2jCHXLw==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.debounce": "4.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.8.6",
|
"version": "16.8.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-chartjs-2": "^2.7.6",
|
"react-chartjs-2": "^2.7.6",
|
||||||
|
"react-debounce-render": "^5.0.0",
|
||||||
"react-dom": "^16.8.6"
|
"react-dom": "^16.8.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import { simplifyJSCase, origSimplifyJSCase } from './simplifyJS.js'
|
import { SimplifyJSCase, SimplifyJSAltCase } from './simplifyJS.js'
|
||||||
import {
|
import {
|
||||||
transformToArrayFormCase,
|
TransformToArrayFormCase,
|
||||||
transformToObjectFormCase,
|
TransformToObjectFormCase,
|
||||||
origSimplifyWithTransformCase
|
OrigSimplifyWithTransformCase
|
||||||
} from './transforms.js'
|
} from './transforms.js'
|
||||||
import {
|
import {
|
||||||
simplifyWASMCase,
|
SimplifyWASMCase,
|
||||||
storeCoordsCase,
|
StoreCoordsCase,
|
||||||
loadResultCase
|
LoadResultCase
|
||||||
} from './simplifyWasm.js'
|
} from './simplifyWasm.js'
|
||||||
|
|
||||||
export const chartDependencies = {
|
export const chartDependencies = {
|
||||||
wasmStack: [storeCoordsCase, loadResultCase, simplifyWASMCase],
|
wasmStack: [StoreCoordsCase, LoadResultCase, SimplifyWASMCase],
|
||||||
simplifyJSStack: [
|
simplifyJSStack: [
|
||||||
origSimplifyWithTransformCase,
|
OrigSimplifyWithTransformCase,
|
||||||
transformToArrayFormCase,
|
TransformToArrayFormCase,
|
||||||
transformToObjectFormCase
|
TransformToObjectFormCase
|
||||||
],
|
],
|
||||||
wasmVsJs: [origSimplifyJSCase, simplifyWASMCase, simplifyJSCase]
|
wasmVsJs: [SimplifyJSCase, SimplifyWASMCase, SimplifyJSAltCase]
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueFilter = (val, idx, self) => self.indexOf(val) === idx
|
const uniqueFilter = (val, idx, self) => self.indexOf(val) === idx
|
||||||
@ -29,9 +29,10 @@ export function getChartDependencies(chartNames) {
|
|||||||
.filter(uniqueFilter)
|
.filter(uniqueFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateCases(data, formState) {
|
export function generateCases(data, { tolRange, highQual, chart }) {
|
||||||
|
if (!tolRange) return []
|
||||||
|
|
||||||
const cases = []
|
const cases = []
|
||||||
const { tolRange, highQual, chart } = formState
|
|
||||||
let params = { data, highQuality: highQual }
|
let params = { data, highQuality: highQual }
|
||||||
for (let tol of tolRange) {
|
for (let tol of tolRange) {
|
||||||
params = { ...params, tol }
|
params = { ...params, tol }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Case } from './Case.js'
|
import { Case } from './Case.js'
|
||||||
|
|
||||||
import simplifyJS from '../../../lib/simplify-js-alternative/simplify.js'
|
import simplifyJSAlt from '../../../lib/simplify-js-alternative/simplify.js'
|
||||||
import origSimplifyJS from '../../../lib/turf-simplify/lib/simplify.js'
|
import simplifyJS from '../../../lib/turf-simplify/lib/simplify.js'
|
||||||
|
|
||||||
function arrayToObjectFormat(coords) {
|
function arrayToObjectFormat(coords) {
|
||||||
return coords.map(coord => {
|
return coords.map(coord => {
|
||||||
@ -9,25 +9,25 @@ function arrayToObjectFormat(coords) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export class simplifyJSCase extends Case {
|
export class SimplifyJSAltCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'simplifyJS'
|
return 'simplifyJSAlt'
|
||||||
}
|
}
|
||||||
async fn() {
|
async fn() {
|
||||||
const { data, tol, highQuality } = this.parameters
|
const { data, tol, highQuality } = this.parameters
|
||||||
simplifyJS(data, tol, highQuality)
|
simplifyJSAlt(data, tol, highQuality)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class origSimplifyJSCase extends Case {
|
export class SimplifyJSCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'origSimplifyJS'
|
return 'simplifyJS'
|
||||||
}
|
}
|
||||||
async setup() {
|
async setup() {
|
||||||
this.parameters.dataObjectForm = arrayToObjectFormat(this.parameters.data)
|
this.parameters.dataObjectForm = arrayToObjectFormat(this.parameters.data)
|
||||||
}
|
}
|
||||||
async fn() {
|
async fn() {
|
||||||
const { dataObjectForm, tol, highQuality } = this.parameters
|
const { dataObjectForm, tol, highQuality } = this.parameters
|
||||||
origSimplifyJS(dataObjectForm, tol, highQuality)
|
simplifyJS(dataObjectForm, tol, highQuality)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { simplifyWasm, getModule } from '../../../lib/simplify-wasm/index.js'
|
|||||||
|
|
||||||
import { Case } from './Case.js'
|
import { Case } from './Case.js'
|
||||||
|
|
||||||
export class simplifyWASMCase extends Case {
|
export class SimplifyWASMCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'simplifyWASM'
|
return 'simplifyWASM'
|
||||||
}
|
}
|
||||||
@ -20,7 +20,7 @@ export class simplifyWASMCase extends Case {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class storeCoordsCase extends Case {
|
export class StoreCoordsCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'storeCoords'
|
return 'storeCoords'
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ export class storeCoordsCase extends Case {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class loadResultCase extends Case {
|
export class LoadResultCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'loadResult'
|
return 'loadResult'
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import origSimplifyJS from '../../../lib/simplify-js/simplify.js'
|
import simplifyJS from '../../../lib/simplify-js/simplify.js'
|
||||||
import { Case } from './Case.js'
|
import { Case } from './Case.js'
|
||||||
|
|
||||||
function arrayToObjectFormat(coords) {
|
function arrayToObjectFormat(coords) {
|
||||||
@ -11,7 +11,7 @@ function objectToArrayFormat(coords) {
|
|||||||
return coords.map(coord => [coord.x, coord.y])
|
return coords.map(coord => [coord.x, coord.y])
|
||||||
}
|
}
|
||||||
|
|
||||||
export class transformToObjectFormCase extends Case {
|
export class TransformToObjectFormCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'transformToObjectFormCase'
|
return 'transformToObjectFormCase'
|
||||||
}
|
}
|
||||||
@ -20,28 +20,28 @@ export class transformToObjectFormCase extends Case {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class transformToArrayFormCase extends Case {
|
export class TransformToArrayFormCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'transformToArrayFormCase'
|
return 'transformToArrayFormCase'
|
||||||
}
|
}
|
||||||
async setup() {
|
async setup() {
|
||||||
const { data, tol, highQuality } = this.parameters
|
const { data, tol, highQuality } = this.parameters
|
||||||
const transformed = arrayToObjectFormat(data)
|
const transformed = arrayToObjectFormat(data)
|
||||||
this.parameters.simplified = origSimplifyJS(transformed, tol, highQuality)
|
this.parameters.simplified = simplifyJS(transformed, tol, highQuality)
|
||||||
}
|
}
|
||||||
async fn() {
|
async fn() {
|
||||||
objectToArrayFormat(this.parameters.simplified)
|
objectToArrayFormat(this.parameters.simplified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class origSimplifyWithTransformCase extends Case {
|
export class OrigSimplifyWithTransformCase extends Case {
|
||||||
get name() {
|
get name() {
|
||||||
return 'origSimplifyWithTransformCase'
|
return 'origSimplifyWithTransformCase'
|
||||||
}
|
}
|
||||||
async fn() {
|
async fn() {
|
||||||
const { data, tol, highQuality } = this.parameters
|
const { data, tol, highQuality } = this.parameters
|
||||||
const transformed = arrayToObjectFormat(data)
|
const transformed = arrayToObjectFormat(data)
|
||||||
const simplified = origSimplifyJS(transformed, tol, highQuality)
|
const simplified = simplifyJS(transformed, tol, highQuality)
|
||||||
objectToArrayFormat(simplified)
|
objectToArrayFormat(simplified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
export class BenchmarkSuite {
|
export class BenchmarkSuite {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.state = 'initialized'
|
||||||
|
this.cases = new CasesIterator([])
|
||||||
this.reset()
|
this.reset()
|
||||||
|
this.onChange = () => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.cases = []
|
this.cases.reset()
|
||||||
this.stats = {}
|
this.stats = this.cases.getInitialStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
setBenchmarkType(benchmarktype) {
|
setBenchmarkType(benchmarktype) {
|
||||||
@ -13,28 +16,54 @@ export class BenchmarkSuite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCases(cases) {
|
setCases(cases) {
|
||||||
this.stats = {}
|
this.cases = new CasesIterator(cases)
|
||||||
this.cases = cases
|
this.stats = this.cases.getInitialStats()
|
||||||
for (let c of this.cases) {
|
|
||||||
this.stats[c.name] = []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onCycle(i, count, stats) {}
|
addStat(name, val) {
|
||||||
|
this.stats[name] = [...this.stats[name], val]
|
||||||
|
}
|
||||||
|
|
||||||
|
async measureNext() {
|
||||||
|
let currentCase = this.cases.next()
|
||||||
|
this.benchmarktype.setCase(currentCase)
|
||||||
|
this.addStat(currentCase.name, await this.benchmarktype.run())
|
||||||
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
let mean
|
while (this.state === 'running' && this.cases.hasNext()) {
|
||||||
let i = 0
|
await this.measureNext()
|
||||||
const count = this.cases.length
|
this.onChange()
|
||||||
this.onCycle(i, count, this.stats)
|
|
||||||
for (const benchCase of this.cases) {
|
|
||||||
this.benchmarktype.setCase(benchCase)
|
|
||||||
mean = await this.benchmarktype.run()
|
|
||||||
this.stats[benchCase.name] = [...this.stats[benchCase.name], mean]
|
|
||||||
i++
|
|
||||||
this.onCycle(i, count, this.stats)
|
|
||||||
await freeEventLoop()
|
await freeEventLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (this.state) {
|
||||||
|
case 'running':
|
||||||
|
this.state = 'finished'
|
||||||
|
break
|
||||||
|
case 'stop_requested':
|
||||||
|
this.reset()
|
||||||
|
this.state = 'initialized'
|
||||||
|
break
|
||||||
|
case 'pause_requested':
|
||||||
|
this.state = this.cases.hasNext() ? 'paused' : 'finished'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.onChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.state = 'running'
|
||||||
|
this.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.state = 'pause_requested'
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.state = 'stop_requested'
|
||||||
|
this.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,3 +75,35 @@ export class BenchmarkSuite {
|
|||||||
async function freeEventLoop() {
|
async function freeEventLoop() {
|
||||||
return new Promise(resolve => setTimeout(resolve, 0))
|
return new Promise(resolve => setTimeout(resolve, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CasesIterator {
|
||||||
|
constructor(cases) {
|
||||||
|
this.cases = cases
|
||||||
|
this.idx = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
current() {
|
||||||
|
return this.cases[this.idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNext() {
|
||||||
|
return this.idx + 1 < this.cases.length
|
||||||
|
}
|
||||||
|
|
||||||
|
next() {
|
||||||
|
this.idx = this.idx + 1
|
||||||
|
return this.idx < this.cases.length ? this.current() : null
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.idx = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
getInitialStats() {
|
||||||
|
let stats = {}
|
||||||
|
for (let c of this.cases) {
|
||||||
|
stats[c.name] = []
|
||||||
|
}
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,3 +7,22 @@ export const benchmarkTypes = {
|
|||||||
iteration: IterationsBenchmark,
|
iteration: IterationsBenchmark,
|
||||||
opsPerTime: OpsPerTimeBenchmark
|
opsPerTime: OpsPerTimeBenchmark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const benchmarkTypeSelector = ({
|
||||||
|
benchMethod,
|
||||||
|
iterations,
|
||||||
|
timeToRun
|
||||||
|
}) => {
|
||||||
|
switch (benchMethod) {
|
||||||
|
case 'benchmarkJS':
|
||||||
|
return new BenchmarkJSBenchmark()
|
||||||
|
case 'iteration':
|
||||||
|
return new IterationsBenchmark(iterations)
|
||||||
|
|
||||||
|
case 'opsPerTime':
|
||||||
|
return new OpsPerTimeBenchmark(timeToRun)
|
||||||
|
default:
|
||||||
|
console.warn('benchmark type "' + name + '" not known')
|
||||||
|
return new IterationsBenchmark(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,92 +1,65 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { Line } from 'react-chartjs-2'
|
import { Line } from 'react-chartjs-2'
|
||||||
|
import debounceRender from 'react-debounce-render'
|
||||||
|
|
||||||
|
import { datasetTemplates, genOptions } from './chartOptions.js'
|
||||||
|
|
||||||
export class Chart extends Component {
|
export class Chart extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
data: chartData,
|
scaleY: 'linear',
|
||||||
options: chartOptions
|
values: 'mean'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let labels = this.props.labels.map(x => x.toFixed(3))
|
let { formState, stats, nodeData } = this.props
|
||||||
const data = { ...chartData, labels }
|
let { tolRange, chart } = formState
|
||||||
data.datasets[0].data = this.props.data['simplifyWASM'] || []
|
tolRange = tolRange || []
|
||||||
data.datasets[1].data = this.props.data['simplifyJS'] || []
|
let { scaleY, values } = this.state
|
||||||
data.datasets[2].data = this.props.data['origSimplifyJS'] || []
|
|
||||||
data.datasets[3].data = this.props.numNodes
|
let labels = tolRange
|
||||||
|
// let labels = tolRange.map(x => Math.round(x * 1000) / 1000)
|
||||||
|
let datasets = Object.keys(stats).map(name => ({
|
||||||
|
...datasetTemplates[name],
|
||||||
|
data: stats[name]
|
||||||
|
}))
|
||||||
|
if (values === 'hz') {
|
||||||
|
for (let dataset of datasets) {
|
||||||
|
dataset.data = dataset.data.map(mean => 1000 / mean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
datasets.push({ ...datasetTemplates.numberOfNodes, data: nodeData })
|
||||||
|
let data = { datasets, labels }
|
||||||
|
let options = genOptions(chart, false, scaleY)
|
||||||
|
// console.log(JSON.stringify(data, null, 2))
|
||||||
return (
|
return (
|
||||||
<div className="component">
|
<div className="component">
|
||||||
<h2>Chart</h2>
|
<h2>Chart</h2>
|
||||||
<Line
|
<Line height={400} width={600} data={data} options={options} />
|
||||||
height={400}
|
|
||||||
width={600}
|
<label>Scale Y-Axis</label>
|
||||||
data={data}
|
<select
|
||||||
options={this.state.options}
|
name="scaleY"
|
||||||
/>
|
value={this.state.scaleY}
|
||||||
|
onChange={e => this.setState({ scaleY: e.target.value })}
|
||||||
|
>
|
||||||
|
<option value="linear">Linear</option>
|
||||||
|
<option value="logarithmic">Logarithmic</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label>Values: </label>
|
||||||
|
<select
|
||||||
|
name="values"
|
||||||
|
value={this.state.values}
|
||||||
|
onChange={e => this.setState({ values: e.target.value })}
|
||||||
|
>
|
||||||
|
<option value="mean">ms per operation (mean)</option>
|
||||||
|
<option value="hz">Operations per second (hz)</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartData = {
|
export const DebouncedChart = debounceRender(Chart, 300)
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'simplifyWASM',
|
|
||||||
backgroundColor: 'red',
|
|
||||||
borderColor: 'red',
|
|
||||||
fill: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'simplifyJS',
|
|
||||||
backgroundColor: 'blue',
|
|
||||||
borderColor: 'blue',
|
|
||||||
fill: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'origSimplifyJS',
|
|
||||||
backgroundColor: 'green',
|
|
||||||
borderColor: 'green',
|
|
||||||
fill: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'numberOfNodes',
|
|
||||||
fill: false,
|
|
||||||
yAxisID: 'nodes',
|
|
||||||
data: [1, 2, 3]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartOptions = {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'simplifyWasm VS simplifyJS'
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
mode: 'nearest',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
yAxes: [
|
|
||||||
{
|
|
||||||
id: 'performance',
|
|
||||||
type: 'linear'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'nodes',
|
|
||||||
position: 'right',
|
|
||||||
type: 'linear',
|
|
||||||
gridLines: {
|
|
||||||
drawOnChartArea: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
69
benchmarking/src/components/chartOptions.js
Normal file
69
benchmarking/src/components/chartOptions.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import ChartJS from 'chart.js'
|
||||||
|
|
||||||
|
function genDataset(label, color, yAxisID = 'performance', type = 'line') {
|
||||||
|
return {
|
||||||
|
label,
|
||||||
|
yAxisID,
|
||||||
|
fill: false,
|
||||||
|
backgroundColor: color,
|
||||||
|
borderColor: color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const datasetTemplates = {
|
||||||
|
numberOfNodes: genDataset('numberOfNodes', 'grey', 'nodes'),
|
||||||
|
simplifyWASM: genDataset('simplifyWASM', 'red'),
|
||||||
|
simplifyJS: genDataset('simplifyJS', 'blue'),
|
||||||
|
simplifyJSAlt: genDataset('simplifyJSAlt', 'green'),
|
||||||
|
storeCoords: genDataset('storeCoords', 'blue'),
|
||||||
|
loadResult: genDataset('loadResult', 'green')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genOptions(title, stacked = false, scaleY = 'linear') {
|
||||||
|
return {
|
||||||
|
animation: {
|
||||||
|
duration: 0
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: title
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
mode: 'nearest',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
// type: scaleX,
|
||||||
|
stacked
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
id: 'performance',
|
||||||
|
type: scaleY,
|
||||||
|
stacked,
|
||||||
|
ticks: {
|
||||||
|
min: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodes',
|
||||||
|
position: 'right',
|
||||||
|
type: scaleY,
|
||||||
|
gridLines: {
|
||||||
|
drawOnChartArea: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
min: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,42 +11,63 @@ export class Form extends Component {
|
|||||||
chart: 'wasmVsJs',
|
chart: 'wasmVsJs',
|
||||||
benchMethod: 'iteration',
|
benchMethod: 'iteration',
|
||||||
iterations: 10,
|
iterations: 10,
|
||||||
timeToRun: 200
|
timeToRun: 200,
|
||||||
|
dataset: 'large',
|
||||||
|
debouncedChart: false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleInputChange = this.handleInputChange.bind(this)
|
this.handleInputChange = this.handleInputChange.bind(this)
|
||||||
this.handleSubmit = this.handleSubmit.bind(this)
|
|
||||||
this.props.onFormChange(this.state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
event.preventDefault()
|
if (prevState !== this.state) {
|
||||||
this.props.onFormSubmit()
|
this.notifyParent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.notifyParent()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange(event) {
|
handleInputChange(event) {
|
||||||
let name, value
|
let name, value
|
||||||
const target = event.target
|
let target = event.target
|
||||||
|
name = target.name
|
||||||
value = target.type === 'checkbox' ? target.checked : target.value
|
value = target.type === 'checkbox' ? target.checked : target.value
|
||||||
value = target.type === 'number' ? Number(value) : value
|
value = target.type === 'number' ? Number(value) : value
|
||||||
value = target.type === 'range' ? Number(value) : value
|
value = target.type === 'range' ? Number(value) : value
|
||||||
name = target.name
|
|
||||||
this.setState({ [name]: value })
|
this.setState({ [name]: value })
|
||||||
this.props.onFormChange({ ...this.state, [name]: value })
|
}
|
||||||
|
|
||||||
|
async notifyParent() {
|
||||||
|
let { tolStart, tolEnd, tolStep } = this.state
|
||||||
|
let tolRange = calculateRange(tolStart, tolEnd, tolStep)
|
||||||
|
this.props.onFormChange({ ...this.state, tolRange })
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="component">
|
<div className="component">
|
||||||
<h2>Settings</h2>
|
<h2>Settings</h2>
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form>
|
||||||
|
<label>Dataset</label>
|
||||||
|
<select
|
||||||
|
name="dataset"
|
||||||
|
value={this.state.dataset}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
>
|
||||||
|
<option value="large">Simplify.js (73.752 points)</option>
|
||||||
|
<option value="bavaria">Bavaria (116.829 points)</option>
|
||||||
|
<option value="small">small (8 points)</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
<label>Tolerance start: </label>
|
<label>Tolerance start: </label>
|
||||||
<input
|
<input
|
||||||
name="tolStart"
|
name="tolStart"
|
||||||
type="number"
|
type="number"
|
||||||
min="0.001"
|
min="0.0001"
|
||||||
max="1"
|
max="1"
|
||||||
step="0.001"
|
step="0.0001"
|
||||||
value={this.state.tolStart}
|
value={this.state.tolStart}
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
/>
|
/>
|
||||||
@ -55,9 +76,9 @@ export class Form extends Component {
|
|||||||
<input
|
<input
|
||||||
name="tolStep"
|
name="tolStep"
|
||||||
type="number"
|
type="number"
|
||||||
min="0.001"
|
min="0.0001"
|
||||||
max="1"
|
max="1"
|
||||||
step="0.001"
|
step="0.0001"
|
||||||
value={this.state.tolStep}
|
value={this.state.tolStep}
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
/>
|
/>
|
||||||
@ -66,9 +87,9 @@ export class Form extends Component {
|
|||||||
<input
|
<input
|
||||||
name="tolEnd"
|
name="tolEnd"
|
||||||
type="number"
|
type="number"
|
||||||
min="0.01"
|
min="0.001"
|
||||||
max="5"
|
max="5"
|
||||||
step="0.01"
|
step="0.001"
|
||||||
value={this.state.tolEnd}
|
value={this.state.tolEnd}
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
/>
|
/>
|
||||||
@ -89,7 +110,7 @@ export class Form extends Component {
|
|||||||
>
|
>
|
||||||
<option value="wasmVsJs">simplifyWasm vs. simplifyJS</option>
|
<option value="wasmVsJs">simplifyWasm vs. simplifyJS</option>
|
||||||
<option value="wasmStack">wasmStack</option>
|
<option value="wasmStack">wasmStack</option>
|
||||||
<option value="simplifyStack">simplifyStack</option>
|
<option value="simplifyStack">simpslifyStack</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label>Benchmarking method: </label>
|
<label>Benchmarking method: </label>
|
||||||
@ -119,7 +140,7 @@ export class Form extends Component {
|
|||||||
max="100"
|
max="100"
|
||||||
step="1"
|
step="1"
|
||||||
value={this.state.iterations}
|
value={this.state.iterations}
|
||||||
onInput={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
></input>
|
></input>
|
||||||
)}
|
)}
|
||||||
{this.state.benchMethod === 'opsPerTime' && (
|
{this.state.benchMethod === 'opsPerTime' && (
|
||||||
@ -133,13 +154,29 @@ export class Form extends Component {
|
|||||||
max="1000"
|
max="1000"
|
||||||
step="10"
|
step="10"
|
||||||
value={this.state.timeToRun}
|
value={this.state.timeToRun}
|
||||||
onInput={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
></input>
|
></input>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<input type="submit" value="run" />
|
<label>Debounce Chart rendering: </label>
|
||||||
|
<input
|
||||||
|
name="debouncedChart"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.debouncedChart}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateRange(start, stop, step) {
|
||||||
|
const range = []
|
||||||
|
for (start; start < stop; start += step) {
|
||||||
|
range.push(start)
|
||||||
|
if (step === 0) break
|
||||||
|
if (range.length > 200) break
|
||||||
|
}
|
||||||
|
return range
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ export const Progress = props => {
|
|||||||
percentage = percentage.toFixed(2)
|
percentage = percentage.toFixed(2)
|
||||||
let text = `${percentage}% of benchmarks completed (${finishedCount}/${totalCount})`
|
let text = `${percentage}% of benchmarks completed (${finishedCount}/${totalCount})`
|
||||||
return (
|
return (
|
||||||
<div className="component">
|
<div>
|
||||||
<h2>Status</h2>
|
<h2>Status</h2>
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
</div>
|
</div>
|
||||||
|
100
benchmarking/src/components/runner.js
Normal file
100
benchmarking/src/components/runner.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
|
||||||
|
import { dataSelector } from '../data/index.js'
|
||||||
|
|
||||||
|
import { BenchmarkSuite } from '../benchmarks/BenchmarkSuite.js'
|
||||||
|
import { generateCases } from '../benchmarkCases/index.js'
|
||||||
|
import { benchmarkTypeSelector } from '../benchmarks/index.js'
|
||||||
|
|
||||||
|
import { Progress } from './progress'
|
||||||
|
|
||||||
|
export class BenchmarkRunner extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
state: 'initialized',
|
||||||
|
totalCount: 0,
|
||||||
|
finishedCount: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.suite = new BenchmarkSuite()
|
||||||
|
|
||||||
|
this.suite.onChange = () => {
|
||||||
|
this.setState({
|
||||||
|
state: this.suite.state,
|
||||||
|
totalCount: this.suite.cases.cases.length,
|
||||||
|
finishedCount: this.suite.cases.idx + 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.onStatsChange(this.suite.stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevState !== this.state) {
|
||||||
|
this.props.onStatsChange(this.suite.stats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeSuite() {
|
||||||
|
let data = dataSelector[this.props.formState.dataset]
|
||||||
|
this.suite.setBenchmarkType(benchmarkTypeSelector(this.props.formState))
|
||||||
|
this.suite.setCases(generateCases(data, this.props.formState))
|
||||||
|
this.suite.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
stateIsOneOf(states) {
|
||||||
|
return states.indexOf(this.suite.state) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
stopButtonEnabled() {
|
||||||
|
return this.stateIsOneOf(['running', 'finished', 'paused'])
|
||||||
|
}
|
||||||
|
|
||||||
|
startButtonShown() {
|
||||||
|
return this.stateIsOneOf(['initialized'])
|
||||||
|
}
|
||||||
|
|
||||||
|
resumeButtonShown() {
|
||||||
|
return this.stateIsOneOf(['paused'])
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseButtonShown() {
|
||||||
|
return this.stateIsOneOf(['running'])
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="component">
|
||||||
|
<Progress
|
||||||
|
totalCount={this.state.totalCount}
|
||||||
|
finishedCount={this.state.finishedCount}
|
||||||
|
/>
|
||||||
|
<div>State: {this.suite.state}</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => this.suite.stop()}
|
||||||
|
disabled={!this.stopButtonEnabled()}
|
||||||
|
>
|
||||||
|
stop
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{this.startButtonShown() && (
|
||||||
|
<button onClick={() => this.initializeSuite()}>start</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{this.resumeButtonShown() && (
|
||||||
|
<button onClick={() => this.suite.start()}>start</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{this.pauseButtonShown() && (
|
||||||
|
<button onClick={() => this.suite.pause()}>pause</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
1
benchmarking/src/data/bavaria.js
Normal file
1
benchmarking/src/data/bavaria.js
Normal file
File diff suppressed because one or more lines are too long
5
benchmarking/src/data/index.js
Normal file
5
benchmarking/src/data/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { data as large } from './large.js'
|
||||||
|
import { data as small } from './small.js'
|
||||||
|
import { data as bavaria } from './bavaria.js'
|
||||||
|
|
||||||
|
export const dataSelector = { large, small, bavaria }
|
@ -1,111 +1,52 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
|
|
||||||
import { data } from '../../data/large.js'
|
|
||||||
|
|
||||||
import { Form } from './components/form.js'
|
import { Form } from './components/form.js'
|
||||||
import { Chart } from './components/chart.js'
|
import { DebouncedChart, Chart } from './components/chart.js'
|
||||||
import { Progress } from './components/progress.js'
|
import { BenchmarkRunner } from './components/runner.js'
|
||||||
|
|
||||||
import { BenchmarkSuite } from './benchmarks/BenchmarkSuite.js'
|
import { simplifyMapper } from './simplifyMapper.js'
|
||||||
import { generateCases } from './benchmarkCases/index.js'
|
|
||||||
import { IterationsBenchmark } from './benchmarks/IterationBenchmark.js'
|
|
||||||
import { BenchmarkJSBenchmark } from './benchmarks/BenchmarkJSBenchmark.js'
|
|
||||||
import { OpsPerTimeBenchmark } from './benchmarks/OpsPerTimeBenchmark.js'
|
|
||||||
|
|
||||||
import simplifyJS from '../../lib/simplify-js-alternative/simplify.js'
|
|
||||||
import { simplifyWasm } from '../../lib/simplify-wasm/index.js'
|
|
||||||
|
|
||||||
export async function simplifyMapper(labels, highQual) {
|
|
||||||
let items = labels.map(tol =>
|
|
||||||
simplifyWasm(data, tol, highQual).then(arr => arr.length)
|
|
||||||
)
|
|
||||||
return await Promise.all(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.handleformChange = this.handleformChange.bind(this)
|
this.handleFormChange = this.handleFormChange.bind(this)
|
||||||
this.handleFormSubmit = this.handleFormSubmit.bind(this)
|
this.handleStatsChange = this.handleStatsChange.bind(this)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
stats: {},
|
stats: {},
|
||||||
formState: { tolRange: [] },
|
formState: {},
|
||||||
totalCount: 0,
|
nodeData: []
|
||||||
finishedCount: 0,
|
|
||||||
numNodes: []
|
|
||||||
}
|
|
||||||
|
|
||||||
this.suite = new BenchmarkSuite(new IterationsBenchmark(10))
|
|
||||||
this.suite.onCycle = (i, count, stats) => {
|
|
||||||
this.setState({
|
|
||||||
finishedCount: i,
|
|
||||||
totalCount: count,
|
|
||||||
stats: stats
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleFormSubmit() {
|
async handleFormChange(formState) {
|
||||||
this.runBenchmarks()
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleformChange(formState) {
|
|
||||||
const { tolStart, tolEnd, tolStep } = formState
|
|
||||||
const tolRange = []
|
|
||||||
for (let i = tolStart; i < tolEnd; i += tolStep) {
|
|
||||||
tolRange.push(i)
|
|
||||||
if (tolStep === 0) break
|
|
||||||
if (tolRange.length > 200) break
|
|
||||||
}
|
|
||||||
this.setState({
|
this.setState({
|
||||||
formState: { ...formState, tolRange },
|
formState,
|
||||||
numNodes: await simplifyMapper(tolRange, formState.highQual)
|
nodeData: await simplifyMapper(formState),
|
||||||
|
stats: {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async runBenchmarks() {
|
handleStatsChange(stats) {
|
||||||
this.suite.reset()
|
this.setState({ stats })
|
||||||
let benchtype
|
|
||||||
console.log(this.state.formState.benchMethod)
|
|
||||||
switch (this.state.formState.benchMethod) {
|
|
||||||
case 'benchmarkJS':
|
|
||||||
benchtype = new BenchmarkJSBenchmark()
|
|
||||||
break
|
|
||||||
case 'iteration':
|
|
||||||
benchtype = new IterationsBenchmark(this.state.formState.iterations)
|
|
||||||
break
|
|
||||||
case 'opsPerTime':
|
|
||||||
benchtype = new OpsPerTimeBenchmark(this.state.formState.timeToRun)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
console.warn('benchmark type "' + this.state.benchtype + '" not known')
|
|
||||||
benchtype = new IterationsBenchmark(10)
|
|
||||||
}
|
|
||||||
this.suite.setBenchmarkType(benchtype)
|
|
||||||
this.suite.setCases(generateCases(data, this.state.formState))
|
|
||||||
updateStatus('Running')
|
|
||||||
await this.suite.run()
|
|
||||||
updateStatus('Finished')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let ChartComponent = this.state.formState.debouncedChart
|
||||||
|
? DebouncedChart
|
||||||
|
: Chart
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form
|
<Form onFormChange={this.handleFormChange} />
|
||||||
onFormChange={this.handleformChange}
|
<BenchmarkRunner
|
||||||
onFormSubmit={this.handleFormSubmit}
|
formState={this.state.formState}
|
||||||
|
onStatsChange={this.handleStatsChange}
|
||||||
/>
|
/>
|
||||||
<Progress
|
<ChartComponent
|
||||||
totalCount={this.state.totalCount}
|
formState={this.state.formState}
|
||||||
finishedCount={this.state.finishedCount}
|
stats={this.state.stats}
|
||||||
/>
|
nodeData={this.state.nodeData}
|
||||||
<Chart
|
|
||||||
labels={this.state.formState.tolRange}
|
|
||||||
highQual={this.state.formState.highQual}
|
|
||||||
data={this.state.stats}
|
|
||||||
numNodes={this.state.numNodes}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@ -113,7 +54,3 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementsByTagName('main')[0])
|
ReactDOM.render(<App />, document.getElementsByTagName('main')[0])
|
||||||
|
|
||||||
const updateStatus = status => {
|
|
||||||
console.log(status)
|
|
||||||
}
|
|
||||||
|
16
benchmarking/src/simplifyMapper.js
Normal file
16
benchmarking/src/simplifyMapper.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { memoize } from 'lodash'
|
||||||
|
import { simplifyWasm } from '../../lib/simplify-wasm/index.js'
|
||||||
|
|
||||||
|
import { dataSelector } from './data/index.js'
|
||||||
|
|
||||||
|
async function numberOfNodes(dataset, tol, highQual) {
|
||||||
|
let data = dataSelector[dataset]
|
||||||
|
return await simplifyWasm(data, tol, highQual).then(arr => arr.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
const memNumberOfNodes = memoize(numberOfNodes, (...args) => args.join('|'))
|
||||||
|
|
||||||
|
export async function simplifyMapper({ dataset, tolRange, highQual }) {
|
||||||
|
let items = tolRange.map(tol => memNumberOfNodes(dataset, tol, highQual))
|
||||||
|
return await Promise.all(items)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user