CPU profiling
Introduction
CPU-intensive operations have a major impact on Node.js applications stability and response timings. Server-side rendering is exactly such a main CPU-intensive task in the tramvai
applications. SSR mainly consists of rendering React
application to HTML string and in a smaller degree global store initial state serialization, and both of this operations are synhronious.
For the most part, it is the rendering time to HTML string that limits the throughput of our application, the actual RPS and directly affects the response timings. Because of that, it is very important to perform load testing and profiling of rendering bottlenecks of our SSR applications. This guide will be focused on profiling, for load testing you can use such a great tools as autocannon and k6.
Profiling
Running the application
To debug the production version of the application, the tramvai start-prod <appName>
command with the --debug
flag is used.
⌛ Run the application:
tramvai start-prod <appName> --debug
After finishing the build and running the application, you will see a log like this: Debugger listening on ws://127.0.0.1:9229/356818ba-fa02-4484-82b3-76b1d57bbd7e
Connecting DevTools
⌛ Open chrome://inspect
url in browser
You will see a tab with connected application:
⌛ Press inspect
button
The DevTools tab will open:
Profiling methodology
At first, select specific page for profiling, for example
/foo/
routeAfter running the application, it is worth warming up caches for selected page.
⌛ Run
curl
once:curl http://localhost:3000/foo/
Then, we need to start profiling.
⌛ Press
start
button in DevTools tabAfter profiling started, send several requests per page, with short intervals.
⌛ Run
curl
multiple times:curl http://localhost:3000/foo/
# wait 2 seconds
curl http://localhost:3000/foo/
# wait 2 seconds
curl http://localhost:3000/foo/And we need to finish profiling
⌛ Press
stop
button in DevTools
After all steps, you will see a Chart with this requests:
Results interpretation
On a Chart diagramm you can easily find heaviest functions calls. In our example, we have synthetically created a heavy React
component, the rendering time of which took 30ms:
This is a really CPU-heavy task, and it's also likely to be much larger in your application than all the other tasks, called per single request.
We recommend paying attention to all tasks that run for more than a few milliseconds. Such tasks can be easily found in the Heavy table by looking at the Self Time column:
In this example, you can find a lot of internal React
work, and one internal tramvai
method call, larger than 2ms - buildPage
. In the real application, you will definitely see a safeStringify
method dehydrate near by, responsible for initial state serialization.
Share results
⌛ Press Save
button on current CPU profile:
⌛ Pack the report in the archive, because it can be very heavy
Summary
In this guide we learned the basics of profiling tramvai applications. Each application has its own peculiarities, profiling different pages will give different results. It is important to understand what workloads your application is handling, what response timings you might consider good from the user's point of view, and to profile the rendering performance regularly. Every millisecond saved will have a positive impact on server health under continuous high loads.