-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
perf(remix-server-runtime): Performance improvements for large apps #4748
Conversation
🦋 Changeset detectedLatest commit: 72a3c70 The changes in this PR will be included in the next version bump. This PR includes changesets to release 18 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Hi @dmarkow, Welcome, and thank you for contributing to Remix! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run. Thanks! - The Remix team |
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
65882f9
to
aa5b6dc
Compare
Just updated to remove the caching logic in |
.map(([id, route]) => ({ | ||
// Create a map of routes by parentId to use recursively instead of | ||
// repeatedly filtering the manifest. | ||
routesByParentId ||= groupRoutesByParentId(manifest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@machour - would the logical or assignment (|=) be transpiled correctly for older browsers in Remix's build process?
I recall recently that null coalescing operator was removed:
#4561
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I know, this code will only run on the server, so it shouldn't cause any problem with the browser
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We ran into an issue on node with this on node 14 that should be fixed since we changed the target in our babelrc
, but that might be worth confirming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up removing the ||=
in df6f364 and switched it to use a default param value
Thanks @dmarkow! I copied your approach over to where we do the same route-tree generation client side as well and switched to use default param values instead of From a perf testing standpoint, I created a net-new app with cd app/routes
for N in $(seq 0 1 1000); do cp index.tsx route-${N}.tsx; done Then I booted up the app with
As stated this isn't an issue in prod since we only pay the cost once on app startup. but the client-side change should squeek out a small perf improvement on hydration. |
🤖 Hello there, We just published version Thanks! |
Closes: #4733
Testing Strategy: All existing tests are still passing. Since this is only performance-related, not sure of any new tests to add. Also running these patches in production with no issues so far.
In apps with many routes, there were some performance issues causing some remix overhead for every request. In my production app (700+ routes), 200ms on average was being added to every request, before my loaders/actions even ran. I narrowed this down to route parsing.
requestHandler
increateRequestHandler
was callingcreateStaticHandlerDataRoutes
on every request, which is an expensive recursive function that creates a nested set of data route objects. In production,build.routes
shouldn't be changing between requests, so the result is now cached and only recalculated ifloadContext
changes. In development, nothing will be cached sincecreateRequestHandler
is called on every request.createStaticHandlerDataRoutes
andcreateRoutes
were recursive functions which repeatedly filtered the entire list of routes. I extracted agroupRoutesByParentId
function to create an object indexed byparentId
. The two functions now call this once, and pass the results down recursively avoiding the repeat filtering. In my app, this dropped each of these functions from 50-100ms down to around 1ms each.requestHandler
. That increased the overhead back up to 10-20ms when the server is under load. So I'm inclined to leave the caching in place as a cheap way to shave those few ms off the response time.