Alan Donovan | 161ba66 | 2014-08-06 17:02:55 -0400 | [diff] [blame] | 1 | // +build darwin linux |
| 2 | // run |
| 3 | |
| 4 | // Copyright 2013 The Go Authors. All rights reserved. |
| 5 | // Use of this source code is governed by a BSD-style |
| 6 | // license that can be found in the LICENSE file. |
| 7 | |
| 8 | // Test that maps don't go quadratic for NaNs and other values. |
| 9 | |
| 10 | package main |
| 11 | |
| 12 | import ( |
| 13 | "fmt" |
| 14 | "math" |
| 15 | "time" |
| 16 | ) |
| 17 | |
| 18 | // checkLinear asserts that the running time of f(n) is in O(n). |
| 19 | // tries is the initial number of iterations. |
| 20 | func checkLinear(typ string, tries int, f func(n int)) { |
| 21 | // Depending on the machine and OS, this test might be too fast |
| 22 | // to measure with accurate enough granularity. On failure, |
| 23 | // make it run longer, hoping that the timing granularity |
| 24 | // is eventually sufficient. |
| 25 | |
| 26 | timeF := func(n int) time.Duration { |
| 27 | t1 := time.Now() |
| 28 | f(n) |
| 29 | return time.Since(t1) |
| 30 | } |
| 31 | |
| 32 | t0 := time.Now() |
| 33 | |
| 34 | n := tries |
| 35 | fails := 0 |
| 36 | for { |
| 37 | t1 := timeF(n) |
| 38 | t2 := timeF(2 * n) |
| 39 | |
| 40 | // should be 2x (linear); allow up to 3x |
| 41 | if t2 < 3*t1 { |
| 42 | if false { |
| 43 | fmt.Println(typ, "\t", time.Since(t0)) |
| 44 | } |
| 45 | return |
| 46 | } |
Russ Cox | 0f698be | 2014-10-27 18:59:02 -0400 | [diff] [blame] | 47 | // If n ops run in under a second and the ratio |
| 48 | // doesn't work out, make n bigger, trying to reduce |
| 49 | // the effect that a constant amount of overhead has |
| 50 | // on the computed ratio. |
| 51 | if t1 < 1*time.Second { |
| 52 | n *= 2 |
| 53 | continue |
| 54 | } |
| 55 | // Once the test runs long enough for n ops, |
| 56 | // try to get the right ratio at least once. |
| 57 | // If five in a row all fail, give up. |
| 58 | if fails++; fails >= 5 { |
Alan Donovan | 161ba66 | 2014-08-06 17:02:55 -0400 | [diff] [blame] | 59 | panic(fmt.Sprintf("%s: too slow: %d inserts: %v; %d inserts: %v\n", |
| 60 | typ, n, t1, 2*n, t2)) |
| 61 | } |
Alan Donovan | 161ba66 | 2014-08-06 17:02:55 -0400 | [diff] [blame] | 62 | } |
| 63 | } |
| 64 | |
| 65 | type I interface { |
| 66 | f() |
| 67 | } |
| 68 | |
| 69 | type C int |
| 70 | |
| 71 | func (C) f() {} |
| 72 | |
| 73 | func main() { |
| 74 | // NaNs. ~31ms on a 1.6GHz Zeon. |
| 75 | checkLinear("NaN", 30000, func(n int) { |
| 76 | m := map[float64]int{} |
| 77 | nan := math.NaN() |
| 78 | for i := 0; i < n; i++ { |
| 79 | m[nan] = 1 |
| 80 | } |
| 81 | if len(m) != n { |
| 82 | panic("wrong size map after nan insertion") |
| 83 | } |
| 84 | }) |
| 85 | |
| 86 | // ~6ms on a 1.6GHz Zeon. |
| 87 | checkLinear("eface", 10000, func(n int) { |
| 88 | m := map[interface{}]int{} |
| 89 | for i := 0; i < n; i++ { |
| 90 | m[i] = 1 |
| 91 | } |
| 92 | }) |
| 93 | |
| 94 | // ~7ms on a 1.6GHz Zeon. |
| 95 | // Regression test for CL 119360043. |
| 96 | checkLinear("iface", 10000, func(n int) { |
| 97 | m := map[I]int{} |
| 98 | for i := 0; i < n; i++ { |
| 99 | m[C(i)] = 1 |
| 100 | } |
| 101 | }) |
| 102 | |
| 103 | // ~6ms on a 1.6GHz Zeon. |
| 104 | checkLinear("int", 10000, func(n int) { |
| 105 | m := map[int]int{} |
| 106 | for i := 0; i < n; i++ { |
| 107 | m[i] = 1 |
| 108 | } |
| 109 | }) |
| 110 | |
| 111 | // ~18ms on a 1.6GHz Zeon. |
| 112 | checkLinear("string", 10000, func(n int) { |
| 113 | m := map[string]int{} |
| 114 | for i := 0; i < n; i++ { |
| 115 | m[fmt.Sprint(i)] = 1 |
| 116 | } |
| 117 | }) |
| 118 | |
| 119 | // ~6ms on a 1.6GHz Zeon. |
| 120 | checkLinear("float32", 10000, func(n int) { |
| 121 | m := map[float32]int{} |
| 122 | for i := 0; i < n; i++ { |
| 123 | m[float32(i)] = 1 |
| 124 | } |
| 125 | }) |
| 126 | |
| 127 | // ~6ms on a 1.6GHz Zeon. |
| 128 | checkLinear("float64", 10000, func(n int) { |
| 129 | m := map[float64]int{} |
| 130 | for i := 0; i < n; i++ { |
| 131 | m[float64(i)] = 1 |
| 132 | } |
| 133 | }) |
| 134 | |
| 135 | // ~22ms on a 1.6GHz Zeon. |
| 136 | checkLinear("complex64", 10000, func(n int) { |
| 137 | m := map[complex64]int{} |
| 138 | for i := 0; i < n; i++ { |
| 139 | m[complex(float32(i), float32(i))] = 1 |
| 140 | } |
| 141 | }) |
| 142 | |
| 143 | // ~32ms on a 1.6GHz Zeon. |
| 144 | checkLinear("complex128", 10000, func(n int) { |
| 145 | m := map[complex128]int{} |
| 146 | for i := 0; i < n; i++ { |
| 147 | m[complex(float64(i), float64(i))] = 1 |
| 148 | } |
| 149 | }) |
Keith Randall | 689dc60 | 2014-09-10 22:54:07 -0700 | [diff] [blame] | 150 | |
| 151 | // ~70ms on a 1.6GHz Zeon. |
| 152 | // The iterate/delete idiom currently takes expected |
| 153 | // O(n lg n) time. Fortunately, the checkLinear test |
| 154 | // leaves enough wiggle room to include n lg n time |
| 155 | // (it actually tests for O(n^log_2(3)). |
Josh Bleecher Snyder | f197988 | 2014-09-15 10:56:37 -0700 | [diff] [blame] | 156 | // To prevent false positives, average away variation |
| 157 | // by doing multiple rounds within a single run. |
| 158 | checkLinear("iterdelete", 2500, func(n int) { |
| 159 | for round := 0; round < 4; round++ { |
| 160 | m := map[int]int{} |
| 161 | for i := 0; i < n; i++ { |
| 162 | m[i] = i |
| 163 | } |
| 164 | for i := 0; i < n; i++ { |
| 165 | for k := range m { |
| 166 | delete(m, k) |
| 167 | break |
| 168 | } |
Keith Randall | 689dc60 | 2014-09-10 22:54:07 -0700 | [diff] [blame] | 169 | } |
| 170 | } |
| 171 | }) |
Alan Donovan | 161ba66 | 2014-08-06 17:02:55 -0400 | [diff] [blame] | 172 | } |