Files
react/perf/lib/BrowserPerfRunnerApp.react.js
2015-02-19 00:10:31 -05:00

220 lines
5.9 KiB
JavaScript

var BrowserPerfRunnerApp = React.createClass({
propTypes: {
tests: React.PropTypes.array.isRequired,
react: React.PropTypes.array.isRequired,
maxTime: React.PropTypes.number,
onCompleteEach: React.PropTypes.func,
onComplete: React.PropTypes.func,
onError: React.PropTypes.func,
headless: React.PropTypes.bool
},
getInitialState: function() {
var queue = [];
this.props.tests.forEach(function(testName) {
this.props.react.forEach(function(version) {
queue.push({
test: testName,
react: version
});
}, this);
}, this);
return {
queue: queue,
results: {}
};
},
handleResults: function(results) {
this.state.results[results.test + '@' + results.react] = results;
this.replaceState(this.state);
},
handleComplete: function(queueItem) {
queueItem.completed = true;
if (!this.props.onCompleteEach) {
return;
}
// Can't get the resultsForAllVersions if there are still some queued
var incompleteCount = 0;
for (var index = this.state.queue.length; --index >= 0;) {
if (this.state.queue[index].completed) {
continue;
}
if (this.state.queue[index].test === queueItem.test) {
return;
}
incompleteCount++;
}
var resultsForAllVersions = Object.keys(this.state.results)
.filter(function(key) {
return key.indexOf(queueItem.test) === 0;
})
.map(function(key) {
return this.state.results[key];
}, this);
this.props.onCompleteEach(resultsForAllVersions);
if (this.props.onComplete && incompleteCount === 0) {
this.props.onComplete(this.state.results);
}
},
render: function() {
var grid = null;
if (!this.props.headless) {
grid = GridViewTable({
rows: this.props.tests,
cols: this.props.react,
renderCell: BrowserPerfRunnerApp.renderBenchmarkCell,
value: this.state.results
});
}
return React.DOM.div(null,
BenchmarkQueue({
initialQueue: this.state.queue,
onChange: this.handleResults,
maxTime: this.props.maxTime,
onCompleteEach: this.handleComplete,
onError: this.props.onError
}),
grid
);
}
});
BrowserPerfRunnerApp.renderBenchmarkCell = function(props, row, col) {
if (col == null && row == null) {
return React.DOM.th(null);
}
if (row == null) {
return React.DOM.th({style:{verticalAlign:'top', textAlign:'center'}}, col);
}
var benchmarks = Object.keys(props.value)
.filter(function(key) {
return key.indexOf(row) === 0;
})
.map(function(key) {
return props.value[key];
})
.filter(function(benchmark) {
return benchmark && !benchmark.isRunning && benchmark.stats;
});
if (col == null) {
return React.DOM.th({style:{verticalAlign:'top', textAlign:'right'}},
React.DOM.a({href:'?test=' + row}, benchmarks[0] && benchmarks[0].name || row)
);
}
var key = row + '@' + col;
var benchmark = props.value[key];
if (!(benchmark && benchmark.stats)) {
return React.DOM.td({key:key});
}
var colors = [
'000000',
'AA0000',
'00AA00',
'AA5500',
'0000AA',
'AA00AA',
'00AAAA',
'AAAAAA',
'555555',
'FF5555',
'55FF55',
'FFFF55',
'5555FF',
'FF55FF',
'55FFFF',
'FFFFFF'
];
function chartValue(value) {
return Math.round(valueFromRangeToRange(value, chartValue.min, chartValue.max, 0, 100));
}
chartValue.min = Math.min.apply(Math, benchmarks.map(function(benchmark) {
return Math.min.apply(Math, benchmark.stats.sample);
}));
chartValue.max = Math.max.apply(Math, benchmarks.map(function(benchmark) {
return Math.max.apply(Math, benchmark.stats.sample);
}));
var means = benchmarks.map(function(benchmark) {
return benchmark.stats.mean;
});
benchmarks.forEach(function(benchmark) {
benchmark.isTheWinner = benchmark.stats.mean <= Math.min.apply(Math, means);
});
var chartValues = benchmarks.map(function(benchmark) {
// benchmark.stats.sample.sort(function(a,b) {return b - a;});
return benchmark.stats.sample.map(chartValue).join(',');
}).join('|');
return (
React.DOM.td({key:key, style:{textAlign:'center', width:234, verticalAlign:'top'}},
benchmark.error && benchmark.error.message || '',
React.DOM.div({style: benchmark.isTheWinner ? {backgroundColor:'#0A5', color:'#AFA'} : {backgroundColor:'transparent', color:'inherit'}},
Math.round(1 / benchmark.stats.mean * 100) / 100, ' op/s ',
React.DOM.strong(null, Math.round(benchmark.stats.mean * 1000 * 100) / 100, ' ms/op '),
React.DOM.small(null, '(±' + (Math.round(benchmark.stats.rme * 10) / 10) + '%)')
),
benchmark.isRunning && 'Running' || React.DOM.img({
style: {
borderWidth: 2,
borderStyle: 'solid',
color: '#' + colors[benchmarks.indexOf(benchmark)]
},
width: 230,
height: 50,
src: 'https://chart.googleapis.com/chart?cht=ls&chs=460x100&chd=t:' + chartValues + '&chco=' + colors.join(',')
})
)
);
};
function valueFromRangeToRange(value, fromMin, fromMax, toMin, toMax) {
var fromRange = fromMax - fromMin;
var toRange = toMax - toMin;
return (((value - fromMin) * toRange) / fromRange) + toMin;
}
var GridViewTable = React.createClass({
propTypes: {
rows: React.PropTypes.array.isRequired,
cols: React.PropTypes.array.isRequired,
renderCell: React.PropTypes.func.isRequired
},
_renderCell: function(col) {
return this.props.renderCell({value:this.props.value}, this._row, col);
},
_renderRow: function(row) {
this._row = row;
return React.DOM.tr({key:row},
this._renderCell(null, 0),
this.props.cols.map(this._renderCell, this)
);
},
render: function() {
return React.DOM.table(null,
this._renderRow(null, 0),
this.props.rows.map(this._renderRow, this)
);
}
});