Skip to content

testing: clarify that b.N does not change during execution of the benchmark function #67137

@dolmen

Description

@dolmen

The testing documentation clearly mentions that the loop boundary variable might change during execution:

During benchmark execution, b.N is adjusted until the benchmark function lasts long enough to be timed reliably.

I noticed that range over integer loop have different behavior than for with clause loops when the loop boundary is changed by the loop body:
https://go.dev/play/p/cObUT32FcXc

	n := 4
	for i := 0; i < n; i++ {
		fmt.Println(n)
		n--
	}

	fmt.Println("----")

	n = 4
	for range n {
		fmt.Println(n)
		n--
	}
4
3
----
4
3
2
1

I have not found this difference in behavior explicitely mentioned in the Go specification. The following sentence speaks about the first evaluation of the range expression, but doesn't mention later evaluations.

The range expression x is evaluated once before beginning the loop, with one exception: if at most one iteration variable is present and len(x) is constant, the range expression is not evaluated.

I wonder how a benchmark would be affected by the use of range-over-integer instead of the documented style.

I have seen no clear mention of forbidding the use of range-over-integer with b.N for benchmarks. I have not seen either an explicit mention that range-over-int is compatible with testing.B benchmarks which makes me wonder if this has been considered. I also did a code search on range b.N in the Go repository and the only result is a test in the database/sql package just added less that 2 months ago by @bradfitz in CL 572119. No tests in testing.

So it would be helpful to have explicit documentation about the use of b.N with range-over-integer.

If this is in fact an incompatibility, it would be helpful to have a go vet alert about use of range-over-int on testing.B.N.

Go version

go version go1.22.2 darwin/arm64

Output of go env in your module/workspace:

N/A (reproducible on play.golang.org)

What did you see happen?

$ go doc -all testing | grep -c 'range b.N'
0

What did you expect to see?

$ go doc -all testing | grep -c 'range b.N'
1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions