mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-22 14:21:44 +00:00
Add initial performance benchmarks
Simple render tree benchmarks originally developed by @lelandrichardson Fix #306
This commit is contained in:
@@ -13,5 +13,6 @@ https://github.com/necolas/react-native-web/CONTRIBUTING.md
|
||||
|
||||
- [ ] includes documentation
|
||||
- [ ] includes tests
|
||||
- [ ] includes benchmark reports
|
||||
- [ ] includes an interactive example
|
||||
- [ ] includes screenshots/videos
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/dist
|
||||
/dist-examples
|
||||
/dist-performance
|
||||
/node_modules
|
||||
|
||||
+6
-2
@@ -1,7 +1,5 @@
|
||||
# Contributing
|
||||
|
||||
We are open to, and grateful for, any contributions made by the community.
|
||||
|
||||
## Reporting Issues and Asking Questions
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
@@ -31,6 +29,12 @@ Run the examples:
|
||||
npm run examples
|
||||
```
|
||||
|
||||
Run the benchmarks in a browser by opening `./performance/index.html` after running:
|
||||
|
||||
```
|
||||
npm run build:performance
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```
|
||||
|
||||
+3
-1
@@ -11,10 +11,11 @@
|
||||
"scripts": {
|
||||
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
|
||||
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
|
||||
"build:performance": "cd performance && webpack",
|
||||
"build:umd": "webpack --config webpack.config.js --sort-assets-by --progress",
|
||||
"deploy:examples": "git checkout gh-pages && rm -rf ./storybook && mv dist-examples storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
|
||||
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
|
||||
"lint": "eslint src",
|
||||
"lint": "eslint performance src",
|
||||
"prepublish": "npm run build && npm run build:umd",
|
||||
"test": "npm run lint && npm run test:jest",
|
||||
"test:jest": "jest",
|
||||
@@ -48,6 +49,7 @@
|
||||
"eslint-plugin-react": "^6.1.2",
|
||||
"file-loader": "^0.9.0",
|
||||
"jest": "^16.0.2",
|
||||
"marky": "^1.1.1",
|
||||
"node-libs-browser": "^0.5.3",
|
||||
"react": "~15.4.1",
|
||||
"react-addons-test-utils": "~15.4.1",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import * as marky from 'marky';
|
||||
|
||||
const fmt = (time) => `${Math.round(time * 100) / 100}ms`;
|
||||
|
||||
const measure = (name, fn) => {
|
||||
marky.mark(name);
|
||||
fn();
|
||||
const performanceMeasure = marky.stop(name);
|
||||
return performanceMeasure;
|
||||
};
|
||||
|
||||
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
|
||||
return new Promise((resolve) => {
|
||||
const performanceMeasures = [];
|
||||
let i = 0;
|
||||
|
||||
setup();
|
||||
const first = measure('first', task);
|
||||
teardown();
|
||||
|
||||
const done = () => {
|
||||
const mean = performanceMeasures.reduce((sum, performanceMeasure) => {
|
||||
return sum + performanceMeasure.duration;
|
||||
}, 0) / runs;
|
||||
|
||||
const firstDuration = fmt(first.duration);
|
||||
const meanDuration = fmt(mean);
|
||||
|
||||
console.log(`First: ${firstDuration}`);
|
||||
console.log(`Mean: ${meanDuration}`);
|
||||
console.groupEnd();
|
||||
resolve(mean);
|
||||
};
|
||||
|
||||
const a = () => {
|
||||
setup();
|
||||
window.requestAnimationFrame(b);
|
||||
};
|
||||
|
||||
const b = () => {
|
||||
performanceMeasures.push(measure('mean', task));
|
||||
window.requestAnimationFrame(c);
|
||||
};
|
||||
|
||||
const c = () => {
|
||||
teardown();
|
||||
window.requestAnimationFrame(d);
|
||||
};
|
||||
|
||||
const d = () => {
|
||||
i += 1;
|
||||
if (i < runs) {
|
||||
window.requestAnimationFrame(a);
|
||||
} else {
|
||||
window.requestAnimationFrame(done);
|
||||
}
|
||||
};
|
||||
|
||||
console.group();
|
||||
console.log(`[benchmark] ${name}: ${description}`);
|
||||
window.requestAnimationFrame(a);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = benchmark;
|
||||
@@ -0,0 +1,85 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const createDeepTree = ({ StyleSheet, View }) => {
|
||||
class DeepTree extends Component {
|
||||
static propTypes = {
|
||||
breadth: PropTypes.number.isRequired,
|
||||
depth: PropTypes.number.isRequired,
|
||||
id: PropTypes.number.isRequired,
|
||||
wrap: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const { breadth, depth, id, wrap } = this.props;
|
||||
let result = (
|
||||
<View
|
||||
style={[
|
||||
styles.outer,
|
||||
depth % 2 === 0 ? styles.even : styles.odd,
|
||||
styles[`custom${id % 3}`]
|
||||
]}
|
||||
>
|
||||
{depth === 0 && (
|
||||
<View
|
||||
style={[
|
||||
styles.terminal,
|
||||
styles[`terminal${id % 3}`]
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{depth !== 0 && Array.from({ length: breadth }).map((el, i) => (
|
||||
<DeepTree
|
||||
breadth={breadth}
|
||||
depth={depth - 1}
|
||||
id={i}
|
||||
key={i}
|
||||
wrap={wrap}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
for (let i = 0; i < wrap; i++) {
|
||||
result = <View>{result}</View>;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
padding: 4
|
||||
},
|
||||
odd: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
even: {
|
||||
flexDirection: 'column'
|
||||
},
|
||||
custom0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
custom1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
custom2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
terminal: {
|
||||
width: 20,
|
||||
height: 20
|
||||
},
|
||||
terminal0: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
terminal1: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
terminal2: {
|
||||
backgroundColor: 'red'
|
||||
}
|
||||
});
|
||||
|
||||
return DeepTree;
|
||||
};
|
||||
|
||||
module.exports = createDeepTree;
|
||||
@@ -0,0 +1,24 @@
|
||||
import benchmark from '../../benchmark';
|
||||
import createDeepTree from './createDeepTree';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactNative from 'react-native';
|
||||
|
||||
// React Native for Web implementation of the tree
|
||||
const DeepTree = createDeepTree(ReactNative);
|
||||
|
||||
const deepTreeBenchmark = ({ breadth, depth, wrap, runs }, node) => () => {
|
||||
const setup = () => { };
|
||||
const teardown = () => ReactDOM.unmountComponentAtNode(node);
|
||||
|
||||
return benchmark({
|
||||
name: 'DeepTree',
|
||||
description: `depth=${depth}, breadth=${breadth}, wrap=${wrap})`,
|
||||
runs,
|
||||
setup,
|
||||
teardown,
|
||||
task: () => ReactDOM.render(<DeepTree breadth={breadth} depth={depth} id={0} wrap={wrap} />, node)
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = deepTreeBenchmark;
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="root"></div>
|
||||
<script src="../dist-performance/performance.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
import deepTree from './benchmarks/deepTree';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const node = document.querySelector('.root');
|
||||
|
||||
Promise.resolve()
|
||||
.then(deepTree({ wrap: 4, depth: 3, breadth: 10, runs: 10 }, node))
|
||||
.then(deepTree({ wrap: 1, depth: 5, breadth: 3, runs: 20 }, node))
|
||||
.then(() => ReactDOM.render(<div>Complete</div>, node));
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
performance: './index'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../dist-performance'),
|
||||
filename: 'performance.bundle.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
query: { cacheDirectory: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
// https://github.com/animatedjs/animated/issues/40
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/es6-set/,
|
||||
path.join(__dirname, '../src/modules/polyfills/Set.js')
|
||||
),
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
dead_code: true,
|
||||
screw_ie8: true,
|
||||
warnings: true
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': path.join(__dirname, '../src')
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3738,6 +3738,10 @@ marked@^0.3.6:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
|
||||
|
||||
marky@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/marky/-/marky-1.1.1.tgz#dcd1eba3e2c6412619a76981f635b3698a8761e8"
|
||||
|
||||
math-expression-evaluator@^1.2.14:
|
||||
version "1.2.14"
|
||||
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab"
|
||||
|
||||
Reference in New Issue
Block a user