aboutsummaryrefslogtreecommitdiff
path: root/src/internal/testenv
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2022-06-10 16:21:46 +0000
committerMichael Knyszek <mknyszek@google.com>2022-06-13 20:15:55 +0000
commit1fe2810f9ca0dcd34e473f852102e2a49d45d7d8 (patch)
tree429e31e7529c6066e3529e96078107f85afce083 /src/internal/testenv
parent6130461149020d2b4b91fb183afa388a211cadc5 (diff)
downloadgo-1fe2810f9ca0dcd34e473f852102e2a49d45d7d8.tar.xz
sync: move lock linearity test and treat it like a performance test
This change moves test/locklinear.go into the sync package tests, and adds a bit of infrastructure since there are other linearity-checking tests that could benefit from it too. This infrastructure is also different than what test/locklinear.go does: instead of trying really hard to get at least one success, we instead treat this like a performance test and look for a significant difference via a t-test. This makes the methodology behind the tests more rigorous, and should reduce flakiness as transient noise should produce an insignificant result. A follow-up CL does more to make these tests even more robust. For #32986. Change-Id: I408c5f643962b70ea708930edb4ac9df1c6123ce Reviewed-on: https://go-review.googlesource.com/c/go/+/411396 Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/internal/testenv')
-rw-r--r--src/internal/testenv/testenv.go65
1 files changed, 65 insertions, 0 deletions
diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go
index 1feb630cf5..b7cb95063b 100644
--- a/src/internal/testenv/testenv.go
+++ b/src/internal/testenv/testenv.go
@@ -16,6 +16,7 @@ import (
"flag"
"fmt"
"internal/cfg"
+ "internal/testmath"
"os"
"os/exec"
"path/filepath"
@@ -463,3 +464,67 @@ func RunWithTimeout(t testing.TB, cmd *exec.Cmd) ([]byte, error) {
return b.Bytes(), err
}
+
+// CheckLinear checks if the function produced by f scales linearly.
+//
+// f must accept a scale factor which causes the input to the function it
+// produces to scale by that factor.
+func CheckLinear(t *testing.T, f func(scale float64) func(*testing.B)) {
+ MustHaveExec(t)
+
+ if os.Getenv("GO_PERF_UNIT_TEST") == "" {
+ // Invoke the same test as a subprocess with the GO_PERF_UNIT_TEST environment variable set.
+ // We create a subprocess for two reasons:
+ //
+ // 1. There's no other way to set the benchmarking parameters of testing.Benchmark.
+ // 2. Since we're effectively running a performance test, running in a subprocess grants
+ // us a little bit more isolation than using the same process.
+ //
+ // As an alternative, we could fairly easily reimplement the timing code in testing.Benchmark,
+ // but a subprocess is just as easy to create.
+
+ selfCmd := CleanCmdEnv(exec.Command(os.Args[0], "-test.v", fmt.Sprintf("-test.run=^%s$", t.Name()), "-test.benchtime=1x"))
+ selfCmd.Env = append(selfCmd.Env, "GO_PERF_UNIT_TEST=1")
+ output, err := RunWithTimeout(t, selfCmd)
+ if err != nil {
+ t.Error(err)
+ t.Logf("--- subprocess output ---\n%s", string(output))
+ }
+ if bytes.Contains(output, []byte("insignificant result")) {
+ t.Skip("insignificant result")
+ }
+ return
+ }
+
+ // Pick a reasonable sample count.
+ const count = 10
+
+ // Collect samples for scale factor 1.
+ x1 := make([]testing.BenchmarkResult, 0, count)
+ for i := 0; i < count; i++ {
+ x1 = append(x1, testing.Benchmark(f(1.0)))
+ }
+
+ // Collect samples for scale factor 2.
+ x2 := make([]testing.BenchmarkResult, 0, count)
+ for i := 0; i < count; i++ {
+ x2 = append(x2, testing.Benchmark(f(2.0)))
+ }
+
+ // Run a t-test on the results.
+ r1 := testmath.BenchmarkResults(x1)
+ r2 := testmath.BenchmarkResults(x2)
+ result, err := testmath.TwoSampleWelchTTest(r1, r2, testmath.LocationDiffers)
+ if err != nil {
+ t.Fatalf("failed to run t-test: %v", err)
+ }
+ if result.P > 0.005 {
+ // Insignificant result.
+ t.Skip("insignificant result")
+ }
+
+ // Let ourselves be within 3x; 2x is too strict.
+ if m1, m2 := r1.Mean(), r2.Mean(); 3.0*m1 < m2 {
+ t.Fatalf("failure to scale linearly: µ_1=%s µ_2=%s p=%f", time.Duration(m1), time.Duration(m2), result.P)
+ }
+}