215 lines
5.8 KiB
JavaScript
215 lines
5.8 KiB
JavaScript
import React from 'react'
|
|
import { Line, Bar } from 'react-chartjs-2'
|
|
import debounceRender from 'react-debounce-render'
|
|
import platform from 'platform'
|
|
|
|
import { downloadAsJson } from '../../../lib/json-download/index.js'
|
|
|
|
import { datasetNames } from '../data/index.js'
|
|
import { datasetTemplates, genOptions, beautifyLabels } from './chartOptions.js'
|
|
|
|
export class Chart extends React.Component {
|
|
render() {
|
|
let { formState, stats, nodeData } = this.props
|
|
let { tolRange, chart } = formState
|
|
tolRange = tolRange || []
|
|
let labels = beautifyLabels(tolRange)
|
|
// let labels = tolRange.map(x => Math.round(x * 1000) / 1000)
|
|
let datasets = Object.keys(stats).map(name => ({
|
|
...datasetTemplates[name],
|
|
data: stats[name]
|
|
}))
|
|
datasets.push({ ...datasetTemplates.numberOfNodes, data: nodeData })
|
|
let data = { datasets, labels }
|
|
addLegendEntry(data, `High quality mode: ${formState.highQual}`)
|
|
addLegendEntry(data, `Platform: ${platform.description}`)
|
|
return (
|
|
<div className="component chartComponent">
|
|
<h2>Chart</h2>
|
|
{chart === 'wasmVsJs' && <WasmVsJsChart data={data} />}
|
|
{chart === 'wasmStack' && <WasmStackChart data={data} />}
|
|
{chart === 'simplifyJSStack' && <JsStackChart data={data} />}
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
class WasmVsJsChart extends React.Component {
|
|
constructor(props) {
|
|
super(props)
|
|
this.state = {
|
|
scaleY: 'linear',
|
|
values: 'mean'
|
|
}
|
|
}
|
|
convertDataToHz(data) {
|
|
let datasets = data.datasets
|
|
for (let dataset of datasets.filter(d => d.id !== 'numberOfNodes')) {
|
|
dataset.data = dataset.data.map(mean => 1000 / mean)
|
|
}
|
|
}
|
|
|
|
render() {
|
|
let data = JSON.parse(JSON.stringify(this.props.data)) // deep copy
|
|
let options = genOptions(
|
|
'Simplify.wasm vs. Simplify.js',
|
|
false,
|
|
this.state.scaleY,
|
|
this.state.values
|
|
)
|
|
if (this.state.values === 'hz') this.convertDataToHz(data)
|
|
return (
|
|
<>
|
|
<Line
|
|
ref={ref => (this.ref = ref)}
|
|
height={400}
|
|
width={400}
|
|
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>
|
|
<button onClick={() => saveChart(this.ref)}>Save chart</button>
|
|
</>
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the dataset specified by label
|
|
*/
|
|
function getDataset(data, id) {
|
|
return data.datasets.filter(d => d.id === id)[0]
|
|
}
|
|
|
|
/**
|
|
* Takes three data arrays. Subtracts the values of arr1 and arr2 from whole.
|
|
* This is used for calculating the time of execution in cases where the data
|
|
* transformation is part of the whole operation.
|
|
* Example: subtract storeCoords and loadResult from the whole simplifyWASM
|
|
* operation to get execution time of wasm code.
|
|
*/
|
|
function calculateTimeDelta(whole, arr1, arr2) {
|
|
const safeGet = (arr, idx) => (idx < arr.length ? arr[idx] : 0)
|
|
return whole.map((val, i) => val - (safeGet(arr1, i) + safeGet(arr2, i)))
|
|
}
|
|
|
|
function modifyDataArrays(data, exec, pre, after, newDatasetLabel) {
|
|
if (getDataset(data, exec) && getDataset(data, pre)) {
|
|
data.datasets.splice(1, 0, {
|
|
id: 'execution',
|
|
backgroundColor: 'red',
|
|
borderColor: 'red',
|
|
label: newDatasetLabel,
|
|
data: calculateTimeDelta(
|
|
getDataset(data, exec).data,
|
|
getDataset(data, pre).data,
|
|
getDataset(data, after).data
|
|
),
|
|
stack: 'a'
|
|
})
|
|
for (let dataset of data.datasets) {
|
|
if (dataset.id === exec) {
|
|
dataset.label = 'Total execution time'
|
|
dataset.stack = 'b'
|
|
dataset.backgroundColor = '#ff7a6b'
|
|
dataset.borderColor = '#ff7a6b'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class WasmStackChart extends React.Component {
|
|
render() {
|
|
const data = this.props.data
|
|
modifyDataArrays(
|
|
data,
|
|
'simplifyWASM',
|
|
'storeCoords',
|
|
'loadResult',
|
|
'Simplification'
|
|
)
|
|
const options = genOptions('Simplify.wasm runtime insights', true)
|
|
return (
|
|
<>
|
|
<Bar
|
|
ref={ref => (this.ref = ref)}
|
|
height={400}
|
|
width={400}
|
|
data={data}
|
|
options={options}
|
|
/>
|
|
<button onClick={() => saveChart(this.ref)}>Save chart</button>
|
|
</>
|
|
)
|
|
}
|
|
}
|
|
|
|
class JsStackChart extends React.Component {
|
|
render() {
|
|
const data = this.props.data
|
|
const options = genOptions(
|
|
'Simplify.js with coordinates transformation',
|
|
true
|
|
)
|
|
modifyDataArrays(
|
|
data,
|
|
'origSimplifyWithTransformCase',
|
|
'transformToObjectFormCase',
|
|
'transformToArrayFormCase',
|
|
'Simplification'
|
|
)
|
|
return (
|
|
<>
|
|
<Bar
|
|
ref={ref => (this.ref = ref)}
|
|
height={400}
|
|
width={400}
|
|
data={data}
|
|
options={options}
|
|
/>
|
|
<button onClick={() => saveChart(this.ref)}>Save chart</button>
|
|
</>
|
|
)
|
|
}
|
|
}
|
|
|
|
export const DebouncedChart = debounceRender(Chart, 300)
|
|
|
|
/**
|
|
* Downloads the chart config as json
|
|
*/
|
|
function saveChart(chartRef) {
|
|
let myChart = chartRef.chartInstance.config
|
|
let config = JSON.stringify(myChart, (key, val) =>
|
|
key === '_meta' ? undefined : val
|
|
)
|
|
downloadAsJson(config, 'config.json')
|
|
}
|
|
|
|
/**
|
|
* Adds an empty dataset to attach info to legend
|
|
* @param {} data
|
|
* @param {*} label
|
|
*/
|
|
function addLegendEntry(data, label) {
|
|
data.datasets.push({ label, data: [] })
|
|
}
|