mirror of
https://github.com/golang/go
synced 2024-09-05 16:04:50 +00:00
![Russ Cox](/assets/img/avatar_default.png)
The test just doubled a certain number of times and then gave up. On a mostly fast but occasionally slow machine this may never make the test run long enough to see the linear growth. Change test to keep doubling until the first round takes at least a full second, to reduce the effect of occasional scheduling or other jitter. The failure we saw had a time for the first round of around 100ms. Note that this test still passes once it sees a linear effect, even with a very small total time. The timeout here only applies to how long the execution must be to support a reported failure. LGTM=khr R=khr CC=golang-codereviews, rlh https://golang.org/cl/164070043
173 lines
3.6 KiB
Go
173 lines
3.6 KiB
Go
// +build darwin linux
|
|
// run
|
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Test that maps don't go quadratic for NaNs and other values.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"time"
|
|
)
|
|
|
|
// checkLinear asserts that the running time of f(n) is in O(n).
|
|
// tries is the initial number of iterations.
|
|
func checkLinear(typ string, tries int, f func(n int)) {
|
|
// Depending on the machine and OS, this test might be too fast
|
|
// to measure with accurate enough granularity. On failure,
|
|
// make it run longer, hoping that the timing granularity
|
|
// is eventually sufficient.
|
|
|
|
timeF := func(n int) time.Duration {
|
|
t1 := time.Now()
|
|
f(n)
|
|
return time.Since(t1)
|
|
}
|
|
|
|
t0 := time.Now()
|
|
|
|
n := tries
|
|
fails := 0
|
|
for {
|
|
t1 := timeF(n)
|
|
t2 := timeF(2 * n)
|
|
|
|
// should be 2x (linear); allow up to 3x
|
|
if t2 < 3*t1 {
|
|
if false {
|
|
fmt.Println(typ, "\t", time.Since(t0))
|
|
}
|
|
return
|
|
}
|
|
// If n ops run in under a second and the ratio
|
|
// doesn't work out, make n bigger, trying to reduce
|
|
// the effect that a constant amount of overhead has
|
|
// on the computed ratio.
|
|
if t1 < 1*time.Second {
|
|
n *= 2
|
|
continue
|
|
}
|
|
// Once the test runs long enough for n ops,
|
|
// try to get the right ratio at least once.
|
|
// If five in a row all fail, give up.
|
|
if fails++; fails >= 5 {
|
|
panic(fmt.Sprintf("%s: too slow: %d inserts: %v; %d inserts: %v\n",
|
|
typ, n, t1, 2*n, t2))
|
|
}
|
|
}
|
|
}
|
|
|
|
type I interface {
|
|
f()
|
|
}
|
|
|
|
type C int
|
|
|
|
func (C) f() {}
|
|
|
|
func main() {
|
|
// NaNs. ~31ms on a 1.6GHz Zeon.
|
|
checkLinear("NaN", 30000, func(n int) {
|
|
m := map[float64]int{}
|
|
nan := math.NaN()
|
|
for i := 0; i < n; i++ {
|
|
m[nan] = 1
|
|
}
|
|
if len(m) != n {
|
|
panic("wrong size map after nan insertion")
|
|
}
|
|
})
|
|
|
|
// ~6ms on a 1.6GHz Zeon.
|
|
checkLinear("eface", 10000, func(n int) {
|
|
m := map[interface{}]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[i] = 1
|
|
}
|
|
})
|
|
|
|
// ~7ms on a 1.6GHz Zeon.
|
|
// Regression test for CL 119360043.
|
|
checkLinear("iface", 10000, func(n int) {
|
|
m := map[I]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[C(i)] = 1
|
|
}
|
|
})
|
|
|
|
// ~6ms on a 1.6GHz Zeon.
|
|
checkLinear("int", 10000, func(n int) {
|
|
m := map[int]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[i] = 1
|
|
}
|
|
})
|
|
|
|
// ~18ms on a 1.6GHz Zeon.
|
|
checkLinear("string", 10000, func(n int) {
|
|
m := map[string]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[fmt.Sprint(i)] = 1
|
|
}
|
|
})
|
|
|
|
// ~6ms on a 1.6GHz Zeon.
|
|
checkLinear("float32", 10000, func(n int) {
|
|
m := map[float32]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[float32(i)] = 1
|
|
}
|
|
})
|
|
|
|
// ~6ms on a 1.6GHz Zeon.
|
|
checkLinear("float64", 10000, func(n int) {
|
|
m := map[float64]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[float64(i)] = 1
|
|
}
|
|
})
|
|
|
|
// ~22ms on a 1.6GHz Zeon.
|
|
checkLinear("complex64", 10000, func(n int) {
|
|
m := map[complex64]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[complex(float32(i), float32(i))] = 1
|
|
}
|
|
})
|
|
|
|
// ~32ms on a 1.6GHz Zeon.
|
|
checkLinear("complex128", 10000, func(n int) {
|
|
m := map[complex128]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[complex(float64(i), float64(i))] = 1
|
|
}
|
|
})
|
|
|
|
// ~70ms on a 1.6GHz Zeon.
|
|
// The iterate/delete idiom currently takes expected
|
|
// O(n lg n) time. Fortunately, the checkLinear test
|
|
// leaves enough wiggle room to include n lg n time
|
|
// (it actually tests for O(n^log_2(3)).
|
|
// To prevent false positives, average away variation
|
|
// by doing multiple rounds within a single run.
|
|
checkLinear("iterdelete", 2500, func(n int) {
|
|
for round := 0; round < 4; round++ {
|
|
m := map[int]int{}
|
|
for i := 0; i < n; i++ {
|
|
m[i] = i
|
|
}
|
|
for i := 0; i < n; i++ {
|
|
for k := range m {
|
|
delete(m, k)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|