Profiling
Measure first. This page lists the tools Wisp gives you, then the steps to profile a release build.
Server-Timing
WISP_SERVER_TIMING adds a Server-Timing header to every answer. It is on in wisp dev and off elsewhere. Set WISP_SERVER_TIMING=on to get it in a release build.
WISP_SERVER_TIMING=on ./my-app &
curl -sI http://127.0.0.1:3000/ | grep -i server-timing
# server-timing: total;dur=0.02
- A release build gives
total: the time from the start of the request to its answer, in milliseconds. - A dev build also gives
before(thebeforehook),handler(loads, actions and endpoints) andrender. Each shows only when that phase ran. - The browser shows these values in the Timing tab of a request in its network panel.
- The edge build (
wasm32) never sends the header.
WISP_LOG=json writes the time of each request in the ms field of its log line. See Logs, metrics and traces.
Devtools
In wisp dev, press Alt+Shift+W to open the devtools. They show the component tree with live props and state, a graph of what reads each signal, the route (params, server values and forms), the table of all routes and the timings. A release build has no devtools. See PWA and the Dev Loop.
Page weight
wisp build --analyze prints the JS, CSS and wasm bytes of each route, raw and gzipped, largest first. It builds nothing.
After the routes, it lists each file once as a module. It shows the heaviest modules and how many routes load each one. It also shows modules that have the same bytes under two names.
wisp build --analyze=json > bundle.json
wisp build --analyze=html
--analyze=json prints one JSON document for tools. It has total (raw and gzip bytes, module count), routes, modules and duplicates. A route has its raw and gzip bytes, its modules by name and its share. The share counts a file that n routes load as 1/n in each route. The shares of all routes add up to the gzip bytes of all modules.
--analyze=html writes a treemap page to target/wisp-analyze.html. Use --out <file> to choose another file. A relative path starts at the app folder, like the default. The first map has a cell for each module. The area of a cell is its gzip size. Point at a cell to see its name, its sizes and the routes that load it. Below the map, a table lists the routes, and each of the first 50 routes has a map of its own files. A map draws at most 300 cells and joins the smallest ones. The page is one file. It has no script and loads no outside file, so it works offline and with JavaScript off.
Profile the CPU of a release build
A release build of a new app has strip = "symbols", so a profiler cannot name the functions. Add a profile that keeps the names and the line tables. The code stays the same.
# Cargo.toml
[profile.profiling]
inherits = "release"
debug = "line-tables-only"
strip = "none"
Build it. The binary is in target/profiling/:
cargo build --profile profiling
Cargo sets PROFILE=release for a profile that inherits release, so the Wisp build script makes the release app.
Linux
- Install
perfand the cargo tools:cargo install flamegraph samply. - Start the app under the profiler:
samply record target/profiling/my-app, orperf record -F 999 --call-graph dwarf -- target/profiling/my-app.
- Send load from another terminal with the load tool you use.
- Stop the app with
Ctrl+C. Read aperfrecording withperf report, or open it insamplywithsamply import perf.data.
cargo flamegraph --profile profiling does steps 2 to 4 and writes flamegraph.svg.
If perf fails with a permission error, set kernel.perf_event_paranoid to 1 with sysctl.
macOS
- Run
cargo install samply. - Run
samply record target/profiling/my-app. - Send load. Stop the app with
Ctrl+C.samplyopens the profile in your browser.
Instruments (Time Profiler) also reads the binary. Choose the target/profiling/my-app file.
Windows
- Run
cargo install samply. - Run
samply record target\profiling\my-app.exe. - Send load. Stop the app with
Ctrl+C.samplyopens the profile in your browser.
Windows Performance Recorder also works. The .pdb file in target\profiling\ gives it the names.
Server code that waits for a database or the network shows little CPU. Use Server-Timing for those requests.
Instructions per request
The number of CPU instructions for one request does not change with load on the machine, so it is a good number to compare two builds. Use callgrind on Linux with valgrind installed.
-
Build with the profile above. Run the app with one worker, on one CPU, with the epoll driver:
WISP_THREADS=1 WISP_IO=epoll PORT=8099 \ taskset -c 0 valgrind --tool=callgrind \ --callgrind-out-file=cg.%p target/profiling/my-app -
Send 100 requests to warm the app. Then zero the counters:
callgrind_control -z. -
Send exactly 1000 requests to one route on one keep-alive connection.
-
Write the counters:
callgrind_control -d. Stop the app. -
Open the dump file
cg.<pid>.1. Thesummary:line is the number of instructions. Divide it by 1000 to get the instructions per request. -
For the split by function, run
callgrind_annotate cg.<pid>.1.
Do the same for the parent commit and compare. Change only one thing between the two runs.
Is This Page Useful?