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": {
|
||||
"version": "4.0.3",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
@ -7367,6 +7367,14 @@
|
||||
"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": {
|
||||
"version": "16.8.6",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
|
||||
|
@ -25,6 +25,7 @@
|
||||
"lodash": "^4.17.11",
|
||||
"react": "^16.8.6",
|
||||
"react-chartjs-2": "^2.7.6",
|
||||
"react-debounce-render": "^5.0.0",
|
||||
"react-dom": "^16.8.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,23 +1,23 @@
|
||||
import { simplifyJSCase, origSimplifyJSCase } from './simplifyJS.js'
|
||||
import { SimplifyJSCase, SimplifyJSAltCase } from './simplifyJS.js'
|
||||
import {
|
||||
transformToArrayFormCase,
|
||||
transformToObjectFormCase,
|
||||
origSimplifyWithTransformCase
|
||||
TransformToArrayFormCase,
|
||||
TransformToObjectFormCase,
|
||||
OrigSimplifyWithTransformCase
|
||||
} from './transforms.js'
|
||||
import {
|
||||
simplifyWASMCase,
|
||||
storeCoordsCase,
|
||||
loadResultCase
|
||||
SimplifyWASMCase,
|
||||
StoreCoordsCase,
|
||||
LoadResultCase
|
||||
} from './simplifyWasm.js'
|
||||
|
||||
export const chartDependencies = {
|
||||
wasmStack: [storeCoordsCase, loadResultCase, simplifyWASMCase],
|
||||
wasmStack: [StoreCoordsCase, LoadResultCase, SimplifyWASMCase],
|
||||
simplifyJSStack: [
|
||||
origSimplifyWithTransformCase,
|
||||
transformToArrayFormCase,
|
||||
transformToObjectFormCase
|
||||
OrigSimplifyWithTransformCase,
|
||||
TransformToArrayFormCase,
|
||||
TransformToObjectFormCase
|
||||
],
|
||||
wasmVsJs: [origSimplifyJSCase, simplifyWASMCase, simplifyJSCase]
|
||||
wasmVsJs: [SimplifyJSCase, SimplifyWASMCase, SimplifyJSAltCase]
|
||||
}
|
||||
|
||||
const uniqueFilter = (val, idx, self) => self.indexOf(val) === idx
|
||||
@ -29,9 +29,10 @@ export function getChartDependencies(chartNames) {
|
||||
.filter(uniqueFilter)
|
||||
}
|
||||
|
||||
export function generateCases(data, formState) {
|
||||
export function generateCases(data, { tolRange, highQual, chart }) {
|
||||
if (!tolRange) return []
|
||||
|
||||
const cases = []
|
||||
const { tolRange, highQual, chart } = formState
|
||||
let params = { data, highQuality: highQual }
|
||||
for (let tol of tolRange) {
|
||||
params = { ...params, tol }
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Case } from './Case.js'
|
||||
|
||||
import simplifyJS from '../../../lib/simplify-js-alternative/simplify.js'
|
||||
import origSimplifyJS from '../../../lib/turf-simplify/lib/simplify.js'
|
||||
import simplifyJSAlt from '../../../lib/simplify-js-alternative/simplify.js'
|
||||
import simplifyJS from '../../../lib/turf-simplify/lib/simplify.js'
|
||||
|
||||
function arrayToObjectFormat(coords) {
|
||||
return coords.map(coord => {
|
||||
@ -9,25 +9,25 @@ function arrayToObjectFormat(coords) {
|
||||
})
|
||||
}
|
||||
|
||||
export class simplifyJSCase extends Case {
|
||||
export class SimplifyJSAltCase extends Case {
|
||||
get name() {
|
||||
return 'simplifyJS'
|
||||
return 'simplifyJSAlt'
|
||||
}
|
||||
async fn() {
|
||||
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() {
|
||||
return 'origSimplifyJS'
|
||||
return 'simplifyJS'
|
||||
}
|
||||
async setup() {
|
||||
this.parameters.dataObjectForm = arrayToObjectFormat(this.parameters.data)
|
||||
}
|
||||
async fn() {
|
||||
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'
|
||||
|
||||
export class simplifyWASMCase extends Case {
|
||||
export class SimplifyWASMCase extends Case {
|
||||
get name() {
|
||||
return 'simplifyWASM'
|
||||
}
|
||||
@ -20,7 +20,7 @@ export class simplifyWASMCase extends Case {
|
||||
}
|
||||
}
|
||||
|
||||
export class storeCoordsCase extends Case {
|
||||
export class StoreCoordsCase extends Case {
|
||||
get name() {
|
||||
return 'storeCoords'
|
||||
}
|
||||
@ -34,7 +34,7 @@ export class storeCoordsCase extends Case {
|
||||
}
|
||||
}
|
||||
|
||||
export class loadResultCase extends Case {
|
||||
export class LoadResultCase extends Case {
|
||||
get name() {
|
||||
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'
|
||||
|
||||
function arrayToObjectFormat(coords) {
|
||||
@ -11,7 +11,7 @@ function objectToArrayFormat(coords) {
|
||||
return coords.map(coord => [coord.x, coord.y])
|
||||
}
|
||||
|
||||
export class transformToObjectFormCase extends Case {
|
||||
export class TransformToObjectFormCase extends Case {
|
||||
get name() {
|
||||
return 'transformToObjectFormCase'
|
||||
}
|
||||
@ -20,28 +20,28 @@ export class transformToObjectFormCase extends Case {
|
||||
}
|
||||
}
|
||||
|
||||
export class transformToArrayFormCase extends Case {
|
||||
export class TransformToArrayFormCase extends Case {
|
||||
get name() {
|
||||
return 'transformToArrayFormCase'
|
||||
}
|
||||
async setup() {
|
||||
const { data, tol, highQuality } = this.parameters
|
||||
const transformed = arrayToObjectFormat(data)
|
||||
this.parameters.simplified = origSimplifyJS(transformed, tol, highQuality)
|
||||
this.parameters.simplified = simplifyJS(transformed, tol, highQuality)
|
||||
}
|
||||
async fn() {
|
||||
objectToArrayFormat(this.parameters.simplified)
|
||||
}
|
||||
}
|
||||
|
||||
export class origSimplifyWithTransformCase extends Case {
|
||||
export class OrigSimplifyWithTransformCase extends Case {
|
||||
get name() {
|
||||
return 'origSimplifyWithTransformCase'
|
||||
}
|
||||
async fn() {
|
||||
const { data, tol, highQuality } = this.parameters
|
||||
const transformed = arrayToObjectFormat(data)
|
||||
const simplified = origSimplifyJS(transformed, tol, highQuality)
|
||||
const simplified = simplifyJS(transformed, tol, highQuality)
|
||||
objectToArrayFormat(simplified)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
export class BenchmarkSuite {
|
||||
constructor() {
|
||||
this.state = 'initialized'
|
||||
this.cases = new CasesIterator([])
|
||||
this.reset()
|
||||
this.onChange = () => {}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.cases = []
|
||||
this.stats = {}
|
||||
this.cases.reset()
|
||||
this.stats = this.cases.getInitialStats()
|
||||
}
|
||||
|
||||
setBenchmarkType(benchmarktype) {
|
||||
@ -13,28 +16,54 @@ export class BenchmarkSuite {
|
||||
}
|
||||
|
||||
setCases(cases) {
|
||||
this.stats = {}
|
||||
this.cases = cases
|
||||
for (let c of this.cases) {
|
||||
this.stats[c.name] = []
|
||||
}
|
||||
this.cases = new CasesIterator(cases)
|
||||
this.stats = this.cases.getInitialStats()
|
||||
}
|
||||
|
||||
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() {
|
||||
let mean
|
||||
let i = 0
|
||||
const count = this.cases.length
|
||||
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)
|
||||
while (this.state === 'running' && this.cases.hasNext()) {
|
||||
await this.measureNext()
|
||||
this.onChange()
|
||||
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() {
|
||||
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,
|
||||
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 { Line } from 'react-chartjs-2'
|
||||
import debounceRender from 'react-debounce-render'
|
||||
|
||||
import { datasetTemplates, genOptions } from './chartOptions.js'
|
||||
|
||||
export class Chart extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
data: chartData,
|
||||
options: chartOptions
|
||||
scaleY: 'linear',
|
||||
values: 'mean'
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let labels = this.props.labels.map(x => x.toFixed(3))
|
||||
const data = { ...chartData, labels }
|
||||
data.datasets[0].data = this.props.data['simplifyWASM'] || []
|
||||
data.datasets[1].data = this.props.data['simplifyJS'] || []
|
||||
data.datasets[2].data = this.props.data['origSimplifyJS'] || []
|
||||
data.datasets[3].data = this.props.numNodes
|
||||
let { formState, stats, nodeData } = this.props
|
||||
let { tolRange, chart } = formState
|
||||
tolRange = tolRange || []
|
||||
let { scaleY, values } = this.state
|
||||
|
||||
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 (
|
||||
<div className="component">
|
||||
<h2>Chart</h2>
|
||||
<Line
|
||||
height={400}
|
||||
width={600}
|
||||
data={data}
|
||||
options={this.state.options}
|
||||
/>
|
||||
<Line height={400} width={600} data={data} options={options} />
|
||||
|
||||
<label>Scale Y-Axis</label>
|
||||
<select
|
||||
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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const chartData = {
|
||||
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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
export const DebouncedChart = debounceRender(Chart, 300)
|
||||
|
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',
|
||||
benchMethod: 'iteration',
|
||||
iterations: 10,
|
||||
timeToRun: 200
|
||||
timeToRun: 200,
|
||||
dataset: 'large',
|
||||
debouncedChart: false
|
||||
}
|
||||
|
||||
this.handleInputChange = this.handleInputChange.bind(this)
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
this.props.onFormChange(this.state)
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
this.props.onFormSubmit()
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState !== this.state) {
|
||||
this.notifyParent()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.notifyParent()
|
||||
}
|
||||
|
||||
handleInputChange(event) {
|
||||
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 === 'number' ? Number(value) : value
|
||||
value = target.type === 'range' ? Number(value) : value
|
||||
name = target.name
|
||||
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() {
|
||||
return (
|
||||
<div className="component">
|
||||
<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>
|
||||
<input
|
||||
name="tolStart"
|
||||
type="number"
|
||||
min="0.001"
|
||||
min="0.0001"
|
||||
max="1"
|
||||
step="0.001"
|
||||
step="0.0001"
|
||||
value={this.state.tolStart}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
@ -55,9 +76,9 @@ export class Form extends Component {
|
||||
<input
|
||||
name="tolStep"
|
||||
type="number"
|
||||
min="0.001"
|
||||
min="0.0001"
|
||||
max="1"
|
||||
step="0.001"
|
||||
step="0.0001"
|
||||
value={this.state.tolStep}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
@ -66,9 +87,9 @@ export class Form extends Component {
|
||||
<input
|
||||
name="tolEnd"
|
||||
type="number"
|
||||
min="0.01"
|
||||
min="0.001"
|
||||
max="5"
|
||||
step="0.01"
|
||||
step="0.001"
|
||||
value={this.state.tolEnd}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
@ -89,7 +110,7 @@ export class Form extends Component {
|
||||
>
|
||||
<option value="wasmVsJs">simplifyWasm vs. simplifyJS</option>
|
||||
<option value="wasmStack">wasmStack</option>
|
||||
<option value="simplifyStack">simplifyStack</option>
|
||||
<option value="simplifyStack">simpslifyStack</option>
|
||||
</select>
|
||||
|
||||
<label>Benchmarking method: </label>
|
||||
@ -119,7 +140,7 @@ export class Form extends Component {
|
||||
max="100"
|
||||
step="1"
|
||||
value={this.state.iterations}
|
||||
onInput={this.handleInputChange}
|
||||
onChange={this.handleInputChange}
|
||||
></input>
|
||||
)}
|
||||
{this.state.benchMethod === 'opsPerTime' && (
|
||||
@ -133,13 +154,29 @@ export class Form extends Component {
|
||||
max="1000"
|
||||
step="10"
|
||||
value={this.state.timeToRun}
|
||||
onInput={this.handleInputChange}
|
||||
onChange={this.handleInputChange}
|
||||
></input>
|
||||
)}
|
||||
|
||||
<input type="submit" value="run" />
|
||||
<label>Debounce Chart rendering: </label>
|
||||
<input
|
||||
name="debouncedChart"
|
||||
type="checkbox"
|
||||
checked={this.state.debouncedChart}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
</form>
|
||||
</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)
|
||||
let text = `${percentage}% of benchmarks completed (${finishedCount}/${totalCount})`
|
||||
return (
|
||||
<div className="component">
|
||||
<div>
|
||||
<h2>Status</h2>
|
||||
<span>{text}</span>
|
||||
</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 ReactDOM from 'react-dom'
|
||||
|
||||
import { data } from '../../data/large.js'
|
||||
|
||||
import { Form } from './components/form.js'
|
||||
import { Chart } from './components/chart.js'
|
||||
import { Progress } from './components/progress.js'
|
||||
import { DebouncedChart, Chart } from './components/chart.js'
|
||||
import { BenchmarkRunner } from './components/runner.js'
|
||||
|
||||
import { BenchmarkSuite } from './benchmarks/BenchmarkSuite.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)
|
||||
}
|
||||
import { simplifyMapper } from './simplifyMapper.js'
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.handleformChange = this.handleformChange.bind(this)
|
||||
this.handleFormSubmit = this.handleFormSubmit.bind(this)
|
||||
this.handleFormChange = this.handleFormChange.bind(this)
|
||||
this.handleStatsChange = this.handleStatsChange.bind(this)
|
||||
|
||||
this.state = {
|
||||
stats: {},
|
||||
formState: { tolRange: [] },
|
||||
totalCount: 0,
|
||||
finishedCount: 0,
|
||||
numNodes: []
|
||||
}
|
||||
|
||||
this.suite = new BenchmarkSuite(new IterationsBenchmark(10))
|
||||
this.suite.onCycle = (i, count, stats) => {
|
||||
this.setState({
|
||||
finishedCount: i,
|
||||
totalCount: count,
|
||||
stats: stats
|
||||
})
|
||||
formState: {},
|
||||
nodeData: []
|
||||
}
|
||||
}
|
||||
|
||||
async handleFormSubmit() {
|
||||
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
|
||||
}
|
||||
async handleFormChange(formState) {
|
||||
this.setState({
|
||||
formState: { ...formState, tolRange },
|
||||
numNodes: await simplifyMapper(tolRange, formState.highQual)
|
||||
formState,
|
||||
nodeData: await simplifyMapper(formState),
|
||||
stats: {}
|
||||
})
|
||||
}
|
||||
|
||||
async runBenchmarks() {
|
||||
this.suite.reset()
|
||||
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')
|
||||
handleStatsChange(stats) {
|
||||
this.setState({ stats })
|
||||
}
|
||||
|
||||
render() {
|
||||
let ChartComponent = this.state.formState.debouncedChart
|
||||
? DebouncedChart
|
||||
: Chart
|
||||
return (
|
||||
<>
|
||||
<Form
|
||||
onFormChange={this.handleformChange}
|
||||
onFormSubmit={this.handleFormSubmit}
|
||||
<Form onFormChange={this.handleFormChange} />
|
||||
<BenchmarkRunner
|
||||
formState={this.state.formState}
|
||||
onStatsChange={this.handleStatsChange}
|
||||
/>
|
||||
<Progress
|
||||
totalCount={this.state.totalCount}
|
||||
finishedCount={this.state.finishedCount}
|
||||
/>
|
||||
<Chart
|
||||
labels={this.state.formState.tolRange}
|
||||
highQual={this.state.formState.highQual}
|
||||
data={this.state.stats}
|
||||
numNodes={this.state.numNodes}
|
||||
<ChartComponent
|
||||
formState={this.state.formState}
|
||||
stats={this.state.stats}
|
||||
nodeData={this.state.nodeData}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
@ -113,7 +54,3 @@ class App extends Component {
|
||||
}
|
||||
|
||||
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