Update benchmarks to be more realistic polymorphically (#7255)

Previously, the extract-components script would create the same number of layers of composites as the page it captures, but it would output a new class for each time any composite is used (since we don't want to replicate all the component logic).

I changed the script to output a single type for each type in the input -- and each generated component takes an index for which output it should return. This should be closer to how the original code behaves, especially with respect to VM function call lookups where the amount of polymorphism makes a difference.

I re-recorded the benchmarks with the new scripts. They run significantly faster:

```
Comparing old.txt (control) vs new.txt (test)
Significant differences marked by ***
% change from control to test, with 99% CIs:

* ssr_pe_cold_ms_jsc_jit
    % change: -41.73% [-43.37%, -40.09%]  ***
    means: 39.3191 (control), 22.9127 (test)
* ssr_pe_cold_ms_jsc_nojit
    % change: -44.24% [-46.69%, -41.80%]  ***
    means: 45.8646 (control), 25.5764 (test)
* ssr_pe_cold_ms_node
    % change: -45.61% [-47.38%, -43.85%]  ***
    means: 90.1118 (control), 49.0116 (test)
```

This is probably in part due to the changes here, but also the page I captured has changed somewhat in the meantime and there seem to be slightly fewer components in the hierarchy, so they're not really comparable. But going forward we can use this benchmark which should be more accurate. I also included an identical copy that uses stateless functional components so we can test optimizations to those later.
This commit is contained in:
Ben Alpert 2016-07-12 19:32:51 -07:00 committed by GitHub
parent 12bc80a6dc
commit e5513eceff
5 changed files with 16614 additions and 15035 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,18 @@
/*eslint-disable no-debugger */
// Copy and paste this file into your (Chrome) browser console after changing
// the React root ID. Works on facebook.com as of 10/28/15 (use a test user).
// the React root ID. Works on facebook.com as of 7/6/16 (use a test user).
// Then run this to convert the JSX:
//
// ../../node_modules/.bin/babel \
// --presets ../../node_modules/babel-preset-react \
// --no-babelrc --compact=false \
// bench-foo.js -o bench-foo-es5.js
'use strict';
var rootID = '.0';
var rootID = 5;
var outputStatelessFunctional = false;
var React = require('React');
var ReactMount = require('ReactMount');
@ -28,9 +35,35 @@ function elementMeta(element) {
}
function print(outerComponent) {
function addComposite(name, child) {
output += 'var ' + name + ' = React.createClass({\n';
var typeCounter = 0;
var elementCounter = 0;
var composites = new Map();
function addComposite(type, child) {
var info = composites.get(type);
if (!info) {
var name = (type.displayName || type.name || 'Component').replace(/(?:^[^a-z]|\W)+/gi, '_') + typeCounter++;
if (!/^[A-Z]/.test(name)) {
name = '_' + name;
}
info = {name: name, values: new Map()};
composites.set(type, info);
}
var c = elementCounter++;
info.values.set(c, child);
return '<' + info.name + ' x={' + c + '} />';
}
function printComposite(info) {
if (outputStatelessFunctional) {
output += 'var ' + info.name + ' = function(props) {\n';
} else {
output += 'var ' + info.name + ' = React.createClass({\n';
output += ' render: function() {\n';
output += ' var props = this.props;\n';
}
for (var [c, child] of info.values) {
output += ' if (props.x === ' + c + ') {\n';
if (child.indexOf('\n') !== -1) {
output += ' return (\n';
output += child.replace(/^|\n/g, '$& ') + '\n';
@ -38,8 +71,14 @@ function print(outerComponent) {
} else {
output += ' return ' + child + ';\n';
}
output += ' }\n';
}
if (outputStatelessFunctional) {
output += '};\n';
} else {
output += ' },\n';
output += '});\n';
}
output += '\n';
}
@ -59,15 +98,8 @@ function print(outerComponent) {
// Composite component
if (typeof element.type === 'function') {
var rendered = printImpl(component._renderedComponent);
var name = (component.getName() || 'Component').replace(/(?:^[^a-z]|\W)+/gi, '_') + counter++;
if (!/^[A-Z]/.test(name)) {
name = '_' + name;
}
addComposite(name, rendered);
var compositeMarkup = '<' + name;
compositeMarkup += elementMeta(component._currentElement);
compositeMarkup += ' />';
return compositeMarkup;
return addComposite(component._currentElement.type, rendered)
.replace(/(?= \/>$)/, elementMeta(component._currentElement));
}
// Native component
@ -116,6 +148,10 @@ function print(outerComponent) {
if (typeof children === 'boolean' || children == null) {
return '' + children;
}
if (typeof children === 'object' && !Array.isArray(children) && children[Symbol.iterator]) {
// TODO: Not quite right.
children = Array.from(children);
}
if (Array.isArray(children)) {
return children.length ? (
'[\n' +
@ -159,10 +195,12 @@ function print(outerComponent) {
}
var output = '(function() {\n\n';
var counter = 0;
var tail = printImpl(outerComponent);
addComposite('Benchmark', tail);
for (var info of composites.values()) {
printComposite(info);
}
printComposite({name: 'Benchmark', values: new Map([[undefined, tail]])});
output += 'this.Benchmark = Benchmark;\n';
output += '\n})(this);\n';
return output;