After publishing several version-to-version runtime benchmarks, we received a lot of requests to test Go next.
So we continued the series with the latest patch releases of Go 1.19 through Go 1.26. We measured a sustained synthetic application alongside focused microbenchmarks to see how throughput, latency, garbage collection, CPU use, memory use, and common hot paths evolved across releases.
Versions tested
We tested official Go Docker images at these exact patch releases:
- Go 1.19.13 (
golang:1.19.13) - Go 1.20.14 (
golang:1.20.14) - Go 1.21.13 (
golang:1.21.13) - Go 1.22.12 (
golang:1.22.12) - Go 1.23.12 (
golang:1.23.12) - Go 1.24.13 (
golang:1.24.13) - Go 1.25.12 (
golang:1.25.12) - Go 1.26.5 (
golang:1.26.5)
Every image passed the benchmark self-test and test suite before measurement.
What the synthetic application does
The application combines work that appears in ordinary services: scanning fields in a deterministic 1,024-byte JSON payload, SHA-256 hashing, a sharded bounded cache, Base64 encoding, and 256 bytes of allocation pressure per operation. A pool of 1,024 payloads keeps the input stable while 10 worker goroutines run against 10 available container processors.
Each application process warmed up for 20 seconds and then measured for 600 seconds. We repeated each Go-version and memory-profile combination in five isolated processes.
Synthetic application results
The charts compare GOMEMLIMIT targets of 512MiB, 2GiB, and 4GiB. This setting guides garbage collection rather than reserving a fixed-size heap, and the workload remained below even the smallest target, so minor differences between profiles should be treated as run-to-run variation.
Throughput
Average throughput in millions of operations per second across five repeats.
Mean latency
Average per-operation latency in microseconds across five repeats.
Total GC pause time
Average total garbage-collection pause time during the measurement window.
CPU use
Average process CPU utilization as a percentage of all available cores.
Resident memory
Average resident set size in MiB.
Go heap memory
Average live Go heap memory in MiB.
Go Sys memory
Average memory obtained from the operating system by the Go runtime.
Application behavior over time
These 4GiB charts include all eight Go versions and use 60-second points from the recorded per-second series. Each point is the median across the five repeats.
Heap over time
Go heap memory in MiB during the 10-minute measurement window.
RSS over time
Resident set size in MiB during measurement.
Go Sys over time
Memory obtained from the operating system by the Go runtime during measurement.
CPU over time
Process CPU utilization as a percentage of all cores during measurement.
Microbenchmark results
Each focused benchmark used a 5-second warmup, a 10-second measurement, and five isolated process repeats. The charts show the 4GiB profile so the version trend stays readable, the 512MiB and 2GiB results followed the same broad story.
JSON parsing
Repeated parsing of the deterministic 1,024-byte JSON payload.
JSON encoding
Repeated serialization of the benchmark object to JSON.
SHA-256
Repeated SHA-256 digest calculation on the same payload size.
Base64 encoding
Repeated Base64 encoding and decoding on the same payload size.
Regular expressions
Repeated field matching in a short string using a regular expression.
Integer sorting
Repeated sorting of an integer slice.
Concurrent map churn
High-churn concurrent map read and write workload.
Deflate compression
Repeated Deflate compression on the same payload size.
How the tests were performed
- Host: Apple M4, 10 cores, 16 GiB RAM
- OS: Darwin 26.3, Docker 29.1.3
- Runtime: official Go images, latest patch of Go 1.19 through 1.26
- Profiles:
GOMEMLIMIT=512MiB,2GiB, and4GiB, thenGOGC=100 - Concurrency: 10 worker goroutines and 10 available container processors
- Synthetic app: 20-second warmup, 600-second measurement, five isolated repeats
- Microbenchmarks: 5-second warmup, 10-second measurement, five isolated repeats
- Summary metrics: arithmetic means across the five repeats
- Time-series metrics: per-second medians across the same repeats
Conclusion
These results show why runtime upgrades are worth measuring with more than one headline number. An overall application and its individual hot paths do not always move together, and the differences that matter depend on where a real service spends its time.
Before upgrading, benchmark the request paths, allocation patterns, and library operations that matter to your application. The charts above are a reference point, not a substitute for testing your own workload.
Which Go workload should we put through the suite next? Send us your ideas.
Happy Benchmark Monday!