How much faster is Go now than in the past? Release notes give the results of small tests. Most blog posts compare only two releases. We wanted the full history. Thus, we used the same programs and the same source code on each Go release from 1.2 to 1.27. We measured all of them on one machine.
Summary:
- Go 1.27 is 11.5% faster than Go 1.23. It is approximately 41% faster than Go 1.2.
- Most of the improvement occurred in two periods. The first period is go1.5 to go1.7, when the compiler changed. The second period is go1.24 to go1.27, when the runtime changed.
- binary-trees is a test of the garbage collector (GC). With pre-allocated nodes, the program is approximately 10× faster. This speed is approximately equal to C.*
- Energy use follows elapsed time. Go 1.27 uses 11.5% less energy than Go 1.23.
- In a 2017 study by Pereira et al., Go used 3.23× the energy of C. With go1.27, this value is approximately 2.25×. With pre-allocated binary-trees, the value is approximately 1.57×.*
- Most of the difference between Go and C comes from SIMD instructions. Against the fastest C programs without SIMD, Go takes 0–30% more time on these tasks.
Test procedure
| item | description |
|---|---|
| programs | The fastest Go program for each of the 10 Benchmarks Game tasks. We also used pure-Go programs for pidigits and regex-redux, because their fastest programs use C libraries (GMP and PCRE). Thus, there are 12 programs. |
| source code | Five programs use functions that are not in Go 1.2, for example strings.Builder and sort.Slice. We changed one line in each program. The changed programs give the same output. On go1.27, they have the same speed as the original programs. |
| Go releases | The latest patch of each minor release, from go1.2.2 to go1.27.1. This is 26 releases. We downloaded each release from the Go module proxy. We compared each release with the Go checksum database. |
| machine | A GCP c3-standard-8 virtual machine with SMT disabled. It has 4 physical cores (Xeon Platinum 8481C) and 32 GB of RAM. |
| runs | 5 rounds. Each round runs each program on each release. Before each round, we cleared the page cache. |
We did these checks:
- We compared the output of each program with the reference output from the Benchmarks Game. The full-size output was the same on all 26 releases, byte for byte.
- We did 1,560 timed runs. The hypervisor steal time was 0 s.
- The median variation between runs was 0.29%.
The test harness, the raw data and the procedure are on GitHub: github.com/nrnjn42/go-bench. A full test takes approximately 5 hours. The cost is approximately $2.
go1.23 compared with go1.27
Go 1.27 is 11.5% faster than Go 1.23 (geometric mean of elapsed time).
| program | go1.23.12 | go1.27.1 | change | cause |
|---|---|---|---|---|
| k-nucleotide | 6.67 s | 2.83 s | −57.5% | Swiss-table maps (go1.24) |
| binary-trees | 8.77 s | 6.08 s | −30.7% | GC and memory allocator. −14% in go1.27 only |
| pidigits (pure Go) | 1.74 s | 1.46 s | −15.9% | math/big (go1.25) |
| regex-redux (PCRE) | 2.31 s | 2.15 s | −7.1% | faster cgo calls (go1.26) |
| regex-redux (pure Go) | 18.27 s | 17.92 s | −1.9% | |
| fannkuch-redux | 7.46 s | 7.35 s | −1.5% | |
| n-body, mandelbrot, fasta, spectral-norm, pidigits (GMP) | ±1% | |||
| reverse-complement | 1.66 s | 1.72 s | +3.1% | peak memory +29% |
| geometric mean | −11.5% | CPU time −12.3% |
Programs that use maps, the memory allocator or the garbage collector are much faster. Numeric loops did not change. n-body, mandelbrot and spectral-norm use the same machine code as in older releases.
All releases
The table gives the geometric mean of elapsed time for each release. The values are relative to go1.27.1. A value of 1.00 is equal to go1.27. A higher value is slower.
| go1.2 | 1.4 | 1.5 | 1.6 | 1.7 | 1.8 | 1.10 | 1.13 | 1.16 | 1.20 | 1.23 | 1.24 | 1.25 | 1.26 | 1.27 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1.69 | 1.83 | 1.42 | 1.39 | 1.21 | 1.16 | 1.16 | 1.12 | 1.14 | 1.14 | 1.13 | 1.06 | 1.04 | 1.01 | 1.00 |
There are three periods:
- go1.2 to go1.7: changes to the compiler. In go1.5, the Go team rewrote the compiler from C to Go. In go1.7, they added the SSA back end. At go1.7, the CPU time of n-body decreased by half. The time of fannkuch-redux decreased by approximately 30%.
- go1.8 to go1.23: little change. In seven years, the speed of these programs changed by approximately 3%. In this period, much of the runtime work decreased GC pauses. That work improves latency. These tests measure throughput, not latency.
- go1.24 to go1.27: changes to the runtime. Swiss-table maps,
math/big, cgo, the GC and the memory allocator changed. Together, these changes give approximately 12% in four releases.
CPU time is user time plus system time on all threads. The CPU time chart shows the changes most clearly:
- n-body at go1.7.
- k-nucleotide at go1.24.
- binary-trees decreases in each release, because the GC does less work.
The peak memory of the numeric programs does not change. The peak memory of binary-trees, regex-redux and reverse-complement changes with the GC settings of each release.
Build time is the time to compile and link the program. The standard library was already built. The build time is approximately 0.15 s on all releases. It is higher on go1.5 and go1.6, after the compiler rewrite.
Energy: a short run time is more important than few cores
We did not measure power directly. We used the model from van Kempen et al., It’s Not Easy Being Green (2024). We fitted the model again to their raw RAPL data (3,192 runs, 13 languages). This is the result:
energy ≈ 1.99 J × CPU-seconds + 286.6 J × elapsed-seconds (R² = 0.97)
CPU time alone does not explain their data (R² ≈ 0). Their server uses approximately 287 W when it is idle. Thus, the run time of a program has a much larger effect on energy than the number of cores that it uses. With this model, go1.27.1 uses 11.5% less energy than go1.23.12. It uses 14% less energy than go1.8.
The 2017 study “Energy Efficiency across Programming Languages”*
Pereira et al. published this study in 2017. Many people share its table of results. In that table, Go uses 3.23× the energy of C (14th of 27 languages). Go uses 2.83× the time and 1.05× the memory of C.
The study does not give the Go release. These facts help to find it:
- The test machine used Ubuntu 16.10.
- The authors published the code on 2017-08-28. Go 1.9 was released four days before that date.
- Go 1.10 was released in February 2018.
Thus, the study most probably used go1.7 or go1.8.
We used their Go programs on go1.8 and go1.27. Then we scaled their raw results for each test. Their combined score is the arithmetic mean of the energy in joules. Thus, binary-trees has the largest effect on the score: it uses 642 J of the 1,502 J total for Go.
| combined score of Go (1.00 = C) | energy | time | approximate energy rank |
|---|---|---|---|
| study, 2017 | 3.25 | 2.83 | 14th of 27 |
| same programs on go1.27 | ~2.25 | 2.07 | ~8th |
| go1.27 with pre-allocated binary-trees | ~1.57 | 1.56 | ~4th |
We did not measure the other languages again. Their values are from 2017. Thus, the ranks are only approximate.
binary-trees without the GC*
binary-trees allocates millions of small nodes. Thus, it is a test of the garbage collector. We wrote a different version of the program:
- Each worker keeps its nodes in a pre-allocated slice.
- The nodes use
int32indices to their child nodes, not pointers. - The GC does not scan the nodes.
- After the program starts, it does not allocate memory.
The Benchmarks Game does not accept this type of program, because its rules do not permit custom memory pools. The C program gets the same result from the APR pool library.
| go1.27 | Go #2 | pre-allocated |
|---|---|---|
| elapsed | 11.28 s | 1.09 s |
| CPU | 42.85 s | 3.41 s |
| peak memory | 594 MB | 131 MB |
The pre-allocated version is approximately 10× faster. This speed is approximately equal to C.
Our first version was slower on four cores than on one core. The cause was false sharing. The four slice headers (24 bytes each) were in one cache line. We increased the size of each header to 128 bytes. Then the time decreased from 8.3 s to 1.09 s.
Why is Go slower than C?
Memory zeroing is not the cause. Go sets memory to zero when it allocates memory, and these loops allocate almost no memory.
The primary cause is hand-written SIMD instructions. On the Benchmarks Game leaderboard, the fastest C program has the label “possible hand-written vector instructions” in 5 of the 10 tasks. The source code shows these instructions:
- AVX (
__m256d) in n-body and spectral-norm. - SSE2 in mandelbrot.
_mm_shuffle_epi8byte shuffles in fannkuch-redux and reverse-complement.
The table compares Go with the fastest C program, and with the fastest C program without SIMD (scalar C):
| task | Go ÷ fastest C (SIMD) | Go ÷ fastest scalar C |
|---|---|---|
| n-body | 3.0× | 1.28× |
| spectral-norm | 3.6× | 1.00× |
| mandelbrot | 2.9× | 0.93× |
| fannkuch-redux | 3.9× | 1.15× |
These items cause the remaining difference:
- The Go compiler does fewer optimizations, because it is designed for fast builds.
- The C programs use
-march=<cpu>. The Go programs useGOAMD64=v2. - Go does bounds checks.
- The GC adds time to programs that allocate much memory.
- Some C programs use C libraries, for example PCRE2-JIT and khash.
Conclusions
- Use the latest release. go1.27 is 11.5% faster than go1.23 on these programs, with no changes to the code. Programs that use many maps or allocate much memory get a larger improvement.
- Recent improvements come from the runtime. Maps, the GC,
math/bigand cgo are faster. The speed of numeric code has not changed since go1.7. - For programs that allocate much memory, a change to the memory layout can give a 10× improvement. An example is a pointer-free arena. This improvement is much larger than the improvement from a new release.
- Most “Go against C” rankings measure SIMD. Against scalar C, Go takes 0–30% more time on the CPU-bound tasks.
Limits of the results
- These programs are small tests. They measure throughput. They do not measure latency or GC pause times.
- All results come from one machine (Intel Sapphire Rapids). The relative changes are probably similar on other hardware. The absolute times are different.
- We calculated the energy with a model. We did not measure it.
- You can repeat all tests with the go-bench repository.
* Sections with an asterisk use results from a shared development VM, not from the GCP machine. These results are less precise than the other results.




