Between foo() exiting and the hardware timer being read, there is * The call to StopTimer() * A conditoinal branch * A call to time.Since * Another conditional * A call to either Now() or runtimenano()
Now calls might get inlined, and the branches get predicted, but I doubt the overhead of all that is zero and, it can be significant compared to a very fast `foo()` function. Also, after Start/Stop timer is called, there's a call to runtime.ReadMemStats() which does nontrivial stuff and could thrash the caches, slowing down the actual benchmark code. The docs for StopTimer say: > StopTimer stops timing a test. This can be used to pause the timer while > performing complex initialization that you don't want to measure. So that sounds like the use-case is to call Stop-, StartTimer once before you enter the main loop of the benchmark. They not efficient enough for a tight inner loop. On Wed, 25 Mar 2020 at 06:51, Jake Montgomery <jake6...@gmail.com> wrote: > > Strange. I hope someone has a real answer for you. > > In the meantime, you can simplify your example to demonstrate the issue: > > package demo_test > > import ( > "testing" > ) > > var Foo1 []string > var Count int = 8 > > func Benchmark1(b *testing.B) { > for i := 0; i < b.N; i++ { > Foo1 = foo(Count) > } > } > > func Benchmark2(b *testing.B) { > b.StopTimer() > for i := 0; i < b.N; i++ { > // Hypothetical setup here > b.StartTimer() > Foo1 = foo(Count) > b.StopTimer() > } > } > > > func foo(count int) []string { > testData := []string{} > for i := 0; i < count; i++ { > testData = append(testData, "a") > } > > return testData > } > > I get: > > goos: windows > goarch: amd64 > Benchmark1-4 2101567 584 ns/op > Benchmark2-4 1000000 1668 ns/op > PASS > > So it appears that StopTimer() and StartTimer() are introducing some > overhead. I am surprised that it is this large. > > Good Luck > > > > On Tuesday, March 24, 2020 at 12:24:08 PM UTC-4, Orson Cart wrote: >> >> I posted this earlier but I realised that the code had a fundamental error >> in it. I've corrected here it but the underlying problem still exists. >> >> I've recently started using go test's benchmarks support and I'm >> particularly interested in understanding the benchmark timer functions. I've >> been getting results that I found surprising and I was wondering if anyone >> could explain what's going on here. >> >> The code below has three benchmarks that each invoke a single function >> (foo). The implementation of foo isn't important, it's just there to consume >> some time: >> - foo is called once per iteration in Benchmark1. >> - It's called twice per iteration in Benchmark2 so I'd expect Benchmark2's >> duration to be nominally twice that of Benchmark1. >> - It's also called twice per iteration in Benchmark3 but the first call is >> wrapped in b.StopTimer and b.startTimer calls. Because of this I'd have >> expected Benchmark3 to be about the same duration as Benchmark1 >> >> Apologies for the length of the example but I didn't think it fair to ask >> the question and leave anything out. >> >> package demo_test >> >> import ( >> "strconv" >> "testing" >> ) >> >> var Foo1 []string >> var Foo2 []string >> var Count int = 32767 >> >> func Benchmark1(b *testing.B) { >> for i := 0; i < b.N; i++{ >> Foo1 = foo(Count) >> } >> } >> >> func Benchmark2(b *testing.B) { >> for i := 0; i < b.N; i++{ >> Foo1 = foo(Count) >> Foo2 = foo(Count) >> } >> } >> >> func Benchmark3(b *testing.B) { >> for i := 0; i < b.N; i++{ >> b.StopTimer() >> Foo1 = foo(Count) >> b.StartTimer() >> Foo2 = foo(Count) >> } >> } >> >> func foo(count int) []string{ >> testData := []string{} >> for i:= 0; i < count; i++ { >> testData = append(testData, strconv.Itoa(i)) >> } >> >> return testData >> } >> >> >> When the benchmarks are run the results are as follows: >> >> Benchmark1-4 351 3345215 ns/op >> Benchmark2-4 166 7206582 ns/op >> Benchmark3-4 334 3457907 ns/op >> PASS >> ok bar.com/benchmarks 6.881s >> >> OK benchmark3 is a little slower than Benchmark1 but that's not what's >> bothering me. It's this: if I now change Count to something much smaller the >> results are a surprise, at least to me. Here are the results when Count = 8: >> >> Benchmark1-4 2706196 442 ns/op >> Benchmark2-4 1357482 873 ns/op >> Benchmark3-4 840729 1387 ns/op >> PASS >> ok bar.com/benchmarks 23.547s >> >> The ratio of timings for Benchmark1 and Benchmark2 are roughly in line with >> expectations but I was surprised to see that the timings for Benchmark3 are >> now larger than those for Benchmark2. >> >> Can anyone explain this? >> >> TIA >> Orson > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/feda7e38-5d1f-43cf-b0cd-98db0a94d3c9%40googlegroups.com. -- Adrian Ratnapala -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAN%2BHj7jEvkOBejV6bZbBmQEcapKafUvBAJKmAhoVGg%2BC7PwEEg%40mail.gmail.com.