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

Improve performance SSR rendering #36

Open
woutdp opened this issue Apr 27, 2023 · 3 comments
Open

Improve performance SSR rendering #36

woutdp opened this issue Apr 27, 2023 · 3 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@woutdp
Copy link
Owner

woutdp commented Apr 27, 2023

Currently SSR takes about 8ms each render in production, and about 20-40ms in dev, and this is being executed synchronously.
So let's say you have 10 components inside a LiveView, it'll end up taking 80ms!

When turning off SSR it's super fast, you can easily render a 100 components and not feel any delay.

The culprit is the call to NodeJS.

Doing the following things will probably do the trick.

  • Reduce the amount of time to call NodeJS
  • Run asynchronously somehow, I'm not sure how to do this inside a HEEx template.

Maybe Bun is the answer here, or a better way of calling Node.

@woutdp woutdp added the enhancement New feature or request label Apr 27, 2023
@woutdp woutdp added the help wanted Extra attention is needed label Aug 11, 2023
@voughtdq
Copy link
Contributor

I had a moment to ponder this and I'm thinking of how to approach "async EEx".

This would all need to be done at compile time. It might be necessary to wrap Phoenix's Live View EEx engine. We would need to traverse the generated AST, replacing the SSR-flagged Svelte components with assigns and accumulating them in a module attribute. Prior to rendering, we would send the SSR calls with something like Task.async_stream, await them, then inject the assigns. Kind of messy, but it should provide a speed boost. One concern is that this would mess with Live View's change tracking.

@voughtdq
Copy link
Contributor

voughtdq commented Oct 5, 2023

So I've been trying to perf this out on the server side and I think there are really only some small optimizations we can do to get a slightly faster response.

Here's an example from using a Unix domain socket server to host render requests in Node.js:

[info] render _build/Elixir.ExampleWeb.LiveSigil: 1099µs
[info] Sent 200 in 17ms

The render time inside Node.js is very fast - usually about a tenth of a millisecond. The rest of that time is spent serializing/deserializing the data. But still, 1ms is pretty fast for elixir -> node.js -> elixir. This response time can be improved by using something like NimblePool and checking out sockets rather than having a GenServer manage them
(and there might be some perf gain in passively reading from the socket instead of using message passing).

My next thought was maybe there are some hidden performance issues with Phoenix rendering. None there either, usually at most 1ms. There are some cases where I've noticed a slower render performance, but I'm getting pretty consistent 2ms for the total server-side render time (that is, the full request to Node.js to request render and returning the final template that gets sent to the browser).

I removed the phoenix svg logo and that actually gave us better response times lol.

Next thing was to remove app.css and app.js. That surprisingly speeds things up by about 5ms. Obviously those are needed, though.

I don't fully understand how defer works for browsers, but somehow loading app.js is making the server-side response look slower. I did notice the app bundle was pretty large. It might make sense to investigate code splitting. That way, only the Svelte components on the page can be called. This would reduce the bundle size.

Let me know if you have any thoughts. We might need to start an external discussion about this because it actually touches a lot of different parts.

@woutdp
Copy link
Owner Author

woutdp commented Oct 6, 2023

Thank you for investigating.

Do you have some code so I can reproduce some of the results?

Could be cool to sort of have a waterfall visualization of what's going on in each step. I'm very unfamiliar with all of this, but it looks like actually Node is not so slow, it's mostly the back and forward passing of the generated html/js/css that's taking more time.

@absowoot absowoot mentioned this issue Oct 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants