Benchmark with react-redux-benchmarks
dai-shi opened this issue · 106 comments
Continuing from: #1 (comment)
Note:
The first
Version
column is meaningless.react-redux 5.0.7
is only used in thetree-view
benchmark and not in othertree-view-*
benchmarks.
Re-run all benchmarks with React 16.8.2 and react-hooks-easy-redux v1.0.0.
$ git clone https://github.com/dai-shi/react-redux-benchmarks.git
$ cd react-redux-benchmarks
$ npm install
$ npm run initialize
$ REDUX=5.0.7 BENCHMARK_TRACE=false BENCHMARKS=tree-view:tree-view-rher:tree-view-rher-simple:tree-view-memo:tree-view-dumb npm start
Running benchmark tree-view
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬─────────┬──────────────────────────────────────────────────────────────────────────────┐
│ Version │ Avg FPS │ FPS Values │
├─────────┼─────────┼──────────────────────────────────────────────────────────────────────────────┤
│ 5.0.7 │ 52.08 │ 1,56,48,53,55,43,45,53,50,56,53,55,46,52,53,50,53,55,56,49,53,54,56,52,51,55 │
└─────────┴─────────┴──────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬─────────┬───────────────────────────────────────────────────────────────────────────────────────┐
│ Version │ Avg FPS │ FPS Values │
├─────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ 5.0.7 │ 38.86 │ 1,36,48,27,43,45,42,33,37,29,28,43,37,44,41,47,33,44,41,29,40,46,32,45,34,43,35,48,38 │
└─────────┴─────────┴───────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher-simple
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher-simple:
┌─────────┬─────────┬────────────────────────────────────────────────────────────────────────────────────┐
│ Version │ Avg FPS │ FPS Values │
├─────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ 5.0.7 │ 51.22 │ 1,57,47,53,55,43,39,51,50,53,52,57,44,56,49,46,49,56,53,58,49,56,51,52,56,52,49,50 │
└─────────┴─────────┴────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-memo
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-memo:
┌─────────┬─────────┬──────────────────────────────────────────────────────────────────────────────────────────┐
│ Version │ Avg FPS │ FPS Values │
├─────────┼─────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ 5.0.7 │ 47.66 │ 1,44,52,43,52,50,43,44,42,49,39,52,50,47,48,50,40,56,41,51,44,41,54,44,52,55,51,52,55,41 │
└─────────┴─────────┴──────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-dumb
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-dumb:
┌─────────┬─────────┬─────────────────────────────────────────────────────────────────────────────────┐
│ Version │ Avg FPS │ FPS Values │
├─────────┼─────────┼─────────────────────────────────────────────────────────────────────────────────┤
│ 5.0.7 │ 29.27 │ 1,23,29,30,25,29,34,31,34,29,22,27,24,23,31,25,26,33,34,32,35,27,38,34,19,29,38 │
└─────────┴─────────┴─────────────────────────────────────────────────────────────────────────────────┘
-rher
: useReduxState in react-hooks-easy-redux
-rher-simple
: useReduxStateSimple in react-hooks-easy-redux
Updated react-hooks-easy-redux v1.1.1.
Also added redux-react-hook (-rrh
) as a benchmark.
$ git clone https://github.com/dai-shi/react-redux-benchmarks.git
$ cd react-redux-benchmarks
$ npm install
$ npm run initialize
$ REDUX=5.0.7 BENCHMARK_TRACE=false BENCHMARKS=tree-view:tree-view-rher:tree-view-rher-simple:tree-view-rrh:tree-view-memo:tree-view-dumb npm start
Running benchmark tree-view
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ FPS Values │
├─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ 51.89 │ 1,55,53,52,55,45,49,48,47,54,53,52,46,54,55,54,49,51,52,53,56,55,50,53,52,55,54,49 │
└─────────┴────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬───────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ FPS Values │
├─────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ 39.61 │ 1,35,45,31,40,45,43,46,33,40,33,22,45,36,48,41,51,34,43,42,30,46,48,33,38,40,33,45,43 │
└─────────┴───────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher-simple
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher-simple:
┌─────────┬──────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ FPS Values │
├─────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ 51.97 │ 1,53,45,50,59,47,49,45,49,56,55,54,51,52,54,53,50,53,52,54,60,53,56,46,54,49,53,55,52,48 │
└─────────┴──────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rrh
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rrh:
┌─────────┬─────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ FPS Values │
├─────────┼─────────────────────────────────────────────────────────────────────────────────┤
│ 52.81 │ 1,58,43,56,53,49,44,54,55,53,47,57,53,50,49,58,52,57,56,57,51,52,56,55,53,52,53 │
└─────────┴─────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-memo
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-memo:
┌─────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ FPS Values │
├─────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 49.12 │ 39,54,42,52,49,46,43,47,48,53,47,51,53,46,52,46,52,43,47,53,47,56,51,52,47,51,49 │
└─────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-dumb
react-redux version: 5.0.7
Checking max FPS... (30 seconds)
Results for benchmark tree-view-dumb:
┌─────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ FPS Values │
├─────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 29.52 │ 22,26,34,28,24,25,29,32,30,29,23,24,37,26,24,33,29,27,33,31,35,34,28,38,30,29,30,29 │
└─────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Average FPS in summary:
react-redux 5.0.7: 51.89
react-hooks-easy-redux v1.1.1 useReduxState
: 39.61
react-hooks-easy-redux v1.1.1 useReduxStateSimple
: 51.97
redux-react-hook v3.1.0: 52.81
bare useContext+useMemo: 49.12
It might be interesting to try with new commits in react-redux: reduxjs/react-redux#1177 (comment)
Also might want to merge down from the latest version of the benchmarks repo - I've updated it to capture initial mount times and subsequent average render times.
Merged down from the upstream of the benchmarks repo.
Re-running tree-view-*
:
$ git clone https://github.com/dai-shi/react-redux-benchmarks.git
$ cd react-redux-benchmarks
$ npm install
$ npm run initialize
$ REDUX=5.1.1 BENCHMARK_TRACE=false BENCHMARKS=tree-view:tree-view-rher:tree-view-rher-simple:tree-view-rrh:tree-view-memo:tree-view-dumb npm start
Running benchmark tree-view
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 46.61 │ 954.7, 0.2 │ 49,42,49,42,35,40,43,48,56,48,54,43,54,50,47,48,51,48,52,46,45,43,44,41,47,43,51,51 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ 36.14 │ 1134.2, 3.2 │ 43,41,11,37,41,42,45,33,31,36,32,9,37,42,39,46,40,44,30,34,51,34,33,30,49,34,31,37,37 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher-simple
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher-simple:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ 47.46 │ 857.3, 1.4 │ 53,29,49,53,41,47,38,49,48,54,46,54,42,51,43,47,49,54,52,49,44,48,49,40,55,55 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rrh
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rrh:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 48.59 │ 834.4, 1.1 │ 38,51,54,44,46,44,46,52,53,49,42,55,35,51,49,41,44,52,57,54,51,49,47,54,52,46,51,51 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-memo
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-memo:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 35.09 │ 518.2, 16.5 │ 42,35,30,35,38,36,25,29,34,30,24,36,35,41,44,35,40,32,38,40,25,36,42,38,30,46,46 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-dumb
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-dumb:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ 17.66 │ 516.7, 87.0 │ 22,15,21,20,16,17,16,18,14,22,19,15,17,21,17,13,18,15,18,17,18,11,21,19,16,16 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────────────────┘
react-hooks-easy-redux v1.1.1:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 39.13 │ 870.2, 2.1 │ 45,29,41,46,43,46,33,37,20,37,36,44,46,39,43,40,41,44,30,42,51,30,38,41,35,41,39,39 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
react-hooks-easy-redux 23e2c5a -> v1.2.0:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 46.29 │ 690.0, 1.6 │ 44,28,48,53,50,39,46,29,50,48,51,50,45,53,40,48,49,41,46,48,46,50,54,48,50,51,46,46 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Rerun all benchmarks with react-hooks-easy-redux v1.2.0:
$ git clone https://github.com/dai-shi/react-redux-benchmarks.git
$ cd react-redux-benchmarks
$ npm install
$ npm run initialize
$ REDUX=5.1.1 BENCHMARK_TRACE=false BENCHMARKS=tree-view:tree-view-rher:tree-view-rher-simple:tree-view-rrh:tree-view-memo:tree-view-dumb npm start
Running benchmark tree-view
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 51.47 │ 593.7, 0.2 │ 51,50,53,45,49,50,53,52,53,46,53,54,51,49,53,51,53,55,53,54,49,50,53,55,54,48,48 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 46.25 │ 682.9, 1.5 │ 46,37,34,53,48,42,45,31,48,53,51,44,48,42,48,50,39,51,42,46,49,45,53,56,45,48,56,56 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher-simple
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher-simple:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 49.57 │ 505.9, 1.0 │ 40,50,56,47,46,41,53,49,56,43,54,43,54,49,43,50,54,48,57,54,51,54,47,46,44,54,56,48,48 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rrh
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rrh:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 49.57 │ 627.7, 1.1 │ 42,49,55,46,40,51,46,55,50,54,48,54,49,45,50,48,53,54,51,55,51,43,48,54,51,47,51,47,47 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-memo
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-memo:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 37.15 │ 483.7, 14.7 │ 44,39,32,36,40,38,39,28,37,33,29,38,37,35,40,43,35,36,37,51,36,38,37,45,29,38,34,33,33 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-dumb
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-dumb:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 17.72 │ 576.4, 81.5 │ 19,16,21,17,13,17,21,14,19,21,19,21,18,17,11,16,21,20,16,21,16,20,11,17,17 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
Benchmark Summary for various React/Redux libraries:
Avg FPS | Render (Mount) | Render (Avg) | |
---|---|---|---|
official react-redux v5.1.1 | 51.47 | 593.7 | 0.2 |
react-hooks-easy-redux v1.2.0 | 46.25 | 682.9 | 1.5 |
redux-react-hook v3.1.0 | 49.57 | 627.7 | 1.1 |
Wanna try tossing the experimental v7 alpha in there too?
Well, it's not very surprising.
$ BENCHMARK_TRACE=false BENCHMARKS=tree-view npm start
Running benchmark tree-view
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
react-redux version: 6.0.0
Checking max FPS... (30 seconds)
react-redux version: 7.0.0-alpha.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 47.85 │ 706.7, 0.2 │ 48,46,52,44,43,42,48,50,47,51,43,54,48,46,47,49,53,49,46,47,45,50,52,48,48 │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 32.80 │ 514.3, 18.1 │ 35,34,31,32,34,36,33,30,32,31,30,34,31,36,33,34,31,35,34,27,35,32,35,29,31,32,32 │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 48.40 │ 605.6, 0.2 │ 47,55,43,45,48,52,55,47,51,46,50,47,44,47,50,54,47,50,49,46,43,50,51,50,48,48 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
$ REDUX=5.1.1 BENCHMARK_TRACE=false BENCHMARKS=tree-view-rher:tree-view-rher-simple:tree-view-rrh npm start
Running benchmark tree-view-rher
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 42.16 │ 648.5, 1.6 │ 36,31,27,48,49,38,40,35,44,46,44,43,46,40,43,44,40,38,44,41,47,43,51,50,42,46,46 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher-simple
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher-simple:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 46.24 │ 495.2, 1.1 │ 38,46,54,42,35,47,49,50,52,40,50,47,46,42,45,51,47,53,47,51,48,45,48,35,49,51,51 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rrh
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rrh:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 47.95 │ 469.2, 1.0 │ 38,49,53,43,44,40,49,54,50,48,44,50,45,50,48,50,48,55,52,51,52,45,46,43,51,44,51,51 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Merged upstream/master which includes reduxjs/react-redux-benchmarks#19.
Rerun benchmarks with react-hooks-easy-redux v1.3.0:
$ git clone https://github.com/dai-shi/react-redux-benchmarks.git
$ cd react-redux-benchmarks
$ npm install
$ npm run initialize
$ BENCHMARK_TRACE=false BENCHMARKS=tree-view npm start
$ REDUX=5.1.1 BENCHMARK_TRACE=false BENCHMARKS=tree-view-rher:tree-view-rher-simple:tree-view-rrh npm start
Running benchmark tree-view
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
react-redux version: 6.0.0
Checking max FPS... (30 seconds)
react-redux version: 7.0.0.alpha-3
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 51.46 │ 757.5, 0.2 │ 50,53,43,50,49,48,53,54,53,48,52,53,54,49,51,53,55,53,54,51,50,55,51,49,49 │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 40.11 │ 560.5, 18.2 │ 42,39,36,40,49,41,37,34,37,38,42,37,45,46,41,39,38,49,31,41,45,37,38,45,37,44,35,35 │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 51.62 │ 686.8, 0.2 │ 51,54,47,48,51,49,53,52,53,45,55,53,48,50,52,49,55,53,54,50,51,54,55,53,54,52,52 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 45.72 │ 726.2, 1.6 │ 43,35,34,53,51,42,43,32,45,44,52,51,47,50,46,45,51,36,50,44,41,49,48,50,57,43,49,51,51 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher-simple
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher-simple:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 50.01 │ 554.0, 1.2 │ 41,51,58,45,49,43,48,53,54,46,54,47,53,47,48,50,51,54,53,54,56,42,51,37,56,54,53,53 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rrh
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rrh:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 50.83 │ 507.2, 1.1 │ 42,53,54,45,42,52,50,54,52,49,54,50,48,52,57,51,57,50,49,46,57,54,53,47,51,50,50 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
I converted deeptree for react-hooks-easy-redux.
The result is below. This is very tough. @markerikson
I might have made a mistake when converting it, but we don't have getDerivedStateFromProps
for hooks anyway. That may or may not be a reason.
Running benchmark deeptree
react-redux version: 5.1.1
Checking max FPS... (30 seconds)
react-redux version: 6.0.0
Checking max FPS... (30 seconds)
react-redux version: 7.0.0.alpha-3
Checking max FPS... (30 seconds)
Results for benchmark deeptree:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────┤
│ 21.99 │ 106.0, 0.1 │ 20,22,23,22,24,23,21,23,22,23,25,26,25,23,24,22,19,17,18,18 │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────┤
│ 20.60 │ 130.0, 2.6 │ 19,20,21,19,20,21,23,21,20,21,23,22,21,20,22,18,17,17 │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────┤
│ 40.32 │ 167.6, 1.4 │ 46,55,51,50,52,50,52,50,53,54,53,56,50,47,31,30,31,30,29,21,22,22 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────┘
Running benchmark deeptree-rher
react-redux version: *****
Checking max FPS... (30 seconds)
Results for benchmark deeptree-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────┤
│ 18.31 │ 88.6, 0.1 │ 18,17,14,17,18,17,18,19,18,20,19,18,19,20,19,18,20,16,17,17 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────┘
getDerivedStateFromProps
Oops, Pair.jsx is not used at all...
@dai-shi : yeah, there's a bunch of junk left over in some of those benchmarks that I need to clean up :)
Latest benchmark results
react-redux 5.1.1
Results for benchmark tree-view:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────┤
│ 51.67 │ 575.0, 0.2 │ 49,53,48,47,50,48,54,52,54,49,50,55,50,53,55,54,49,51,56,52,53,49,49 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────┘
Results for benchmark deeptree:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────┤
│ 22.37 │ 112.1, 0.1 │ 20,23,24,22,21,22,23,25,23,24,23,25,24,26,23,25,26,18,17,17 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────┘
react-redux 7.0.0.beta-0
Results for benchmark tree-view:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ 52.19 │ 653.0, 0.2 │ 52,54,47,52,50,54,55,47,53,54,50,49,55,53,56,51,56,55,49,51,55,54,52,51,49,49 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────────────────┘
Results for benchmark deeptree:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 40.62 │ 121.1, 1.4 │ 50,51,53,50,51,48,50,53,51,52,50,53,54,53,49,31,30,32,31,30,23,22,21,21 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘
react-hooks-easy-redux 1.4.0
Results for benchmark tree-view-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 47.45 │ 677.8, 1.5 │ 51,29,47,56,45,46,36,49,45,51,52,47,54,42,49,50,43,44,45,53,49,50,56,47,52,44,43,43 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Results for benchmark deeptree-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 39.06 │ 99.3, 0.8 │ 48,49,42,46,48,45,47,51,47,49,45,51,50,31,27,28,29,27,28,27,29,28,29,21,21 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
@dai-shi : I'd encourage you to try to get the other benchmarks working too for comparison
setup
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
npm install
npm run initialize
REDUX=7.0.0.beta-0 BENCHMARK_TRACE=false BENCHMARKS=tree-view:tree-view-rher:deeptree:deeptree-rher:twitter-lite:twitter-lite-rher npm start
results
tree-view (react-redux 7.0.0.beta-0)
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ 52.25 │ 817.5, 0.2 │ 51,55,45,48,52,50,53,55,56,47,54,52,49,52,53,56,51,56,55,51,53,56,54,51,55,55 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────────────────┘
tree-view (react-hooks-easy-redux 1.6.0)
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 47.68 │ 658.4, 1.5 │ 51,27,48,54,45,46,35,50,43,53,54,44,54,41,49,48,42,44,48,54,45,56,55,51,55,51,43,50,50 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
deeptree (react-redux 7.0.0.beta-0)
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 40.34 │ 108.1, 1.4 │ 51,53,52,54,51,50,52,47,54,52,54,52,44,31,25,30,27,32,31,32,24,23,21,22,22 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
deeptree (react-hooks-easy-redux 1.6.0)
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 39.10 │ 87.3, 0.9 │ 48,53,47,49,50,49,46,47,48,49,51,48,52,30,29,30,29,33,29,31,29,21,20,21,21 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
twitter-lite (react-redux 7.0.0.beta-0)
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────┤
│ 48.95 │ 3.9, 0.3 │ 59,60,59,60,58,60,58,60,55,60,56,51,47,44,38,39,37,31,33,31,28,30,30 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────┘
twitter-lite (react-hooks-easy-redux 1.6.0) with React.memo (is it fair?)
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────┤
│ 56.22 │ 3.1, 1.1 │ 59,60,59,60,59,60,59,60,52,56,54,60,58,56,59,58,57,56,51,49,44,31,31 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────┘
Very compelling results. It almost feels like in real world usage with complex apps, using @theKashey's proxyequal will be faster than manual specification of mapStateToProps
.
Mount and Avg are millseconds (for initial component mount and average re-render), correct?
Mount and Avg are millseconds (for initial component mount and average re-render), correct?
Guess so. @markerikson ?
in real world usage with complex apps
We really want to run benchmarks with real-world examples.
be faster than manual specification of mapStateToProps
That's my expectation too. 😉
(Technically, the manual optimization should always win, but not everyone are experts for optimization.)
Wow. This is amazing! Magic out of the box!
Technically, the manual optimization should always win, but not everyone are experts for optimization.
That was a reason I've started all this. I had too many cases when manual memoization, selection and optimization would be too hard, even with years of experience.
After a year fully into it(proxyqual was released exactly a year ago) I don't see any problem with it for myself, but I remember how I struggle, and I could see the same sadness in the eyes of mine friends.
Anyway - I remember that a few my applications(displaying biiiig tables) got slower after adding beautiful-react-redux
(proxyequal "on" mapStateToProp). And not the tracking was a problem, but shouldInstrument
called before Proxy
creation.
I should double check it.
Yes, the times in that "render" column are in milliseconds.
FWIW, long-term I'm totally interested in the idea of "auto-magic" behavior in React-Redux itself. However, my own focus for now has to simply be on making sure that the current implementation works as needed (and as expected).
So, please keep on experimenting with this stuff, and perhaps if it works out, we may be able to adopt some of your work.
like :mindblow: slices :mindblow:
shouldInstrument
I was just looking at it last weekend. I thought the use of Map is not necessary, and tried eliminating it.
However, it didn't make a big difference in the current benchmark scenarios (namely, tree-view and deeptree at that point),
probably due to the fact the number of components doesn't increase in those scenarios.
@epeli You might want to check some benchmark results here. I'm now converting another scenario.
Newly converted benchmark: stockticker
how to run
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
npm install
npm run initialize
REDUX=7.0.0.beta-0 BENCHMARK_TRACE=false BENCHMARKS=stockticker:stockticker-rher npm start
react-redux 7.0.0.beta-0
Results for benchmark stockticker:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 51.48 │ 330.0, 0.3 │ 48,44,40,47,54,52,53,51,54,55,53,52,54,52,54,53,54,53,51,52,51,52,53,55,55 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
react-hooks-easy-redux 1.6.0
Results for benchmark stockticker-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────┤
│ 27.00 │ 277.5, 0.3 │ 26,29,28,26,29,28,29,28,29,27,28,27,28,26,25,26,27,26,27,27 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────┘
This is tough, maybe because of getDerivedStateFromProps(), which can't be implemented in hooks. I mimicked with useMemo. Is there any better implementation?
Interestingly, @epeli was saying a similar issue in reduxjs/react-redux#1179 (comment)
We could only do like this:
function HasComments(props) {
const state = useReduxState();
const hasComments = state.posts[props.postId].comments.length > 0;
return useMemo(() => hasComments ? "yes" : "no", [hasComments]);
}
We can't bail out rendering of this component, but child components are ok.
I remember @markerikson saying somewhere that always when React gets involved in state changes things get very slow even if you can bail out it like that from child components.
But this can be a trade off which is worth it.
Also because react-hooks-easy-redux accesses state only during the render phase I think you can safely combine it with the official react-redux and optimize those cases with connect() should the need for it arise.
Random reads from a state. Remember it?
// random read and random operation
const hasComments = state.posts[props.postId].comments.length > 0; // 👎
// absolutely the same
const hasComments = getGetCommentsCount(state, props) >0 ; // 👎
// no random here
const hasComments = getHasComments(state, props); // 👍
"selectors". You will know that you have used a selector, and it has used propsId
and state.props
, and nothing else was used outside selector - so you will know when to rerun it, and still result will be the same, and you will bailout without triggering an update.
wrap reselect
or any other (known) memoization library with a specialized tracker - piece a cake.
You will know that you have used a selector, and it has used propsId and state.props, and nothing else was used outside selector
I understand how it should work, but it doesn't sound to me like a piece of cake.
Is it possible to tell that some properties are used in reselect only from proxy handler?
Oh, that was the discussion there: #1 (comment)
Here's the last one.
Benchmark: forms
how to run
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
npm install
npm run initialize
REDUX=7.0.0.beta-0 BENCHMARK_TRACE=false BENCHMARKS=forms:forms-rher npm start
react-redux 7.0.0-beta-0
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 56.62 │ 1090.0, 0.3 │ 56,58,54,57,56,57,56,57,58,57,58,59,57,54,57,56,58,55,58,56,57,55,56,56 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘
react-hooks-easy-redux 1.6.0
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 57.09 │ 1133.0, 0.3 │ 58,57,56,57,58,57,58,56,60,57,58,56,57,56,57,55,56,58,57,56,56 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
🚀
Here's the last one.
Oh, there was a new commit: reduxjs/react-redux-benchmarks#20
OK, merged upstream. I converted that benchmark too. Cleaned up my repo. Let me run everything.
All Benchmarks
How to run
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
git checkout ed756362339f318f5fa1544728011296ae6c1cac
npm install
rm sources/*/yarn.lock
npm run initialize
REDUX=7.0.0-beta.0 BENCHMARK_TRACE=false BENCHMARKS=tree-view:tree-view-rher:deeptree:deeptree-rher:twitter-lite:twitter-lite-rher:stockticker:stockticker-rher:forms:forms-rher:deeptree-nested:deeptree-nested-rher npm start
Results
Running benchmark tree-view
Checking max FPS... (30 seconds)
Results for benchmark tree-view:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 51.49 │ 745.6, 0.2 │ 52,55,46,48,51,50,52,54,55,46,54,51,45,49,55,52,57,48,57,51,50,54,53,56,46,53,53 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-rher
Checking max FPS... (30 seconds)
Results for benchmark tree-view-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 47.94 │ 698.6, 2.0 │ 51,30,48,56,45,49,35,47,49,54,50,49,50,42,55,37,54,47,52,49,52,50,51,55,45,50,42,42 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark deeptree
Checking max FPS... (30 seconds)
Results for benchmark deeptree:
┌─────────┬──────────────┬────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────┤
│ 39.14 │ 162.4, 0.7 │ 48,50,51,47,46,49,46,45,49,38,29,30,29,26,20,21,21 │
└─────────┴──────────────┴────────────────────────────────────────────────────┘
Running benchmark deeptree-rher
Checking max FPS... (30 seconds)
Results for benchmark deeptree-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 38.06 │ 94.4, 0.8 │ 46,44,46,49,46,47,48,47,45,46,48,44,45,44,46,29,28,30,27,26,28,27,19,19 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘
Running benchmark twitter-lite
Checking max FPS... (30 seconds)
Results for benchmark twitter-lite:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────┤
│ 49.01 │ 3.6, 0.2 │ 60,59,60,59,60,59,57,51,60,56,50,48,45,37,38,36,34,32,30,31,28,27,27 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────┘
Running benchmark twitter-lite-rher
Checking max FPS... (30 seconds)
Results for benchmark twitter-lite-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 57.44 │ 3.6, 1.1 │ 59,60,59,60,59,60,59,60,59,60,54,60,59,60,59,54,57,49,46,45,45 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark stockticker
Checking max FPS... (30 seconds)
Results for benchmark stockticker:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ 53.50 │ 266.5, 0.3 │ 51,57,56,53,56,44,55,54,56,55,53,56,55,52,56,54,53,52,54,51,56,51,52,51,52,52 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────────────────┘
Running benchmark stockticker-rher
Checking max FPS... (30 seconds)
Results for benchmark stockticker-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────┤
│ 26.30 │ 272.3, 0.3 │ 25,27,29,28,26,27,26,27,26,25,27,25,26,25,26,27,25,27,25,25 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────┘
Running benchmark forms
Checking max FPS... (30 seconds)
Results for benchmark forms:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 56.79 │ 1066.4, 0.3 │ 56,57,56,58,57,58,60,57,54,57,58,56,57,55,56,54,58,55,58,56,56 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark forms-rher
Checking max FPS... (30 seconds)
Results for benchmark forms-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 56.03 │ 1075.4, 0.3 │ 56,57,58,57,58,57,58,57,60,58,57,58,56,54,56,55,56,49,47,53,53 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark deeptree-nested
Checking max FPS... (30 seconds)
Results for benchmark deeptree-nested:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 50.75 │ 174.2, 0.5 │ 51,58,56,58,57,56,55,56,58,57,54,55,49,43,40,45,38,46,43,45,41,38,43,43 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘
Running benchmark deeptree-nested-rher
Checking max FPS... (30 seconds)
Results for benchmark deeptree-nested-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 50.98 │ 146.2, 0.6 │ 58,59,56,59,56,59,55,57,56,54,57,55,56,53,49,47,42,43,42,47,42,44,40,35,35 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
Notes
- The version of react-redux is 7.0.0-beta.0
- The version of react-hooks-easy-redux is 1.6.0
-rher
suffix means variants forreact-hooks-easy-redux
- This time, I removed
yarn.lock
to use the same react version. (There was a difference previously.) - twitter-lite-rher uses React.memo for optimization, so it might not be fair
Summary in table
react-redux 7.0.0-beta.0 | react-hooks-easy-redux 1.6.0 | |
---|---|---|
tree-view | 51.49 FPS | 47.94 FPS |
deeptree | 39.14 FPS | 38.06 FPS |
twitter-lite | 49.01 FPS | 57.44 FPS |
stockticker | 53.50 FPS | 26.30 FPS |
forms | 56.79 FPS | 56.03 FPS |
deeptree-nested | 50.75 FPS | 50.98 FPS |
So only one case where react-redux
it better. The question is why.
cuz proxies rock and aren't slow, plus the enormous amount of extra stuff the react-redux lib does for mapState/Dispatch/etc
to work. Basically facilitating the manual approach requires cycles we must not dismiss. But you're right, it's something specific that points to the elegance of your underlying solution. Access checking (the way you did it) can simply determine the necessary answers faster than manual comparison. It could also just be because react-redux supports a lot of other features.
In many cases, yours can probably find the answer far sooner by seeing no usage in fact did occur, whereas react-redux likely has to go farther down the comparison/caching rabbit hole to make the same determinations. That may or may not be what happened in these benchmarks, but I'm willing to bet that happens often in real apps. In short, your amazing proxyequal library can short-circuit sooner (i.e. with less, but better information).
So only one case where react-redux it better. The question is why.
Why socketticker-rher
is slow? Here's the code I think makes difference.
Is this also what you refer as "random reads"?
sources/stockticker/src/Pair.jsx
original code for react-redux:
const mapState = (state, props) => state[props.sliceId][props.pairId];
class Pair extends React.Component {
state = {
direction: "up",
value: this.props.value
};
static getDerivedStateFromProps(props, state) {
if (props.value === state.value) return null;
const direction = props.value > state.value ? "up" : "down";
return {
value: props.value,
direction
};
}
shouldComponentUpdate(nextProps) {
return this.props.value !== nextProps.value;
}
render() {
const { direction } = this.state;
return (
<li className="list-group-item">
<span>{this.props.name}</span>
<span
className={
"pull-right " +
(direction === "up" ? "text-success" : "text-warning")
}
>
<span
className={
"glyphicon " +
(direction === "up"
? "glyphicon-arrow-up"
: "glyphicon-arrow-down")
}
/>
<span>{this.props.value}</span>
</span>
</li>
);
}
}
export default connect(mapState)(Pair);
sources/stockticker-rher/src/Pair.jsx
converted code with hooks for react-hooks-easy-redux:
const Pair = props => {
const state = useReduxState();
const { value, name } = state[props.sliceId][props.pairId];
const prevValue = useRef(null);
useEffect(() => {
prevValue.current = value;
});
const direction = value > prevValue.current ? "up" : "down";
return useMemo(
() => (
<li className="list-group-item">
<span>{name}</span>
<span
className={
"pull-right " +
(direction === "up" ? "text-success" : "text-warning")
}
>
<span
className={
"glyphicon " +
(direction === "up"
? "glyphicon-arrow-up"
: "glyphicon-arrow-down")
}
/>
<span>{value}</span>
</span>
</li>
),
[value]
);
};
so useMemo
is not helping here? Probably cos it's could drop cache in real, thus make rendering slower - https://twitter.com/alexandereardon/status/1108488559881641986
without useMemo
Results for benchmark stockticker-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 27.03 │ 429.8, 0.3 │ 25,29,27,29,28,30,28,27,26,27,26,27,26,25,27,28,26,27,26,25,25 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
with useMemo
Results for benchmark stockticker-rher:
┌─────────┬──────────────┬───────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────┤
│ 27.34 │ 325.3, 0.3 │ 26,29,28,27,29,28,27,29,28,26,27,26,27,28,27,25,24,24 │
└─────────┴──────────────┴───────────────────────────────────────────────────────┘
It doesn't make any big difference. Maybe, the root cause is somewhere else.
so, could you do a baaad thing:
- move "render" our of the component, let direction and value be function arguments.
- memoize it
- update use Effect not to record duplicate values.
// cache size is 4.
const renderFn = memoizerific(4)((value, direction) =>
(
<li className="list-group-item">
<span>{name}</span>
<span
className={
"pull-right " +
(direction === "up" ? "text-success" : "text-warning")
}
>
<span
className={
"glyphicon " +
(direction === "up"
? "glyphicon-arrow-up"
: "glyphicon-arrow-down")
}
/>
<span>{value}</span>
</span>
</li>
));
const Pair = props => {
const state = useReduxState();
const { value, name } = state[props.sliceId][props.pairId];
const prevValue = useRef(null);
useEffect(() => {
prevValue.current = value;
}, [value]);
const direction = value > prevValue.current ? "up" : "down";
return renderFn(value, direction);
};
That should be
- fast
- have effect more rarily
- not create functions every render (like class based component IS doing)
- hardly memoize it
version 2 is to use React.memo, it terms of function creating is actually weak point. Just extract it!
const RenderFn = React.memo(({value, direction}) =>
(
<li className="list-group-item">
<span>{name}</span>
<span
className={
"pull-right " +
(direction === "up" ? "text-success" : "text-warning")
}
>
<span
className={
"glyphicon " +
(direction === "up"
? "glyphicon-arrow-up"
: "glyphicon-arrow-down")
}
/>
<span>{value}</span>
</span>
</li>
));
const Pair = props => {
const state = useReduxState();
const { value, name } = state[props.sliceId][props.pairId];
const prevValue = useRef(null);
useEffect(() => {
prevValue.current = value;
}, [value]);
const direction = value > prevValue.current ? "up" : "down";
return <RenderFn value={value} direction={direction} />
};
naive question: useEffect
is used because of its characteristic that it runs after the component renders (guaranteeing prevValue
is essentially stale at render time)?
stockticker-rher
baseline (useMemo)
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────┤
│ 23.69 │ 352.8, 0.3 │ 22,25,26,25,24,23,24,25,23,24,23,24,23,22,24,23,22,23,22,22 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────┘```
React.memo (we also need to pass name
)
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 23.23 │ 342.1, 0.3 │ 22,25,26,24,23,24,23,25,23,24,25,24,23,24,21,23,19,20,25,24,24 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
React.memo + useEffect(..., [value])
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 23.18 │ 357.0, 0.3 │ 22,25,24,22,23,22,25,26,23,24,23,24,23,22,23,24,22,21,22,21,21 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
useEffect(..., [value]) + memoizerific(4)
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 23.10 │ 340.2, 0.3 │ 22,26,24,22,21,24,23,22,24,23,24,23,19,23,25,24,21,24,22,23,22,24,21,23,23 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
memoizerific(4) only
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 23.17 │ 345.4, 0.3 │ 23,26,24,25,23,24,25,24,22,24,25,24,23,21,20,23,20,21,22,23,23 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
The numbers are not very stable while I run those.
Anyway, we might have a bottleneck somewhere else.
OK, I dug into it a bit deeper. stockticker-rher
.
TL;DR @epeli 's #3 (comment) (the first part) is probably correct. I think this is a trade off.
react-redux: 44 FPS
retrun null (like below): 35 FPS
const Pair = props => {
const state = useReduxState();
const { value, name } = state[props.sliceId][props.pairId];
return null;
};
useMemo: 23 FPS
So, the first cost (44FPS -> 35FPS) is invocation of rendering function.
The second cost (35FPS -> 23FPS) is memoization or something I don't know of.
The first one is the current limitation, that can't be helped at the moment.
But, this is something respond-framework's selectors will help, correct?
The second one seems strange, but all the same for useMemo, React.memo (with second arg too), memoizerific, useRef, or even without anything.
I hope I misunderstand something.
Yes, but I found how selectors should be used with current redux, so it's something that can be worked on with your library as is, and apply to the entire Redux ecosystem, if you want to work on it.
Here's how:
// REQUIREMENT: MUST BE PURE!!! (no props variables from closure!)
const mySelector(state, sliceId, pairId) => {
return state[sliceId][pairId]
}
const Pair = props => {
const { value, name } = useReduxState(mySelector, props.sliceId, props.pairId);
return null
}
and for complete clarity, compare to the following which is wrong:
const Pair = props => {
const deps = [props.sliceId, props.pairId]
const { value, name } = useReduxState(state => {
return state[props.sliceId][props.pairId] // PROBLEMATIC CLOSURE!!
}, deps);
return null
}
@theKashey you agree?
the requirement that the selector is pure solves the problem mentioned by @epeli . You could even define the selector in the Pair
component, so long as you don't close over variables. Obviously putting in the outer scope is a bit faster.
Most importantly, you don't have to define selectors when the store is created (which requires a fork of a redux). So the following is no longer required to start making progress on this:
createStore(reducer, selectors, etc)
Since the selectors are pure, it doesn't matter if you provided them on store creation or if you passed them to useReduxState
within component render. It's the same. 🚀
ps. actually it may be preferable to not define the function with the component for another reason: if it's defined once and imported into multiple components, as part of the caching we can check the reference to this function. In fact we need this if we want to allow for caching multiple usages across multiple components. But without it, I think single usage "memoization" will be fine.
Therefore, moving the function out of the component is a kind of advanced, but natural optimization, performed in userland.
the plan would be to use a combination of @theKashey 's kashe library + proxyequal
to cache potentially multiple usages of mySelectors
(i.e. within multiple components).
The result is a sort of 2 level hierarchical cache that happens with useReduxState
(proxyequal on one level, and kashe on the other).
Basically you can use the same technique you're using for tracking reducer state via proxyequal, plus you use kashe to see if the arguments (eg: sliceId, pairId) have been used before and therefore if you have a value in cache.
it's more sounds like useReduxSelector
hook. Which is createSelector
+ useMemo
+ magic
.
sure. bottom line: the interfaces people use in Reactlandia suck. it's all residue from the "js fatigue" era. if we want everything low level, fine. or if we want to make it easy for people to fall into the "pit of success" we begin to couple opinionated interfaces to help them, and move higher up the stack.
so why not make things easier for people and just let users optionally pass selectors to useReduxState
, instead of introduce another function. for us either is fine--it's the same implementation under the hood, and that's the intriguing part no one has yet to solve. ...we all know very well that later many different interfaces, such as Respond's, will pop up :)
useMemo
is only memoized--we want your weakmaps caching so multiple calls across different components can be remembered.
..but i guess a first implementation could focus on the "magic" while using useMemo
to make easy-redux display comparable benchmarks for the stockticker demo.
I just remembered selectors passed here or on store creation--pure or not--still can't bail out. i keep forgetting that. only store subscriptions can "bail out."
react-redux uses a wrapper HOC. They seem to be able to do it with hooks. A year ago I trivially blocked rendering with getDerivedStateFromProps
. The old react-redux 5 HOC did it with neither option. The takeaway is it's easily possible with a wrapper HOC, but not without.
So the Respond interface could easily be powered by a HOC in the backround. Perhaps even using easy-redux within that hoc, but it won't be able to do it as a single component. easy-redux itself is essentially crippled, and I'd be surprised if it was solveable (minus perhaps a hypothetical suspense error-throwing feature resulting in rendering from cache, rather than a spinner, and who knows if thats even possible or going to be possible--it's never clear what suspense features are available yet and which ones arent).
So, the first cost (44FPS -> 35FPS) is invocation of rendering function.
The second cost (35FPS -> 23FPS) is memoization or something I don't know of.
The first one is the current limitation, that can't be helped at the moment.
But, this is something respond-framework's selectors will help, correct?
Yes, cuz our babel plugin can generate any approach with hooks or hocs under the hood (or perhaps both). It's looking like at least HOCs are required.
The second one seems strange, but all the same for useMemo, React.memo (with second arg too), memoizerific, useRef, or even without anything.
Seems strange to me too. I dont fully understand the difference between "one" and "two," but my guess the Respond interface, implemented with a hoc, plus complete implementation of kashe + proxyequal would bring us back up to 44 FPS for one reason or another. Maybe higher.
ps. clearly manual usage of useMemo
returning jsx also isn't that powerful. we just need this component to not render at all. that makes react do far less work, for whatever reasons internal to react.
#1 (comment)
Again, I'm still not yet at the point to understand everything, but it sounds to me, if this is possible, we can track the state usage inside a selector, and then avoid rendering without HoCs.
Or, maybe something like:
function HasComments(props) {
const { state, runSelector } = useReduxStateSpecial();
const hasComments = runSelector(() => state.posts[props.postId].comments.length > 0);
return hasComments ? "yes" : "no";
}
(Just a thought. Posting it without any serious considerations.)
There is a picture in my mind
function HasComments(props) {
const { state, runSelector } = useReduxStateSpecial();
const hasComments = runSelector(() => state.posts[props.postId].comments.length > 0);
// ------------ LINE IN THE SAND -----------------
return hasComments ? "yes" : "no";
}
We should know what was done before render and inside render. I dont know what to do next, this is just a picture in my mind.
Yes, your "LINE IN THE SAND" is uncrossable. In userland (or via shitty babel plugin) you could automate applying useMemo
on the jsx returned, but as the benchmarks showed, that didn't attain the same perf as react-redux can (which uses a HOC for complete bailout).
Now, Suspense is supposed to be based on this:
function HasComments(props) {
const { state, runSelector } = useReduxStateSpecial();
const hasComments = runSelector(() => state.posts[props.postId].comments.length > 0);
throw 'correct-component-already-rendered'
But these features--although they started heavily promoting them a year ago!--are not live. They aren't documented and not supposed to be used, but some of them seem to exist in master. For example, here's an internal implementation of a cache that throws:
https://github.com/facebook/react/blob/master/packages/react-cache/src/ReactCache.js#L159
const resource = {
read(input: I): V {
// react-cache currently doesn't rely on context, but it may in the
// future, so we read anyway to prevent access outside of render.
readContext(CacheContext);
const key = hashInput(input);
const result: Result<V> = accessResult(resource, fetch, input, key);
switch (result.status) {
case Pending: {
const suspender = result.value;
throw suspender; // spinner or cached item will show as a result of this line
}
case Resolved: {
const value = result.value;
return value;
}
case Rejected: {
const error = result.value;
throw error;
}
default:
// Should be unreachable
return (undefined: any);
}
}
by the way, i made a PR to their suspense cache a year ago!: https://github.com/facebook/react/pull/12446/files
So basically, we can do what we all want. But not with just hooks. It has to be with hooks + suspense which isn't live yet.
Lastly, according @epeli this:
runSelector(() => state.posts[props.postId].comments.length > 0)
won't work because it gets props from a closure. Perhaps this too is permitted once we are using the throw suspender
technique from suspense proper.
If it was able to work, it would have to be closer to simply useCallback:
useCallback(() => state.posts[props.postId].comments.length > 0, [props.postId])
except, that isn't the complete picture, and it would need to be our custom--but similar--function because we track both props + the external redux state store. It would be this:
useSelector(() => state.posts[props.postId].comments.length > 0, [props.postId])
and under the hood we use a combination of useCallback
+ what @dai-shi is already doing for reducers. and done. ..but we need to throw
to trigger suspense, and of course we gotta do so in a way that doesnt trigger a spinner here:
<Suspense fallback={Spinner}>
<HasComments postId={postId} />
</Suspense>
When HasComponent
throws it will trigger a spinner. We simply want to just show the previous render of the component. For that we have to be lucky enough to be taught the correct way by the almighty React team. Or go digging in their code, which I don't have time to do cuz I DONT NEED SUSPENSE (OR HOOKS). And either do u. See you in Respondlandia; got some good things coming soon.
In conclusion, if we want to do this, we should start using the experimental suspense features in preparation for this future, or help me with the Respond interface which can use HOCs, while providing the user a better interface than all these options X10 (since we also use Babel). Choice is yours. ...But I'm not begging, I'm just saying we can solve these problems a better way now, in case u guys find time and are super interested.
We don't have to follow the React team in every dead end they take us down, while all they do is build up their internet fame and take 5 years to release what was once known as "fiber." They certainly don't control my destiny. React from 3 years ago is fine with me. I look forward to "time slicing" though, so we can have better animations. But the sad part is that's the best part, the "real suspense" and the absolute hardest part about what they're doing. I have a feeling they may fail and not actually be able to pull it off lol. They might not have understood all the effects this has on the way 3rd party libraries are used, and application code in general--just look at the problems James Long discovered yesterday:
In general, solving common problems like this with useRef
is backwards. Hooks unfortunately is less obvious of an API as they had hoped. They thought they came up with a way for the simple component function primitive to tackle more use cases, while still being super simple. It's looking bad to me. I don't like all these things James Long had to do to make that work. This is just one example. All I'm seeing is workarounds that we're supposed to now consider normal best practices. The primitive is no longer simple. It's now super complex with far too many options. That completely misses the goal of what they were trying to do. Fortunately for them, they're forcing us to dog food their product so they can learn from this and perhaps change it. Until then, I don't have time to waste holding their hand for several years as they figure it out. They're smart. I believe they'll figure it out eventually. I'm almost done with something that has far more figured out this spring/summer.
So my advice is to forget hooks, @dai-shi. I know it's your first popular library., so I don't expect you to stop (and you shouldn't), but reducers without selectors is useless. It's a fun experiment. Real apps need selectors. Specifically selectors that take props as arguments. Period.
So I'm putting my money on it taking until 2020 until they finalize the remaining features of suspense, which revolve around time slicing. Not 2nd quarter 2019 as promised. We're already there.
check this suspense test:
function AsyncText(props) {
const text = props.text;
try {
TextResource.read([props.text, props.ms]);
Scheduler.yieldValue(text);
return text;
} catch (promise) {
if (typeof promise.then === 'function') {
Scheduler.yieldValue(`Suspend! [${text}]`);
} else {
Scheduler.yieldValue(`Error! [${text}]`);
}
// we need to figure out how to throw something that says:
// just render the current component
throw promise;
}
}
it('throws a promise if the requested value is not in the cache', () => {
function App() {
return (
<Suspense fallback={<Text text="Loading..." />}>
<AsyncText ms={100} text="Hi" />
</Suspense>
);
}
ReactTestRenderer.create(<App />, {
unstable_isConcurrent: true,
});
expect(Scheduler).toFlushAndYield(['Suspend! [Hi]', 'Loading...']);
jest.advanceTimersByTime(100);
expect(Scheduler).toHaveYielded(['Promise resolved [Hi]']);
expect(Scheduler).toFlushAndYield(['Hi']);
});
or forget this nonsense. Don't just React, Respond.
Life will find a way 🦖.
For example.... we have decorators 🤔
decorators are hocs. decorators are dead. there's nothing wrong with the hoc approach. its just:
import transparentConnect from 'transparent-redux'
const MyComponent(props) => {
return <button onClick={props.actions.go}>{props.state.foo}</button>
}
export default transparentConnect(MyComponent)
dispatch is pre-bound
do ur proxyequal magic under the hood. i prefer this than using a damn hook. there's literally no gain from the hook, especially in the case of a package like this one, where the whole purpose is to do as much as possible with as little of an API surface as possible.
it's funny we're doing all this just to follow the React team in whatever they do, like lemmings off a cliff; and we're missing the big picture. The Hooks API makes sense for other things, not here. It's totally unnecessary, except for the marketing ability to say: "LOOK WE MADE EVEN BETTER REACT-REDUX THANKS TO HOOKS" and thats essentially a lie. it has nothing to do with hooks.
and FYI. above is the same api as React.memo, eg:
import Transparent from 'transparent-redux'
export default Transparent.connect((props) => {
return <button onClick={props.actions.go}>{props.state.foo}</button>
})
look familiar??
promise u guys, hooks + suspense are gonna be the biggest disaster the React team ever did. they're gonna be making excuses for years to come. they've already started.
...and i'm wrong, it wont work because it assumes the selectors receives (state, props)
, rather than (state, arg1, arg2)
. Even if we required all our selectors to receive a single props object argument, that wouldn't work because different components have different props.
Which means, the way react-redux
does it where the selector's arguments are individually prepared out of props is required to move this work into a parent HOC.
Or suspense has a way to do it with throw
.
Which means suspense is now our only hope lol...It's a good thing I've waited this long to implement this part of the framework--now we have to wait for suspense to be finished.
Relax. One failed benchmark does not count. If we could not solve the problem from our side - probably we should solve the problem?
WHY we have many component and many updates? Just virtualize them! Optimize the root cause!
It's not about the benchmark. It's about proper selectors support. @dai-shi sent me this URL in the past:
I didn't understand its importance then. Now I do. We're going to need more advanced bail-out capabilities from React core.
And here's the trouble they're having with implementing this feature:
https://overreacted.io/why-isnt-x-a-hook/#composition-1
This is not good.
It's not covered by the article, but the only right solution is - only hook which caused an update could bailout.
Not bailout in any case, regardless of the update source. But this requires a little different semantic, and needed only by a few consumers (like redux)
ur 100% correct. I don't understand how Dan missed this obvious thing. They need to change the implementation instead of pretending it's not possible. U should let him know.
Here's likely the best API this library could do today:
import Transparent from 'transparent-redux'
export default Transparent.connect((props) => {
const val = props.state.mySelector(props => [props.bar, props.baz])
return <button onClick={props.actions.go}>{val}</button>
})
// mySelector = (state, bar, baz) => ...
An in the parent HOC, along with a combination of access tracking, we transform the props to bar
and baz
and see if we get the same result or not before we render the child component. If we get the same, we bail.
It's a way for all selectors within the component to tell the HOC what "bits" of props they want.
We will have to pass this function back up to the HOC somehow, similar to how proxyequal tracks what was used on render. It's a shitty side-effect, but stays within the conceptual unit of the parent + child. It should be doable.
There's one last problem: we don't know if the component was rendering props.bar
or props.baz
itself. Our selector may return the same result, but the component may want to display the new value for those props. I guess react-redux always rendered the child too. So basically that means the only optimization we can ever get out of this is not re-running selectors. It is unrelated to completely bailing out render of the child component. Which means there's no point in doing this lol--because the point was to block renders, but we arent even allowed to block renders, as the component will want to render the new props anyway. Therefore we can possibly render a cached return from selectors then. The only way a perf boost could be achieved here is if props.bar/baz
were overwritten in the classic react-redux connect HOC to always be the same value, since it was only mapStateToProps
that needed them. Something like this, which I dont recall ever seeing.
@theKashey look at this:
To summarize the behavior of the component wrapped by connect
with mapStateToProps
to extract data from the store:
(state) => stateProps |
(state, ownProps) => stateProps |
|
---|---|---|
mapStateToProps runs when: |
store state changes |
store state changes or any field of ownProps is different |
component re-renders when: | any field of stateProps is different |
any field of stateProps is different or any field of ownProps is different |
any field of stateProps is different is wrong. It should be the same as the right column. react-redux isn't blocking renders just because mapStateToProps
' return hasnt changed, is it? I've been out of the game for a while, but how is the child supposed to change in response to non-redux props??
If it doesn't re-render, then what I was trying to do with props.state.mySelector(props => [props.bar, props.baz])
would work.
Either there's a bug in the react-redux docs or my plan was correct...confirmed, it's a bug in the docs. What they probably meant is the parent HOC does a little bit less work if ownProps
change but stateProps
do not.
Unfortunately in this case the bug would have been a good thing.
Actually, all in all it's a great thing: because it means we don't have to concern ourselves with our selectors bailing out. It's not their job! It is their job if they don't use props, and we can do that via similar techniques as is done with reducers. But if they use props, there's nothing we ever could do anyway--the component is supposed to re-render.
Regarding the benchmarks, an important experiment is this:
const Pair = props => {
const state = useReduxState();
const { value, name } = state[props.sliceId][props.pairId];
- const prevValue = useRef(null);
- useEffect(() => {
- prevValue.current = value;
- }, [value]);
- const direction = value > prevValue.current ? "up" : "down";
+ const direction = "up";
return renderFn(value, direction);
};
We gotta make sure the refs and effects aren't the cause.
And another thing to try is additional memoization:
function areEqual(prevProps, nextProps) {
return prevProps.value === nextProps.value
}
const Pair = React.memo(props => {
// the goal of React.memo is to prevent this code from running
const state = useReduxState(); // caveat: I'm not sure that state can be used with React.memo
const { value, name } = state[props.sliceId][props.pairId];
const prevValue = useRef(null);
useEffect(() => {
prevValue.current = value;
}, [value]);
const direction = value > prevValue.current ? "up" : "down";
return renderFn(value, direction); // memoized here as before
}, areEqual)
If you look at the original class, it has 3 bailout places:
getDerivedStateFromProps
returning nullshouldComponentUpdate
connect
https://github.com/dai-shi/react-redux-benchmarks/blob/master/sources/stockticker/src/Pair.jsx
Clearly, it's bailing out more. And unless the bottleneck is the new apis with refs + useEffect, something else is triggering renderings besides just the value changes.
We gotta make sure the refs and effects aren't the cause.
baseline
Results for benchmark stockticker-rher:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────┤
│ 13.97 │ 498.4, 0.5 │ 13,15,14,18,12,13,18,14,13,15,12,14,9,14,16,13,15,12,15,16,13,14,12,12 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────┘
const direction = "up";
Results for benchmark stockticker-rher:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 13.92 │ 615.9, 0.5 │ 17,13,16,18,14,15,12,16,12,13,12,14,15,11,14,11,14,11,16,13,16,12,14,14 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘
No difference. (Not that my machine is on battery.)
Here's the other thing to check:
the action ${c.UPDATE_PAIR}_${sliceId}
should not trigger a re-render in the parent Slice
:
+const Slice = ({ idx }) => {
+ const state = useReduxState();
+ const dispatch = useReduxDispatch();
+ const slice = state[idx];
+ return (
+ <ul className="list-group">
+ {slice.map(pair => {
+ return <Pair key={pair.id} sliceId={idx} pairId={pair.id} />;
+ })}
+ </ul>
+ );
}
because it only changes a single Pair's value
(which only Pair
listens to):
const highOrderSliceReducer = (sliceId = "") => (
state = initialState,
action
) => {
switch (action.type) {
case `${c.FILL_PAIRS}_${sliceId}`: {
return [...action.pairs];
}
case `${c.UPDATE_PAIR}_${sliceId}`: {
return state.map(pair => {
return pair.id === action.id ? { ...pair, value: action.value } : pair;
});
}
default: {
return state;
}
}
};
Slice
does not listen to that. Basically, log the render functions of components in both benchmarks, and make sure they have the same # of renderings per component. This has a high potential for revealing the bottleneck.
Do you have any benchmarks of the library running off a Proxy polyfill? (To get a sense of how this will perform in IE11)
@joshjg - immer has comparison - https://github.com/mweststrate/immer#performance
immer + es5 and immer + proxy
The pollfill also isn’t automatic. You must know all the keys you want to track ahead of time. I could be wrong but that probably requires too much work for a library whose sole goal is to automate all this boilerplate away.
Not quite correct - polyfill is automatically applied on the nested property access. It polyfills the call to the Proxy
constructor, which would be made to a read only state.
There are only two problems:
- adding properties in runtime. It setter should be able to add getter for a new key. A case for
immer
(mostly setters), but not for state selections (only getters) - huge arrays or objects - you have to iterate through them and create getters. O(n), but only for a first time. Next time, if object is not changed (you are not changing all the store every tick) - proxy instance gonna to be reused.
To be even more correct - on compact objects(10 keys?) Proxy polyfill is even faster.
through doing this extra work under the hood to support the polyfill, you're saying it could be made fully automated to the end user? fully?
Yes. It's an essential part of any polyfill - it just works.
the proxy poylfill doesn't constitute a "proper polyfill." it doesn't "just work." extra work is required by us. and if we do that, only then can the experience be seamless to the end user (and I'm still not convinced it is). so the question is do we care?
Proxy polyfill polyfills everything we want to use. It just works. It even comes with proxyequal in a short, compact and enough version - https://github.com/theKashey/proxyequal/blob/master/src/proxy-polyfill.js
now thats what i wanted to hear!
on compact objects(10 keys?) Proxy polyfill is even faster.
I didn't know that.
Here's the other thing to check:
the action ${c.UPDATE_PAIR}_${sliceId}
should not trigger a re-render in the parent Slice:
No problem with this. It just renders only once.
stockticker benchmarks
how to run
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
git checkout e3f37855e2969591c51b3e14262969d6d426fa37
npm install
rm sources/*/yarn.lock
BENCHMARKS=stockticker:stockticker-useReduxState:stockticker-useReduxStateMapped:stockticker-rrh npm run initialize
REDUX=7.0.0-beta.0 BENCHMARK_TRACE=false BENCHMARKS=stockticker:stockticker-useReduxState:stockticker-useReduxStateMapped:stockticker-rrh npm start
results
Running benchmark stockticker
Checking max FPS... (30 seconds)
Results for benchmark stockticker:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ 52.10 │ 297.6, 0.3 │ 49,50,54,52,54,46,54,53,55,56,54,53,51,56,53,56,53,55,53,51,49,51,50,52,50,49,49 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Running benchmark stockticker-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark stockticker-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 26.07 │ 276.4, 0.3 │ 25,27,28,27,28,26,27,26,27,25,27,25,27,26,25,26,25,26,25,24,24 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark stockticker-useReduxStateMapped
Checking max FPS... (30 seconds)
Results for benchmark stockticker-useReduxStateMapped:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────┤
│ 26.05 │ 293.6, 0.3 │ 24,27,28,27,26,28,26,28,27,26,27,26,25,26,25,26,27,25,26,24,25,25 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────┘
Running benchmark stockticker-rrh
Checking max FPS... (30 seconds)
Results for benchmark stockticker-rrh:
┌─────────┬──────────────┬──────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────┤
│ 28.91 │ 185.4, 0.9 │ 28,31,30,31,30,31,30,31,28,29,28,27,29,27,28,26,29,28,28 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────┘
notes
stockticker
is react-reduxstockticker-useReduxState
andstockticker-useReduxStateMapped
are two variants from reactive-react-reduxstockticker-rrh
is redux-react-hook
It's good to have a slow example - easier to detect friction points (is there any?)
FWIW, I'd suggest focusing on the deeptree
and deeptree-nested
benchmarks as being more representative of typical update patterns.
Agreed. This one isn't something you'd do in a normal Redux app (i.e. it's quite contrived).
However we gotta get to the bottom of what makes the proxy solution slow here. It's an important clue regarding the current weakness of this approach.
focusing on the deeptree and deeptree-nested benchmarks
Understood.
It's an important clue regarding the current weakness of this approach.
Agreed too.
(is there any?)
I have no clue at the moment.
If it's just a consequence of the work Proxies are doing, a non-redux benchmark of calling the mapStateToProps
function vs the equivalent with proxies might be revealing.
I.e. it might be useful to get a real idea how many cycles the proxy approach is running through.
There are a few obvious wars to speed up it a bit:
- change proxyequal reporting model. Right now it’s a long strings, what is object model would be faster?
- proxyCompare? There is another function - proxyShallowEqual - which does the same in respect of object nesting, ie capable to skip nested object comparison, if parent is the same. (Could benefit from idea 1)
- collectValuables, currently called every tick. What if on every “report” change some hash, and if new hash === oldHash - return last valuables. Something like memoization.
Proxyequal is very hot spot, and it’s probably worth to rewrite it. We might keep all the tests to keep it “right” (I’ll add a few failing ones), and make it more perfomant.
1: string concatenation vs. object creation. not sure which is faster.
2: I may misunderstood proxyShallowEqual. So, can I use it for this project?
3: If we can avoid collectValuables with small cost, that would be awesome. Note that collectValuables is invoked only once in render now, and the bottleneck is not render but comparison for these benchmarks.
1: string concatenation and de-concatenation vs. object creation. It's more about CPU vs GC.
2. proxyShallowEqual
is meant to be faster for deeper objects. But is it fast - I don't know. Probably I should replace Maps
by plain objects. And yes - you may use it, memoize-state
is using it. But it's a more theoretical correct concept, than real. I am not sure about some decisions of mine after you have refactored 🤷♂️normal🤷♂️ function to be 10 times faster.
3. collectValuables
could be an easy win, if hash calculation would be cheap enough.
What is proxyShallow
? I confused it with proxyShallowEqual
.
So what I am going to spike:
-
how tracking is working - right now it's pushing keys to an array. Longer and longer strings with every nested access. What if use objects, and nest objects access as objects could be nested
-
how
valuable
access is calculated. It has no understood nesting, and could memoize last access, as long as in our case it's almost always the same. The same memoization could make .1 more perfomant, if "new" access is mocking the last one (in terms of v8 hidden classes). -
how proxies got removed. Probably we shall hook into
createElement
todeproxify
and update usage information for the objects passed down as-is.
In short - gonna rewrite proxyequal from a scratch.
FYI: just replacing with proxyShallowEqual
was slower.
Not a surprise - it was faster than proxyCompare
, but then you optimized proxyCompare
and it becomes slower.
With regard to stockticker-useReduxState
, I have one clue. It's Slice.jsx.
I found that affected
is very long. ex:
(878) [".0.map", ".0.length", ".0.constructor", ".0.0.id", ".0.1.id", ".0.2.id", ".0.3.id", ".0.4.id", ".0.5.id", ".0.6.id", ".0.7.id", ".0.8.id", ".0.9.id", ".0.10.id", ".0.11.id", ".0.12.id", ".0.13.id", ".0.14.id", ".0.15.id", ".0.16.id", ".0.17.id", ".0.18.id", ".0.19.id", ".0.20.id", ".0.21.id", ".0.22.id", ".0.23.id", ".0.24.id", ".0.25.id", ".0.26.id", ".0.27.id", ".0.28.id", ".0.29.id", ".0.30.id", ".0.31.id", ".0.32.id", ".0.33.id", ".0.34.id", ".0.35.id", ".0.36.id", ".0.37.id", ".0.38.id", ".0.39.id", ".0.40.id", ".0.41.id", ".0.42.id", ".0.43.id", ".0.44.id", ".0.45.id", ".0.46.id", ".0.47.id", ".0.48.id", ".0.49.id", ".0.50.id", ".0.51.id", ".0.52.id", ".0.53.id", ".0.54.id", ".0.55.id", ".0.56.id", ".0.57.id", ".0.58.id", ".0.59.id", ".0.60.id", ".0.61.id", ".0.62.id", ".0.63.id", ".0.64.id", ".0.65.id", ".0.66.id", ".0.67.id", ".0.68.id", ".0.69.id", ".0.70.id", ".0.71.id", ".0.72.id", ".0.73.id", ".0.74.id", ".0.75.id", ".0.76.id", ".0.77.id", ".0.78.id", ".0.79.id", ".0.80.id", ".0.81.id", ".0.82.id", ".0.83.id", ".0.84.id", ".0.85.id", ".0.86.id", ".0.87.id", ".0.88.id", ".0.89.id", ".0.90.id", ".0.91.id", ".0.92.id", ".0.93.id", ".0.94.id", ".0.95.id", ".0.96.id", …]
It won't trigger render, but comparing every time obviously.
Just tried simply skipping comparing if the affected is very long.
baseline
┌─────────┬──────────────┬──────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────┤
│ 21.36 │ 398.5, 0.3 │ 21,23,24,22,23,18,17,22,23,24,22,18,13,17,21,22,23,22,22 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────┘
with the hack
┌─────────┬──────────────┬──────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼──────────────────────────────────────────────────────────────────────┤
│ 26.88 │ 332.6, 0.3 │ 26,30,29,27,29,25,29,28,26,25,29,27,19,20,25,28,27,26,28,26,27,28,28 │
└─────────┴──────────────┴──────────────────────────────────────────────────────────────────────┘
Still, there seems to be some other causes, to compare with react-redux
's 40~50FPS.
For reference, here the code of Slice.jsx:
import React from "react";
import { useReduxState, useReduxDispatch } from "reactive-react-redux";
import Pair from "./Pair";
import { fillPairs } from "./pairActions";
const Slice = ({ idx }) => {
const state = useReduxState();
const dispatch = useReduxDispatch();
const slice = state[idx];
return (
<ul className="list-group">
{slice.map(pair => {
return <Pair key={pair.id} sliceId={idx} pairId={pair.id} />;
})}
</ul>
);
};
Slice.displayName = "Slice";
export default Slice;
proxyShallowCompare
would compare only .0
and go deeper only it makes a sense. So it worth to be rewritten.
Instead of react-redux-benchmarks, I tried benchmark with js-framework-benchmark.
The result seems somewhat realistic, doesn't it?
The benchmark repo
With regard to stockticker-useReduxState, I have one clue. It's Slice.jsx.
This wasn't the main reason of the slowness. The slowness is more by many comparisons with small affected like the following.
[".1", ".1.267", ".1.267.value", ".1.267.name"]
Death by a thousands cuts. Probably it worth so store used keys in a nested object form the beginning, then we would be able to:
- fastely compare them, without any preprocessing needed
- utilize v8 hidden classes if many objects uses similar objects. In this case it would be
{value, name}
Comparing v2 and v3
useReduxState in v2.0.1
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
git checkout 43cc0b92fef86ee222014e3263704be34da29556
npm install
rm sources/*/yarn.lock
BENCHMARKS=deeptree-nested-useReduxState:deeptree-useReduxState:forms-useReduxState:stockticker-useReduxState:tree-view-useReduxState:twitter-lite-useReduxState npm run initialize
REDUX=7.0.2 BENCHMARK_TRACE=false BENCHMARKS=deeptree-nested-useReduxState:deeptree-useReduxState:forms-useReduxState:stockticker-useReduxState:tree-view-useReduxState:twitter-lite-useReduxState npm start
Running benchmark deeptree-nested-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark deeptree-nested-useReduxState:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 50.96 │ 174.2, 0.5 │ 51,57,58,57,60,57,54,55,56,57,53,54,48,47,43,45,46,45,42,44,45,40,41,41 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘
Running benchmark deeptree-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark deeptree-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 38.09 │ 84.4, 0.8 │ 46,45,48,47,45,43,44,48,46,45,49,46,48,45,28,29,28,29,28,29,28,26,23,21,21 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
Running benchmark forms-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark forms-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 56.71 │ 1141.9, 0.3 │ 56,57,58,56,57,56,57,58,60,58,55,54,56,58,57,55,56,55,59,57,57 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark stockticker-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark stockticker-useReduxState:
┌─────────┬──────────────┬───────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼───────────────────────────────────────────────────────────────────┤
│ 25.20 │ 292.9, 0.2 │ 25,26,27,25,26,25,26,27,26,24,26,25,24,25,23,25,24,25,24,25,24,24 │
└─────────┴──────────────┴───────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark tree-view-useReduxState:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 47.08 │ 687.7, 1.4 │ 49,29,44,54,47,42,45,36,50,51,48,52,42,48,49,42,45,47,44,51,48,55,52,50,51,47,51,51 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark twitter-lite-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark twitter-lite-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 55.14 │ 3.1, 0.9 │ 60,59,60,59,60,59,58,59,60,59,55,59,55,56,60,57,58,54,51,47,46,44,34,36,36 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────┘
useReduxState in v3.0.0
git clone https://github.com/dai-shi/react-redux-benchmarks.git
cd react-redux-benchmarks
git checkout 09d621170ca2536ba14001b40972bb39c8f72191
npm install
rm sources/*/yarn.lock
BENCHMARKS=deeptree-nested-useReduxState:deeptree-useReduxState:forms-useReduxState:stockticker-useReduxState:tree-view-useReduxState:twitter-lite-useReduxState npm run initialize
REDUX=7.0.2 BENCHMARK_TRACE=false BENCHMARKS=deeptree-nested-useReduxState:deeptree-useReduxState:forms-useReduxState:stockticker-useReduxState:tree-view-useReduxState:twitter-lite-useReduxState npm start
Running benchmark deeptree-nested-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark deeptree-nested-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 51.60 │ 171.5, 0.4 │ 60,59,56,60,58,56,55,54,57,56,50,47,45,46,44,47,46,44,41,45,45 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark deeptree-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark deeptree-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 39.27 │ 91.2, 0.6 │ 48,47,50,52,46,47,51,49,45,48,52,54,30,29,32,30,29,30,28,21,21 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Running benchmark forms-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark forms-useReduxState:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────┤
│ 56.61 │ 1070.2, 0.3 │ 54,55,56,55,56,60,54,56,57,58,57,58,57,56,57,58,57,58,56,56 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────┘
Running benchmark stockticker-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark stockticker-useReduxState:
┌─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ 52.92 │ 218.8, 0.2 │ 53,59,54,58,59,58,57,56,54,55,56,55,52,50,54,55,53,49,48,50,53,49,52,51,49,47,49,49 │
└─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark tree-view-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark tree-view-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 50.58 │ 533.3, 1.2 │ 42,52,58,45,50,42,49,54,52,55,46,53,46,53,50,52,51,56,54,52,55,50,48,43,56,52,53,45,45 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Running benchmark twitter-lite-useReduxState
Checking max FPS... (30 seconds)
Results for benchmark twitter-lite-useReduxState:
┌─────────┬──────────────┬────────────────────────────────────────────────────────────────┐
│ Avg FPS │ Render │ FPS Values │
│ │ (Mount, Avg) │ │
├─────────┼──────────────┼────────────────────────────────────────────────────────────────┤
│ 59.02 │ 3.2, 0.9 │ 60,59,60,59,60,59,60,59,60,59,60,59,57,59,58,60,57,60,59,55,55 │
└─────────┴──────────────┴────────────────────────────────────────────────────────────────┘
Summary
useReduxState in v2.0.1 | useReduxState in v3.0.0 | |
---|---|---|
deeptree-nested | 50.96 | 51.60 |
deeptree | 38.09 | 39.27 |
forms | 56.71 | 56.61 |
stockticker | 25.20 | 52.92 |
tree-view | 47.08 | 50.58 |
twitter-lite | 55.14 | 59.02 |
Numbers are Avg FPS.