I'm playing around with benchmarks in Go and I have a simple function that just sleeps for 5 Nanoseconds, however when I run a benchmark test it shows 298.1 ns/op
. I'm curious why is that. Shouldn't it be 5 ns/op
?
Go version:
go version go1.19 linux/amd64
The code:
package andrei
import (
"testing"
"time"
)
func Hi() {
time.Sleep(5 * time.Nanosecond)
}
func BenchmarkHi(b *testing.B) {
for i := 0; i < b.N; i {
Hi()
}
}
The results:
$ go test -run none -bench . -benchmem ./andrei
goos: linux
goarch: amd64
pkg: andrei/andrei
cpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
BenchmarkHi-8 3861392 298.1 ns/op 0 B/op 0 allocs/op
PASS
ok andrei/andrei 1.470s
CodePudding user response:
time.Sleep
only guarantees that it will sleep at least as long as the argument. How long it actually sleeps depends on your operating system and other factors.
On Windows, it sleeps at least 1.9ms. On my MacBook, I get 408 ns/op, and you are seeing 298.1 ns/op.
You can find out more details about this problem in this ticket in the Go github repository:
https://github.com/golang/go/issues/29485
CodePudding user response:
The article "How to Write Accurate Benchmarks in Go" (Teiva Harsanyi, Aug. 2022) and the Benchmarks wiki page both mention perflock
(on Linux):
We should make sure the machine executing the benchmark is idle. However, external processes may run in the background, which may affect benchmark results.
For that reason, tools such as
perflock
can limit how much CPU a benchmark can consume.For example, we can run a benchmark with 70% of the total available CPU, giving 30% to the OS and other processes and reducing the impact of the machine activity factor on the results.
See also "issues 44343: runtime
: time.Sleep
takes more time than expected".
For Linux, we should use
epoll_pwait2
, like https://go.dev/cl/363417.
This system call is on the newer side, but this will improve things going forward and provide a workaround (upgrade the kernel) for particularly affected users.