aboutsummaryrefslogtreecommitdiff
path: root/src/testing
diff options
context:
space:
mode:
authorJunyang Shao <shaojunyang@google.com>2024-12-04 20:11:18 +0000
committerGo LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-12-05 21:03:13 +0000
commit5213e1e56c17bde612398dac8a2996bf020fe542 (patch)
tree0d03f98382b6dadd6d9ab8fbd9d7b01687f4f772 /src/testing
parent32e19fc4397142b743646ff8a526d07c126bf89b (diff)
downloadgo-5213e1e56c17bde612398dac8a2996bf020fe542.tar.xz
testing: improve documentation, examples, release notes for
testing.b.Loop. This CL added documentation of the no-inlining semantic of b.Loop, with a concrete example. This CL also tries to improve the release note to be more descriptive. Fixes #61515 Change-Id: I1e13cc92d5d6bdbf40fb44f44475e249747b807f Reviewed-on: https://go-review.googlesource.com/c/go/+/633536 Reviewed-by: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Commit-Queue: Junyang Shao <shaojunyang@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: David Chase <drchase@google.com>
Diffstat (limited to 'src/testing')
-rw-r--r--src/testing/benchmark.go16
-rw-r--r--src/testing/benchmark_test.go30
2 files changed, 46 insertions, 0 deletions
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
index 67a5abccb4..dbc0814884 100644
--- a/src/testing/benchmark.go
+++ b/src/testing/benchmark.go
@@ -405,6 +405,22 @@ func (b *B) loopSlowPath() bool {
// A benchmark should either use Loop or contain an explicit loop from 0 to b.N, but not both.
// After the benchmark finishes, b.N will contain the total number of calls to op, so the benchmark
// may use b.N to compute other average metrics.
+//
+// The parameters and results of function calls inside the body of "for b.Loop() {...}" are guaranteed
+// not to be optimized away.
+// Also, the local loop scaling for b.Loop ensures the benchmark function containing the loop will only
+// be executed once, i.e. for such construct:
+//
+// testing.Benchmark(func(b *testing.B) {
+// ...(setup)
+// for b.Loop() {
+// ...(benchmark logic)
+// }
+// ...(clean-up)
+// }
+//
+// The ...(setup) and ...(clean-up) logic will only be executed once.
+// Also benchtime=Nx (N>1) will result in exactly N executions instead of N+1 for b.N style loops.
func (b *B) Loop() bool {
if b.loopN != 0 && b.loopN < b.N {
b.loopN++
diff --git a/src/testing/benchmark_test.go b/src/testing/benchmark_test.go
index 1f55fa5060..259b70ed4c 100644
--- a/src/testing/benchmark_test.go
+++ b/src/testing/benchmark_test.go
@@ -149,6 +149,36 @@ func TestBLoopHasResults(t *testing.T) {
}
}
+func ExampleB_Loop() {
+ simpleFunc := func(i int) int {
+ return i + 1
+ }
+ n := 0
+ testing.Benchmark(func(b *testing.B) {
+ // Unlike "for i := range N {...}" style loops, this
+ // setup logic will only be executed once, so simpleFunc
+ // will always get argument 1.
+ n++
+ // It behaves just like "for i := range N {...}", except with keeping
+ // function call parameters and results alive.
+ for b.Loop() {
+ // This function call, if was in a normal loop, will be optimized away
+ // completely, first by inlining, then by dead code elimination.
+ // In a b.Loop loop, the compiler ensures that this function is not optimized away.
+ simpleFunc(n)
+ }
+ // This clean-up will only be executed once, so after the benchmark, the user
+ // will see n == 2.
+ n++
+ // Use b.ReportMetric as usual just like what a user may do after
+ // b.N loop.
+ })
+ // We can expect n == 2 here.
+
+ // The return value of the above Benchmark could be used just like
+ // a b.N loop benchmark as well.
+}
+
func ExampleB_RunParallel() {
// Parallel benchmark for text/template.Template.Execute on a single object.
testing.Benchmark(func(b *testing.B) {