Go, gccgo, and llgo Compared: Build Time, Binary Size, and Runtime
I tested the same programs with Go 1.22, gccgo 14, and llgo 0.10.0 on Ubuntu under WSL.
I passed the -O3 flag to gccgo throughout. I couldn’t find documentation for llgo, so I did not pass it any optimization flags. The llgo results are therefore not directly comparable.
These are measurements from my setup, not a rigorous compiler benchmark. In particular, the Fibonacci program is too small to draw strong conclusions from its nanosecond timings.
Hello, World!
Source code
package main
func main() {
println("Hello, World!")
}
Build time
$ ./build.sh
Building with standard Go compiler...
Successfully built optimized binary with Go (took 0 seconds)
Successfully built regular binary with Go (took 0 seconds)
Building with GCC Go compiler...
Successfully built optimized binary with GCC Go (took 0 seconds)
Successfully built regular binary with GCC Go (took 0 seconds)
Building with LLGO compiler...
Successfully built optimized binary with LLGO (took 5 seconds)
Successfully built regular binary with LLGO (took 4 seconds)
Build process completed
Binary size
Without extra linker flags:
$ ls -lh
total 1.6M
-rwxrwxrwx 1 iruanp iruanp 1.4M Mar 13 10:16 main
-rwxrwxrwx 1 iruanp iruanp 46K Mar 13 10:16 main_gccgo
-rwxrwxrwx 1 iruanp iruanp 143K Mar 13 10:16 main_llgo
With Go’s -s -w linker flags:
$ ls -lh
total 1.1M
-rwxrwxrwx 1 iruanp iruanp 869K Mar 13 10:16 main
-rwxrwxrwx 1 iruanp iruanp 23K Mar 13 10:16 main_gccgo
-rwxrwxrwx 1 iruanp iruanp 143K Mar 13 10:16 main_llgo
For this program, the binaries were largest with Go, followed by llgo and then gccgo.
Fibonacci sequence
Source code
package main
import (
"fmt"
"time"
)
func fibonacci(n int) uint64 {
if n <= 1 {
return uint64(n)
}
a, b := uint64(0), uint64(1)
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}
func main() {
// Using a hardcoded number for testing
n := 93
start := time.Now()
result := fibonacci(n)
duration := time.Since(start)
fmt.Printf("Fibonacci(%d) = %d\n", n, result)
fmt.Printf("Time taken: %v\n", duration)
}
Build time
$ ./build.sh
Building with standard Go compiler...
Successfully built optimized binary with Go (took 0 seconds)
Successfully built regular binary with Go (took 0 seconds)
Building with GCC Go compiler...
Successfully built optimized binary with GCC Go (took 1 seconds)
Successfully built regular binary with GCC Go (took 0 seconds)
Building with LLGO compiler...
Successfully built optimized binary with LLGO (took 8 seconds)
Successfully built regular binary with LLGO (took 7 seconds)
Build process completed
For this run, Go built the fastest, followed by gccgo and then llgo.
Binary size
Without extra linker flags:
$ ls -lh
total 3.4M
-rwxrwxrwx 1 iruanp iruanp 1.9M Mar 13 10:20 main
-rwxrwxrwx 1 iruanp iruanp 64K Mar 13 10:20 main_gccgo
-rwxrwxrwx 1 iruanp iruanp 1.5M Mar 13 10:20 main_llgo
With -s -w:
$ ls -lh
total 2.8M
-rwxrwxrwx 1 iruanp iruanp 1.2M Mar 13 10:20 main
-rwxrwxrwx 1 iruanp iruanp 37K Mar 13 10:20 main_gccgo
-rwxrwxrwx 1 iruanp iruanp 1.5M Mar 13 10:20 main_llgo
Runtime
First run:
$ ./run.sh
=== Running binaries from build directory ===
\nRunning: build/main
Fibonacci(93) = 12200160415121876738
Time taken: 118ns
Time taken: 0 seconds
\nRunning: build/main_gccgo
Fibonacci(93) = 12200160415121876738
Time taken: 169ns
Time taken: 0 seconds
\nRunning: build/main_llgo
Fibonacci(93) = 12200160415121876738
Time taken: 338ns
Time taken: 0 seconds
\n=== Running binaries from build-flags directory ===
\nRunning: build-flags/main
Fibonacci(93) = 12200160415121876738
Time taken: 119ns
Time taken: 0 seconds
\nRunning: build-flags/main_gccgo
Fibonacci(93) = 12200160415121876738
Time taken: 294ns
Time taken: 1 seconds
\nRunning: build-flags/main_llgo
Fibonacci(93) = 12200160415121876738
Time taken: 229ns
Time taken: 0 seconds
I ran the test a second time:
$ ./run.sh
=== Running binaries from build directory ===
\nRunning: build/main
Fibonacci(93) = 12200160415121876738
Time taken: 114ns
Time taken: 0 seconds
\nRunning: build/main_gccgo
Fibonacci(93) = 12200160415121876738
Time taken: 169ns
Time taken: 0 seconds
\nRunning: build/main_llgo
Fibonacci(93) = 12200160415121876738
Time taken: 244ns
Time taken: 0 seconds
\n=== Running binaries from build-flags directory ===
\nRunning: build-flags/main
Fibonacci(93) = 12200160415121876738
Time taken: 130ns
Time taken: 0 seconds
\nRunning: build-flags/main_gccgo
Fibonacci(93) = 12200160415121876738
Time taken: 172ns
Time taken: 0 seconds
\nRunning: build-flags/main_llgo
Fibonacci(93) = 12200160415121876738
Time taken: 262ns
Time taken: 0 seconds
I don’t know whether llgo has additional optimization flags. Without -O3, gccgo ran at about the same speed as llgo in my tests.
These results suggest that runtime performance was broadly similar for this small program.
Conclusion
For me, there is no reason to spend time configuring gccgo or llgo for these programs. Just use the standard Go compiler:
go build cmd/main.go