Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack size issues #2922

Closed
mhart opened this issue Jan 18, 2016 · 20 comments
Closed

Stack size issues #2922

mhart opened this issue Jan 18, 2016 · 20 comments
Labels

Comments

@mhart
Copy link

mhart commented Jan 18, 2016

Investigating a slowdown from 1.x to 2.x on our server-side rendering – we're seeing around a 15-25% hit going to 2.x (edit – this may be a red herring and not due to react-router)

I've run a profile, but it's quite hard to read because of the amount of recursion performed when matching routes – and this actually hasn't really changed from 1.x so I'm not sure that it's even contributing.

It would be great if plain 'ol synchronous routes didn't have this incredible amount of recursion – we only have 20 routes in our app, but route matching accounts for the highest amount of CPU used in our app – much more than the React rendering itself (edit – no it doesn't, the stacks just made it look that way).

To give you an illustration, here's a profile (taken with node --prof) of our app – it's about 80 functions deep before you can even see the renderToString function – this makes it incredibly hard to profile and track down performance issues:

 [Top down (heavy) profile]:
  Note: callees occupying less than 0.1% are not shown.

  inclusive      self           name
  ticks   total  ticks   total
  13901   18.3%      0    0.0%  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  13410   17.6%      0    0.0%    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  13410   17.6%      0    0.0%      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  13314   17.5%      0    0.0%        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  13314   17.5%      0    0.0%          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  13314   17.5%      0    0.0%            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  13314   17.5%      0    0.0%              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  13314   17.5%      0    0.0%                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  13314   17.5%      0    0.0%                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  13314   17.5%      0    0.0%                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  13314   17.5%      0    0.0%                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  13314   17.5%      0    0.0%                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  13314   17.5%      0    0.0%                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  13269   17.4%      0    0.0%                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  13269   17.4%      0    0.0%                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  13269   17.4%      0    0.0%                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  13269   17.4%      0    0.0%                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  12979   17.0%      0    0.0%                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  12979   17.0%      0    0.0%                                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  12979   17.0%      0    0.0%                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  12979   17.0%      0    0.0%                                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  12507   16.4%      0    0.0%                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  12507   16.4%      0    0.0%                                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  12507   16.4%      0    0.0%                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  12507   16.4%      0    0.0%                                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  11843   15.6%      0    0.0%                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  11843   15.6%      0    0.0%                                                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  11843   15.6%      0    0.0%                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  11843   15.6%      0    0.0%                                                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  11140   14.6%      0    0.0%                                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  11140   14.6%      0    0.0%                                                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  11140   14.6%      0    0.0%                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  11140   14.6%      0    0.0%                                                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  10412   13.7%      0    0.0%                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  10412   13.7%      0    0.0%                                                                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
  10412   13.7%      0    0.0%                                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  10412   13.7%      0    0.0%                                                                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   9190   12.1%      0    0.0%                                                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   9190   12.1%      0    0.0%                                                                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   9190   12.1%      0    0.0%                                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   9190   12.1%      0    0.0%                                                                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   8503   11.2%      0    0.0%                                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   8503   11.2%      0    0.0%                                                                                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   8503   11.2%      0    0.0%                                                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   8503   11.2%      0    0.0%                                                                                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   7647   10.0%      0    0.0%                                                                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   7647   10.0%      0    0.0%                                                                                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   7647   10.0%      0    0.0%                                                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   7647   10.0%      0    0.0%                                                                                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   6350    8.3%      0    0.0%                                                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   6350    8.3%      0    0.0%                                                                                                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   6350    8.3%      0    0.0%                                                                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   6350    8.3%      0    0.0%                                                                                                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   4735    6.2%      0    0.0%                                                                                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   4735    6.2%      0    0.0%                                                                                                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   4735    6.2%      0    0.0%                                                                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   4735    6.2%      0    0.0%                                                                                                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   3318    4.4%      0    0.0%                                                                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   3318    4.4%      0    0.0%                                                                                                                      LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   3318    4.4%      0    0.0%                                                                                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   3318    4.4%      0    0.0%                                                                                                                          LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   2087    2.7%      0    0.0%                                                                                                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   2087    2.7%      0    0.0%                                                                                                                              LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:16:16
   2087    2.7%      0    0.0%                                                                                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
   2087    2.7%      0    0.0%                                                                                                                                  LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
   1082    1.4%      0    0.0%                                                                                                                                    Function: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:96:29
   1041    1.4%      0    0.0%                                                                                                                                      LazyCompile: *getIndexRoute /app/node_modules/react-router/lib/matchRoutes.js:29:23
   1041    1.4%      0    0.0%                                                                                                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:102:49
   1041    1.4%      0    0.0%                                                                                                                                          LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   1041    1.4%      0    0.0%                                                                                                                                            LazyCompile: ~done /app/node_modules/react-router/lib/AsyncUtils.js:11:16
   1041    1.4%      0    0.0%                                                                                                                                              LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:139:53
   1041    1.4%      0    0.0%                                                                                                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
   1041    1.4%      0    0.0%                                                                                                                                                  LazyCompile: ~done /app/node_modules/react-router/lib/AsyncUtils.js:11:16
   1041    1.4%      0    0.0%                                                                                                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:76:59
   1041    1.4%      0    0.0%                                                                                                                                                      LazyCompile: ~finishMatch /app/node_modules/react-router/lib/createTransitionManager.js:88:23
   1041    1.4%      0    0.0%                                                                                                                                                        LazyCompile: *runEnterHooks /app/node_modules/react-router/lib/TransitionUtils.js:46:23
   1041    1.4%      0    0.0%                                                                                                                                                          LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:99:69
   1041    1.4%      0    0.0%                                                                                                                                                            LazyCompile: *getComponents /app/node_modules/react-router/lib/getComponents.js:26:23
   1041    1.4%      0    0.0%                                                                                                                                                              LazyCompile: *mapAsync /app/node_modules/react-router/lib/AsyncUtils.js:29:18
   1041    1.4%      0    0.0%                                                                                                                                                                LazyCompile: *forEach native array.js:943:22
   1041    1.4%      0    0.0%                                                                                                                                                                  LazyCompile: *InnerArrayForEach native array.js:924:27
   1041    1.4%      0    0.0%                                                                                                                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/AsyncUtils.js:53:26
    533    0.7%      0    0.0%                                                                                                                                                                      LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/getComponents.js:27:51
    533    0.7%      0    0.0%                                                                                                                                                                        LazyCompile: ~getComponentsForRoute /app/node_modules/react-router/lib/getComponents.js:7:31
    533    0.7%      0    0.0%                                                                                                                                                                          LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/AsyncUtils.js:54:32
    494    0.6%      0    0.0%                                                                                                                                                                            LazyCompile: ~done /app/node_modules/react-router/lib/AsyncUtils.js:38:16
    494    0.6%      0    0.0%                                                                                                                                                                              LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:106:56
    494    0.6%      0    0.0%                                                                                                                                                                                LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/match.js:54:46
    494    0.6%      0    0.0%                                                                                                                                                                                  LazyCompile: ~<anonymous> /app/server.js:185:71
    494    0.6%      0    0.0%                                                                                                                                                                                    LazyCompile: renderToString /app/node_modules/react/lib/ReactServerRendering.js:30:24
...

I really think the route matching needs to be converted to an iterative algorithm instead of a recursive one – if getChildRoutes is encountered, then that'll need callback treatment, but normal childRoutes should just be able to be iterated over flatly.

If that's not possible, then I wonder if it's worth considering making it truly asynchronous and processing each route on a different tick in the event loop – although I realise that this may actually make it less performant, though the function stacks would be a bit more sane.

Thoughts? Has anyone else encountered performance issues with this library?

@taion
Copy link
Contributor

taion commented Jan 18, 2016

I'd rather keep route matching synchronous. Which specific version did you use as the baseline for your benchmarks?

@mhart
Copy link
Author

mhart commented Jan 18, 2016

v1.0.3 to v2.0.0-rc5

Again, I wouldn't hold v1.0.3 up as the performance king – it's still the heaviest part of our server-side rendering AFAICT (edit – nope... it's not).

@taion
Copy link
Contributor

taion commented Jan 18, 2016

Of all the code I thought I'd have to optimize, front end open source JS code would probably have taken last place...

If I put something up on a branch, can you test against it?

@mhart
Copy link
Author

mhart commented Jan 18, 2016

Yep, for sure – should have time to test it tomorrow.

@taion
Copy link
Contributor

taion commented Jan 18, 2016

Try this branch: #2923

I've unwound the recursion in loopAsync when next() is called synchronously.

@ryanflorence
Copy link
Member

I'd just like to say this is actually really exciting to me that the project has moved beyond the point of "works" to "lets make it work faster". Thanks for this issue, obviously we want route matching to be as fast as possible while still allowing for code-splitting on routes.

@timdorr
Copy link
Member

timdorr commented Jan 18, 2016

I don't think there has been any explicit performance effort made on react-router, outside of really obvious stuff. These kinds of deep dives into integrated performance testing are likely to yield some serious gains.

@taion taion added the feature label Jan 18, 2016
@mhart
Copy link
Author

mhart commented Jan 18, 2016

@taion that branch certainly helps flatten things out (by about a factor of 2) – now about 40 functions deep before we get to renderToString:

  inclusive      self           name
  ticks   total  ticks   total
  39754   39.1%      1    0.0%  LazyCompile: *match /app/node_modules/react-router/lib/match.js:36:15
  39135   38.5%      0    0.0%    LazyCompile: ~match /app/node_modules/react-router/lib/createTransitionManager.js:71:17
  39135   38.5%      0    0.0%      LazyCompile: *matchRoutes /app/node_modules/react-router/lib/matchRoutes.js:170:21
  39135   38.5%      0    0.0%        Function: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:174:20
  39134   38.5%      1    0.0%          LazyCompile: ~loopAsync /app/node_modules/react-router/lib/AsyncUtils.js:7:19
  34063   33.5%      1    0.0%            LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:18:16
  20904   20.5%      0    0.0%              LazyCompile: *<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  20904   20.5%      0    0.0%                LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  20901   20.5%      0    0.0%                  LazyCompile: *getChildRoutes /app/node_modules/react-router/lib/matchRoutes.js:17:24
  20901   20.5%      0    0.0%                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:134:46
  20901   20.5%      1    0.0%                      LazyCompile: *matchRoutes /app/node_modules/react-router/lib/matchRoutes.js:170:21
  20900   20.5%      0    0.0%                        Function: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:174:20
  20900   20.5%      0    0.0%                          LazyCompile: ~loopAsync /app/node_modules/react-router/lib/AsyncUtils.js:7:19
  20899   20.5%      0    0.0%                            LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:18:16
  20899   20.5%      2    0.0%                              LazyCompile: *<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:175:51
  20897   20.5%      1    0.0%                                LazyCompile: *matchRouteDeep /app/node_modules/react-router/lib/matchRoutes.js:80:24
  20859   20.5%      1    0.0%                                  Function: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:96:29
  20851   20.5%      0    0.0%                                    LazyCompile: *getIndexRoute /app/node_modules/react-router/lib/matchRoutes.js:29:23
  20851   20.5%      1    0.0%                                      LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:102:49
  20850   20.5%      0    0.0%                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  20850   20.5%      0    0.0%                                          LazyCompile: ~done /app/node_modules/react-router/lib/AsyncUtils.js:13:16
  20850   20.5%      0    0.0%                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:139:53
  20850   20.5%      0    0.0%                                              LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:176:100
  20850   20.5%      0    0.0%                                                LazyCompile: ~done /app/node_modules/react-router/lib/AsyncUtils.js:13:16
  20850   20.5%      0    0.0%                                                  LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:76:59
  20837   20.5%      0    0.0%                                                    LazyCompile: ~finishMatch /app/node_modules/react-router/lib/createTransitionManager.js:88:23
  20836   20.5%      0    0.0%                                                      LazyCompile: *runEnterHooks /app/node_modules/react-router/lib/TransitionUtils.js:46:23
  20834   20.5%      0    0.0%                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:99:69
  20834   20.5%      0    0.0%                                                          LazyCompile: *getComponents /app/node_modules/react-router/lib/getComponents.js:26:23
  20834   20.5%      1    0.0%                                                            LazyCompile: *mapAsync /app/node_modules/react-router/lib/AsyncUtils.js:46:18
  20832   20.5%      0    0.0%                                                              LazyCompile: *forEach native array.js:943:22
  20832   20.5%      1    0.0%                                                                LazyCompile: *InnerArrayForEach native array.js:924:27
  20831   20.5%      1    0.0%                                                                  LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/AsyncUtils.js:70:26
  11658   11.5%      0    0.0%                                                                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/getComponents.js:27:51
  11658   11.5%      0    0.0%                                                                      LazyCompile: ~getComponentsForRoute /app/node_modules/react-router/lib/getComponents.js:7:31
  11657   11.5%      0    0.0%                                                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/AsyncUtils.js:71:32
  10651   10.5%      0    0.0%                                                                          LazyCompile: ~done /app/node_modules/react-router/lib/AsyncUtils.js:55:16
  10651   10.5%      0    0.0%                                                                            LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:106:56
  10646   10.5%      0    0.0%                                                                              LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/match.js:54:46
  10641   10.5%      1    0.0%                                                                                LazyCompile: ~<anonymous> /app/server.js:185:71
  10242   10.1%      0    0.0%                                                                                  LazyCompile: renderToString /app/node_modules/react/lib/ReactServerRendering.js:30:24
...

Obviously I'd love it if this stack could be a little flatter, but I don't know enough about the internals to see if there's some more low-hanging fruit in there.

In terms of performance, I think it's probably better to create some smaller benchmarks where the rest of React isn't going to get in the way – it's hard to isolate react-router as the sole cause of performance issues in our case – the deep stacks make it look like it may be responsible, but there's a good chance it might just be a red herring in our case.

I'll play around some more and let you know if anything jumps out.

@taion
Copy link
Contributor

taion commented Jan 18, 2016

Oh, darn, thought that'd kill all the stack bloat.

It makes perfect sense to me that this sort of thing is not going to JIT well – unless V8 is ridiculously aggressive about inlining recursive function calls.

Let me see if I can flatten this out a little bit more.

How specifically are you running benchmarks?

@mhart
Copy link
Author

mhart commented Jan 18, 2016

@taion just using node --prof on our server, and then using wrk to generate load against certain URLs that react-router will need to match

@timdorr
Copy link
Member

timdorr commented Jan 18, 2016

What's your route tree look like? I'd love to get some performance tests in place so we can compare versions over time.

Actually, that's one of my startup ideas: A CI service that also integrates performance data over time.

@mhart
Copy link
Author

mhart commented Jan 18, 2016

Route tree is pretty flat and looks like:

  <Route path='/prefix' component={a}>
    <IndexRoute component={b} />

    <Route path='a.html' component={c} />
    <Route path='b.html' component={d} />
    <Route path='c.html' component={e} />

    {/* 13 more exact matches */}

    <Route path='d/:id.html' component={f} />

    <Route path=':id.html' component={g} />
    <Route path=':id1/:id2.html' component={h} />
    <Route path=':id1/:id2/:id3.html' component={i} />

    <Route path='*' component={NotFound} />
  </Route>

And I'm testing one of the :id1/:id2.html URLs near the end there.

Gonna create a simpler version of what we've got and test it out a bit – now that I can see the stack a little clearer, I'm less convinced that react-router's actually having a noticeable impact on performance.

@taion
Copy link
Contributor

taion commented Jan 18, 2016

@mhart

I unwound a bit more of the stack and rebased by branch. It's not going to clear as many stack frames, but it should clear a few. Can you when you get a chance and let me know how it looks?

The code is far enough down into internals and well enough covered by tests that I'm not worried about complexity there.

As long as it's not e.g. introducing a perf regression, I'd be inclined to merge it just so people get more usable stack frames.

@mhart
Copy link
Author

mhart commented Jan 18, 2016

@taion great stuff, cut it in half again, much easier to read!

  inclusive      self           name
  ticks   total  ticks   total

  14664   52.9%      1    0.0%  LazyCompile: *match /app/node_modules/react-router/lib/match.js:36:15
  14326   51.7%      0    0.0%    LazyCompile: ~match /app/node_modules/react-router/lib/createTransitionManager.js:71:17
  14326   51.7%      0    0.0%      LazyCompile: *matchRoutes /app/node_modules/react-router/lib/matchRoutes.js:188:21
  14325   51.7%      1    0.0%        Function: ~<anonymous> /app/node_modules/react-router/lib/matchRoutes.js:192:20
  14324   51.7%      1    0.0%          LazyCompile: ~loopAsync /app/node_modules/react-router/lib/AsyncUtils.js:8:19
  12029   43.4%      0    0.0%            LazyCompile: ~next /app/node_modules/react-router/lib/AsyncUtils.js:26:16
  11925   43.0%      0    0.0%              LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:76:59
  11912   43.0%      0    0.0%                LazyCompile: ~finishMatch /app/node_modules/react-router/lib/createTransitionManager.js:88:23
  11910   43.0%      0    0.0%                  LazyCompile: *runEnterHooks /app/node_modules/react-router/lib/TransitionUtils.js:46:23
  11910   43.0%      0    0.0%                    LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:99:69
  11910   43.0%      0    0.0%                      LazyCompile: *getComponents /app/node_modules/react-router/lib/getComponents.js:26:23
  11910   43.0%      0    0.0%                        LazyCompile: *mapAsync /app/node_modules/react-router/lib/AsyncUtils.js:61:18
  11910   43.0%      0    0.0%                          LazyCompile: *forEach native array.js:943:22
  11910   43.0%      0    0.0%                            LazyCompile: *InnerArrayForEach native array.js:924:27
  11910   43.0%      0    0.0%                              LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/AsyncUtils.js:85:26
   6647   24.0%      0    0.0%                                LazyCompile: *<anonymous> /app/node_modules/react-router/lib/getComponents.js:27:51
   6647   24.0%      0    0.0%                                  LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/AsyncUtils.js:86:32
   6647   24.0%      0    0.0%                                    LazyCompile: *done /app/node_modules/react-router/lib/AsyncUtils.js:70:16
   6647   24.0%      0    0.0%                                      LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/createTransitionManager.js:106:56
   6647   24.0%      1    0.0%                                        LazyCompile: ~<anonymous> /app/node_modules/react-router/lib/match.js:54:46
   6635   23.9%      1    0.0%                                          LazyCompile: ~<anonymous> /app/server.js:185:71
   6178   22.3%      0    0.0%                                            LazyCompile: renderToString /app/node_modules/react/lib/ReactServerRendering.js:30:24
...

@mhart
Copy link
Author

mhart commented Jan 18, 2016

Ok, so now that I can traverse the stacks a little easier, I can see that there are a bunch of other renderToStrings further down the profile and cumulatively they add up to about 70-75% of the total ticks – so I'm gonna go ahead and say I was almost definitely wrong before in blaming react-router for being the heaviest part of our rendering!

Will do a quick check to see if I can get some more accurate numbers on performance differences (if any) between the versions – it could be that @taion's changes have made a noticeable speed-up, although I suspect it's something else that's changed in our code that caused what I saw earlier.

@mhart mhart changed the title Performance and stack size issues Stack size issues Jan 18, 2016
@mhart
Copy link
Author

mhart commented Jan 18, 2016

Changed this issue to be purely about the stack size – I think any performance exploration needs something a bit more scientific than what I can see in our app, so I'll open a separate issue if I build a reproducible benchmark that shows anything.

@taion's changes in #2923 are a huge help in viewing stack traces and performance profiles though, so once that gets merged I say we close this issue.

@taion
Copy link
Contributor

taion commented Jan 18, 2016

Based on the profiling you've done so far, how likely do you think it is that #2923 does not introduce performance regressions?

@mhart
Copy link
Author

mhart commented Jan 18, 2016

Very likely – I see no noticeable difference in performance (either negative or positive) between #2923 and 2.0.0-rc5

@timdorr
Copy link
Member

timdorr commented Jan 18, 2016

I've always heard rumblings from the mythical v8 devs that the engine better handles iterative code, rather than recursive. I don't know the internals well enough to know if that's true. It may matter if the JITer is warmed up enough, along with other caches. So, my inclination is that the code changes in #2923 are probably marginally faster.

@timdorr timdorr closed this as completed Jan 18, 2016
@taion
Copy link
Contributor

taion commented Jan 18, 2016

At a glance the main JIT optimization possible with iterative code that would be difficult to apply in the recursive case would be inlining, which is probably going to be the biggest potential gain here anyway (avoid a bunch of extra jumps).

@lock lock bot locked as resolved and limited conversation to collaborators Jan 22, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants