From ed0cafac7caf553eaaa3d373024442b1509f4b50 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Wed, 17 Jan 2018 16:57:16 -0800 Subject: [PATCH] Rewrite benchmarks app Reorganizes and rewrites the benchmarks. Each implementation is now self-contained and the benchmarks can be run using a GUI. The benchmarks themselves have been changed so that individual tests render over a shorter time frame and more samples are taken. --- packages/benchmarks/README.md | 45 +-- packages/benchmarks/index.html | 5 + packages/benchmarks/package.json | 29 +- packages/benchmarks/src/app/App.js | 293 ++++++++++++++++++ .../benchmarks/src/app/Benchmark/index.js | 221 +++++++++++++ packages/benchmarks/src/app/Benchmark/math.js | 27 ++ .../benchmarks/src/app/Benchmark/timing.js | 18 ++ .../benchmarks/src/app/Benchmark/types.js | 28 ++ packages/benchmarks/src/app/Button.js | 70 +++++ packages/benchmarks/src/app/Icons.js | 55 ++++ packages/benchmarks/src/app/Layout.js | 63 ++++ packages/benchmarks/src/app/ReportCard.js | 71 +++++ packages/benchmarks/src/app/Text.js | 34 ++ packages/benchmarks/src/app/theme.js | 101 ++++++ packages/benchmarks/src/benchmark.js | 98 ------ packages/benchmarks/src/cases/NestedTree.js | 58 ---- .../src/cases/SierpinskiTriangle.js | 127 ++++---- packages/benchmarks/src/cases/Tree.js | 45 +++ .../benchmarks/src/cases/renderDeepTree.js | 14 - .../src/cases/renderSierpinskiTriangle.js | 112 ------- packages/benchmarks/src/cases/renderTweet.js | 112 ------- .../benchmarks/src/cases/renderWideTree.js | 14 - .../benchmarks/src/createRenderBenchmark.js | 24 -- packages/benchmarks/src/impl.js | 38 +++ .../src/implementations/aphrodite/Box.js | 17 +- .../src/implementations/aphrodite/Provider.js | 2 + .../src/implementations/aphrodite/index.js | 2 + .../implementations/css-modules/Provider.js | 2 + .../css-modules/box-styles.css | 17 +- .../src/implementations/css-modules/index.js | 2 + .../src/implementations/emotion/Box.js | 17 +- .../src/implementations/emotion/Dot.js | 7 +- .../src/implementations/emotion/Provider.js | 2 + .../src/implementations/emotion/index.js | 2 + .../src/implementations/glamor/Box.js | 17 +- .../src/implementations/glamor/Dot.js | 7 +- .../src/implementations/glamor/Provider.js | 2 + .../src/implementations/glamor/index.js | 2 + .../src/implementations/inline-styles/Box.js | 17 +- .../src/implementations/inline-styles/Dot.js | 7 +- .../implementations/inline-styles/Provider.js | 2 + .../implementations/inline-styles/index.js | 2 + .../src/implementations/radium/Box.js | 17 +- .../src/implementations/radium/Dot.js | 7 +- .../src/implementations/radium/Provider.js | 2 + .../src/implementations/radium/index.js | 2 + .../implementations/{jss => react-jss}/Box.js | 17 +- .../src/implementations/react-jss/Provider.js | 2 + .../{jss => react-jss}/View.js | 0 .../{jss => react-jss}/index.js | 2 + .../implementations/react-native-web/Box.js | 17 +- .../implementations/react-native-web/Dot.js | 7 +- .../react-native-web/Provider.js | 2 + .../implementations/react-native-web/index.js | 2 + .../src/implementations/reactxp/Box.js | 17 +- .../src/implementations/reactxp/Dot.js | 7 +- .../src/implementations/reactxp/Provider.js | 31 ++ .../src/implementations/reactxp/index.js | 2 + .../implementations/styled-components/Box.js | 18 +- .../implementations/styled-components/Dot.js | 5 +- .../styled-components/Provider.js | 2 + .../styled-components/index.js | 2 + .../implementations/styletron-react/Box.js | 47 +++ .../implementations/styletron-react/Dot.js | 25 ++ .../styletron-react/Provider.js | 19 ++ .../implementations/styletron-react/View.js | 26 ++ .../{styletron => styletron-react}/index.js | 2 + .../src/implementations/styletron/Box.js | 49 --- .../src/implementations/styletron/Dot.js | 37 --- .../src/implementations/styletron/View.js | 33 -- packages/benchmarks/src/index.js | 141 +++------ packages/benchmarks/webpack.config.js | 10 +- .../src/exports/KeyboardAvoidingView/index.js | 2 +- yarn.lock | 156 ++++++---- 74 files changed, 1632 insertions(+), 904 deletions(-) create mode 100644 packages/benchmarks/src/app/App.js create mode 100644 packages/benchmarks/src/app/Benchmark/index.js create mode 100644 packages/benchmarks/src/app/Benchmark/math.js create mode 100644 packages/benchmarks/src/app/Benchmark/timing.js create mode 100644 packages/benchmarks/src/app/Benchmark/types.js create mode 100644 packages/benchmarks/src/app/Button.js create mode 100644 packages/benchmarks/src/app/Icons.js create mode 100644 packages/benchmarks/src/app/Layout.js create mode 100644 packages/benchmarks/src/app/ReportCard.js create mode 100644 packages/benchmarks/src/app/Text.js create mode 100644 packages/benchmarks/src/app/theme.js delete mode 100644 packages/benchmarks/src/benchmark.js delete mode 100644 packages/benchmarks/src/cases/NestedTree.js create mode 100644 packages/benchmarks/src/cases/Tree.js delete mode 100644 packages/benchmarks/src/cases/renderDeepTree.js delete mode 100644 packages/benchmarks/src/cases/renderSierpinskiTriangle.js delete mode 100644 packages/benchmarks/src/cases/renderTweet.js delete mode 100644 packages/benchmarks/src/cases/renderWideTree.js delete mode 100644 packages/benchmarks/src/createRenderBenchmark.js create mode 100644 packages/benchmarks/src/impl.js create mode 100644 packages/benchmarks/src/implementations/aphrodite/Provider.js create mode 100644 packages/benchmarks/src/implementations/css-modules/Provider.js create mode 100644 packages/benchmarks/src/implementations/emotion/Provider.js create mode 100644 packages/benchmarks/src/implementations/glamor/Provider.js create mode 100644 packages/benchmarks/src/implementations/inline-styles/Provider.js create mode 100644 packages/benchmarks/src/implementations/radium/Provider.js rename packages/benchmarks/src/implementations/{jss => react-jss}/Box.js (74%) create mode 100644 packages/benchmarks/src/implementations/react-jss/Provider.js rename packages/benchmarks/src/implementations/{jss => react-jss}/View.js (100%) rename packages/benchmarks/src/implementations/{jss => react-jss}/index.js (64%) create mode 100644 packages/benchmarks/src/implementations/react-native-web/Provider.js create mode 100644 packages/benchmarks/src/implementations/reactxp/Provider.js create mode 100644 packages/benchmarks/src/implementations/styled-components/Provider.js create mode 100644 packages/benchmarks/src/implementations/styletron-react/Box.js create mode 100644 packages/benchmarks/src/implementations/styletron-react/Dot.js create mode 100644 packages/benchmarks/src/implementations/styletron-react/Provider.js create mode 100644 packages/benchmarks/src/implementations/styletron-react/View.js rename packages/benchmarks/src/implementations/{styletron => styletron-react}/index.js (71%) delete mode 100644 packages/benchmarks/src/implementations/styletron/Box.js delete mode 100644 packages/benchmarks/src/implementations/styletron/Dot.js delete mode 100644 packages/benchmarks/src/implementations/styletron/View.js diff --git a/packages/benchmarks/README.md b/packages/benchmarks/README.md index 9eaa8293..20ac6c9b 100644 --- a/packages/benchmarks/README.md +++ b/packages/benchmarks/README.md @@ -4,48 +4,55 @@ To run these benchmarks: ``` yarn benchmark +open ./packages/benchmarks/index.html ``` -To run benchmarks for individual implementations append `?,` to the -URL, e.g., `?css-modules,react-native-web`. +Develop against these benchmarks: + +``` +yarn compile --watch +yarn benchmark --watch +``` ## Notes -These benchmarks are crude approximations of extreme cases that libraries may +These benchmarks are approximations of extreme cases that libraries may encounter. The deep and wide tree cases look at the performance of mounting and -rendering large trees of styled elements. The Triangle case looks at the +rendering large trees of styled elements. The dynamic case looks at the performance of repeated style updates to a large mounted tree. Some libraries must inject new styles for each "dynamic style", whereas others may not. Libraries without support for dynamic styles (i.e., they rely on user-authored -inline styles) do not include the `SierpinskiTriangle` benchmark. +inline styles) do not include a corresponding benchmark. The components used in the render benchmarks are simple enough to be implemented by multiple UI or style libraries. The benchmark implementations and the features of the style libraries are _only approximately equivalent in functionality_. +No benchmark will run for more than 20 seconds. + ## Results Typical render timings*: mean ± two standard deviations. -| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) | +| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Update tree (ms) | | :--- | ---: | ---: | ---: | -| `react-native-web@0.2.2` | `89.67` `±28.51` | `167.46` `±27.03` | `65.40` `±19.50` | -| `css-modules` | `77.42` `±45.50` | `141.44` `±33.96` | - | -| `inline-styles` | `236.25` `±95.57` | `477.01` `±88.30` | `40.95` `±23.53` | +| `css-modules` | `15.23` `±04.31` | `21.27` `±07.03` | - | +| `react-native-web@0.3.1` | `17.52` `±04.44` | `24.14` `±04.39` | `15.03` `±02.22` | +| `inline-styles` | `50.06` `±06.70` | `76.38` `±09.58` | `06.43` `±02.02` | Other libraries -| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) | +| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Update tree (ms) | | :--- | ---: | ---: | ---: | -| `styletron@3.0.0-rc.5` | `83.53` `±33.55` | `153.12` `±39.13` | `56.47` `±24.22` | -| `aphrodite@1.2.5` | `88.23` `±31.22` | `164.03` `±34.70` | - | -| `glamor@2.20.40` | `110.09` `±34.20` | `182.06` `±50.39` | ‡ | -| `emotion@8.0.12` | `103.44` `±32.12` | `204.45` `±41.00` | `110.28` `±26.94` | -| `react-jss@8.2.0` | `136.17` `±59.23` | `270.51` `±69.20` | - | -| `styled-components@2.3.2` | `217.57` `±51.90` | `437.57` `±65.74` | `76.99` `±41.79` | -| `reactxp@0.46.6` | `240.88` `±79.82` | `467.32` `±74.42` | `70.95` `±32.90`| -| `radium@0.19.6` | `400.19` `±94.58` | `816.59` `±91.10` | `71.13` `±27.22` | +| `aphrodite@1.2.5` | `17.27` `±05.96` | `24.89` `±08.36` | - | +| `glamor@2.20.40` | `21.59` `±05.38` | `27.93` `±07.56` | ‡ | +| `emotion@8.0.12` | `21.07` `±04.16` | `31.40` `±09.40` | ‡ `19.80` `±13.56` | +| `styletron-react@3.0.3` | `23.55` `±05.14` | `34.26` `±07.58` | `10.39` `±02.94` | +| `react-jss@8.2.1` | `27.31` `±07.87` | `40.74` `±10.67` | - | +| `styled-components@2.4.0` | `43.89` `±06.99` | `63.26` `±09.02` | `16.17` `±03.71` | +| `reactxp@0.51.0-alpha.9` | `51.86` `±07.21` | `78.80` `±11.85` | `15.04` `±03.92` | +| `radium@0.21.0` | `101.06` `±13.00` | `144.46` `±16.94` | `17.44` `±03.59` | These results indicate that render times when using `react-native-web`, `css-modules`, `aphrodite`, and `styletron` are roughly equivalent and @@ -53,4 +60,4 @@ significantly faster than alternatives. *MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3. Google Chrome 62. -‡Glamor essentially crashes the browser tab. +‡Glamor essentially crashes the browser tab. Emotion gets slower every iteration. diff --git a/packages/benchmarks/index.html b/packages/benchmarks/index.html index 74abe613..0c0059ca 100644 --- a/packages/benchmarks/index.html +++ b/packages/benchmarks/index.html @@ -3,6 +3,11 @@ Performance tests + +
diff --git a/packages/benchmarks/package.json b/packages/benchmarks/package.json index a77ae85a..cdd196a3 100644 --- a/packages/benchmarks/package.json +++ b/packages/benchmarks/package.json @@ -3,31 +3,30 @@ "name": "benchmarks", "version": "0.3.1", "scripts": { - "benchmark": "webpack --config ./webpack.config.js && open index.html" + "benchmark": "webpack --config ./webpack.config.js" }, "dependencies": { - "aphrodite": "^1.2.5", - "babel-polyfill": "^6.26.0", + "aphrodite": "1.2.5", "classnames": "^2.2.5", "d3-scale-chromatic": "^1.1.1", - "emotion": "^8.0.12", - "glamor": "^2.20.40", - "marky": "^1.2.0", - "radium": "^0.19.6", + "emotion": "8.0.12", + "glamor": "2.20.40", + "radium": "0.21.0", "react": "^16.2.0", + "react-component-benchmark": "^0.0.4", "react-dom": "^16.2.0", - "react-jss": "^8.2.0", - "react-native-web": "^0.3.1", - "reactxp": "^0.46.6", - "styled-components": "^2.3.2", - "styletron-client": "^3.0.0-rc.5", - "styletron-utils": "^3.0.0-rc.3" + "react-jss": "8.2.1", + "react-native-web": "0.3.1", + "reactxp": "0.51.0-alpha.9", + "styled-components": "2.4.0", + "styletron-client": "3.0.2", + "styletron-react": "3.0.3" }, "devDependencies": { "babel-plugin-react-native-web": "^0.3.1", - "css-loader": "^0.28.7", + "css-loader": "^0.28.9", "style-loader": "^0.19.1", "webpack": "^3.10.0", - "webpack-bundle-analyzer": "^2.9.1" + "webpack-bundle-analyzer": "^2.9.2" } } diff --git a/packages/benchmarks/src/app/App.js b/packages/benchmarks/src/app/App.js new file mode 100644 index 00000000..8a95af78 --- /dev/null +++ b/packages/benchmarks/src/app/App.js @@ -0,0 +1,293 @@ +/* eslint-disable react/prop-types */ + +import Benchmark from './Benchmark'; +import { Picker, StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native'; +import React, { Component } from 'react'; +import Button from './Button'; +import { IconClear, IconEye } from './Icons'; +import ReportCard from './ReportCard'; +import Text from './Text'; +import Layout from './Layout'; +import { colors } from './theme'; + +const Overlay = () => ; + +export default class App extends Component { + static displayName = '@app/App'; + + constructor(props, context) { + super(props, context); + const currentBenchmarkName = Object.keys(props.tests)[0]; + this.state = { + currentBenchmarkName, + currentLibraryName: 'react-native-web', + status: 'idle', + results: [] + }; + } + + render() { + const { tests } = this.props; + const { currentBenchmarkName, status, currentLibraryName, results } = this.state; + const currentImplementation = tests[currentBenchmarkName][currentLibraryName]; + const { Component, Provider, getComponentProps, sampleCount } = currentImplementation; + + return ( + + + + Library + {currentLibraryName} + + + {Object.keys(tests[currentBenchmarkName]).map(libraryName => ( + + ))} + + + + + Benchmark + {currentBenchmarkName} + + {Object.keys(tests).map(test => ( + + ))} + + + + + + +