Measure performance
Measure a real route or query before you change it. Include authentication, database access and rendering when they are part of the path.
#Ask your agent
Read https://cicadel.fauzaanu.com/en/dev/howto/performance/index.md.
Investigate [slow route or operation] in this application.
Create a representative benchmark, record latency and allocations, and profile it.
Fix the measured bottleneck while preserving behavior and permissions.
Compare repeated runs and report the workload and any remaining uncertainty.#Benchmark a handler
Add a benchmark next to the handler you want to measure. This template uses a generated notes app. Replace it with your real handler and its dependencies.
package notes
import (
"net/http/httptest"
"testing"
)
func BenchmarkIndex(b *testing.B) {
request := httptest.NewRequest("GET", "/notes/", nil)
b.ReportAllocs()
for b.Loop() {
recorder := httptest.NewRecorder()
Index(recorder, request)
if recorder.Code != 200 {
b.Fatalf("status = %d", recorder.Code)
}
}
}Run it five times:
go test ./apps/notes -run '^$' -bench BenchmarkIndex -benchmem -count=5This measures the handler directly. It leaves out network, middleware and database costs unless your setup includes them.
#Profile the operation
go test ./apps/notes -run '^$' -bench BenchmarkIndex -benchtime=5s \
-cpuprofile=cpu.out -memprofile=mem.out
go tool pprof -top cpu.out
go tool pprof -top -alloc_space mem.outFor database work, load representative rows into a disposable PostgreSQL database.
- Check query counts with the application testing helpers.
- Inspect slow SQL with PostgreSQL’s query planner.
#Compare a change
- Keep the same inputs, dataset and concurrency before and after the change.
- Record errors as well as latency.
- Repeat the runs. One short run is not enough to claim a speedup.
Do not use a development server’s latency as your production capacity estimate.
#Compared with Django
The documentation home page compares the standard Cicadel project with a vanilla Django 6.1 project on the same machine, both on PostgreSQL with auth, sessions and CSRF.
| Measure | Cicadel | Django |
|---|---|---|
| Container image | 35 MB | 320 MB |
| Memory at idle | 24 MB | 86 MB (1 gunicorn worker) |
| Requests per second | ~23,000 | ~10,300 (10 gunicorn workers, 581 MB) |
| Median response | 1 ms | 2 ms (10 workers) |
The method, versions, raw results and a harness to reproduce them are in the comparison notes. Re-run it on your own hardware before relying on a number.