mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-02 02:25:22 +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 documentation
|
||||||
- [ ] includes tests
|
- [ ] includes tests
|
||||||
|
- [ ] includes benchmark reports
|
||||||
- [ ] includes an interactive example
|
- [ ] includes an interactive example
|
||||||
- [ ] includes screenshots/videos
|
- [ ] includes screenshots/videos
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
/dist
|
/dist
|
||||||
/dist-examples
|
/dist-examples
|
||||||
|
/dist-performance
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|||||||
+6
-2
@@ -1,7 +1,5 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
We are open to, and grateful for, any contributions made by the community.
|
|
||||||
|
|
||||||
## Reporting Issues and Asking Questions
|
## Reporting Issues and Asking Questions
|
||||||
|
|
||||||
Before opening an issue, please search the [issue
|
Before opening an issue, please search the [issue
|
||||||
@@ -31,6 +29,12 @@ Run the examples:
|
|||||||
npm run examples
|
npm run examples
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run the benchmarks in a browser by opening `./performance/index.html` after running:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run build:performance
|
||||||
|
```
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
+3
-1
@@ -11,10 +11,11 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
|
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
|
||||||
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
|
"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",
|
"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 -",
|
"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",
|
"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",
|
"prepublish": "npm run build && npm run build:umd",
|
||||||
"test": "npm run lint && npm run test:jest",
|
"test": "npm run lint && npm run test:jest",
|
||||||
"test:jest": "jest",
|
"test:jest": "jest",
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
"eslint-plugin-react": "^6.1.2",
|
"eslint-plugin-react": "^6.1.2",
|
||||||
"file-loader": "^0.9.0",
|
"file-loader": "^0.9.0",
|
||||||
"jest": "^16.0.2",
|
"jest": "^16.0.2",
|
||||||
|
"marky": "^1.1.1",
|
||||||
"node-libs-browser": "^0.5.3",
|
"node-libs-browser": "^0.5.3",
|
||||||
"react": "~15.4.1",
|
"react": "~15.4.1",
|
||||||
"react-addons-test-utils": "~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"
|
version "0.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
|
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:
|
math-expression-evaluator@^1.2.14:
|
||||||
version "1.2.14"
|
version "1.2.14"
|
||||||
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab"
|
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab"
|
||||||
|
|||||||
Reference in New Issue
Block a user