Improve state management

This commit is contained in:
Alfred Melch 2019-08-04 22:45:39 +02:00
parent 460dd7983b
commit e7f3fe853b
19 changed files with 464 additions and 236 deletions

View File

@ -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",

View File

@ -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": {

View File

@ -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 }

View File

@ -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)
} }
} }

View File

@ -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'
} }

View File

@ -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)
} }
} }

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}
]
}
}

View 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
}
}
]
}
}
}

View File

@ -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
}

View File

@ -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>

View 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>
)
}
}

File diff suppressed because one or more lines are too long

View 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 }

View File

@ -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)
}

View 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)
}