-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
Performance regression on route matching for sites with 20+ routes #3215
Comments
Can you spit out the route config? That would be helpful to see. I know Gatsby auto-generates it, so it may be creating something that's non-optimal. Not that we wouldn't want to make that config performant, but it would be helpful to attack from both sides. |
Is there an easy way to print out the route config? Here's a screenshot of the routes object: This is where the main bit where pages are added to the react-router config https://github.com/gatsbyjs/gatsby/blob/master/lib/isomorphic/create-routes.js#L152-L159 |
I took a quick look. There's something wrong with our generated regexps. I don't see this problem against the experimental path-to-regexp branch: https://github.com/reactjs/react-router/tree/path-to-regexp. |
@KyleAMathews Can you run your test real quick against the path-to-regexp branch and let me know what the results look like? |
Woah, the path-regexp branch drops execution time another order of magnitude. Now takes roughly ~10ms. |
To put some UX meat on these raw execution time bones. Assuming a max route matching time of 1 second (at which point users start to lose focus on what they're doing) this is the maximum number of routes possible: 2.0.1: ~100 |
FWIW, I'd suggest using a different strategy for your use case – something like a single route with a path like #3217 is probably the right way to go, though using path-to-regexp is still up in the air, but properly supporting scalable routing for something like your case with a big basket of static routes would require special-casing child routes with fixed paths. We can do this, but it'll be a bit farther away. Hmm, or maybe not. |
To elaborate on the above, the idea would be to special-case child routes that don't define any params, as those can be resolved with a simple hashmap lookup. For example, this is the logic in routington that handles this special case when matching: https://github.com/pillarjs/routington/blob/1.0.3/lib/match.js#L36-L47 cc @jonathanong, whose name has popped up on this issue tracker before, for any further thoughts here. |
Great idea @taion! Gatsby is a special case for sure and since all routes are known ahead of time, it'd be easy + lightning fast to drop all our routes into an object and "match" using that. I'll try this in the next few days and report back. |
@taion I was thinking the same thing. Even better, you can short circuit all of the regex and just use some |
Better than We could actually do this right now, but I'd rather we not build further specific dependencies on our idiosyncratic pattern matching logic, given that we're still considering moving to path-to-regexp. |
@taion not sure what input I can provide other than the hash map. I wouldn't use path to regexp for react router because it doesn't store routes as a tree (express and friends generally store routes as a simple list), which are how react routes are declared. Seems harder to integrate path to regexp than it is to make your own tree router with path to regexp semantics. If you or anyone want to help maintain routington I can add you as a collaborator. The semantics are pretty similar to path to regexp. I don't even use it because trie routers are more difficult to code with when you take into account http methods, but it makes sense to me for react router. |
We actually already have all the tree routing set up, and it is effectively a trie-based router. The route matching needs to be custom because we support lazily loading parts of the route tree from code-splitted chunks. I was thinking about path-to-regexp in the context of replacing our existing code for generating those regexp semantics. It's similar enough that we can use it as a drop-in replacement for the function we currently use, though the feature set is slightly different. In other words, use path-to-regexp purely for building the regexps for each node in the trie. |
Why'd we close this? Seems like a problem. |
#3217 mostly fixed the regression. There's further room for improvement/optimization, but this issue per se is now resolved. |
I'm working on upgrading my blog to the latest version of Gatsby which also means an upgrade under the hood from React-Router 0.13 to 2.0.
I noticed that upon doing so, clicking on some links on the front page has became very slow. As in 1.2 seconds as shown in this performance audit.
Looking at the bottom up chart. Almost all the time is spent in the
matchPattern
function.My blog has roughly ~100 routes. Clicking around, some routes load near instantly and other take longer. When I added some console.logs, it turns out the
matchPattern
function is called for every route until there's a match. So if the route is near the top of the routes list, it's matched very quickly but if near the bottom, not so quickly. Surrounding the function with console.time, the function takes ~15ms of time each time it's called on my Macbook Pro ~2015 machine in Chrome.Given 15ms execution per route, even with as little as 7 routes, users can start seeing delays of over 100ms which according to Google's RAIL Performance Model is the point at which users start noticing system delays.
Version
2.0.2
Steps to reproduce
npm install -g gatsby
gatsby new perf-blog-test https://github.com/kyleamathews/blog; cd perf-blog; gatsby develop
Expected Behavior
Path matching takes less than 100ms
Actual Behavior
Path matching can take longer than 1000ms.
The text was updated successfully, but these errors were encountered: