diff options
| author | Michael Anthony Knyszek <mknyszek@google.com> | 2022-06-10 16:21:46 +0000 |
|---|---|---|
| committer | Michael Knyszek <mknyszek@google.com> | 2022-06-13 20:15:55 +0000 |
| commit | 1fe2810f9ca0dcd34e473f852102e2a49d45d7d8 (patch) | |
| tree | 429e31e7529c6066e3529e96078107f85afce083 /src/internal/testenv | |
| parent | 6130461149020d2b4b91fb183afa388a211cadc5 (diff) | |
| download | go-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.go | 65 |
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) + } +} |
