github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/third_party/gofrontend/libgo/go/runtime/map_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "fmt" 9 "math" 10 "reflect" 11 "runtime" 12 "sort" 13 "strings" 14 "sync" 15 "testing" 16 ) 17 18 // negative zero is a good test because: 19 // 1) 0 and -0 are equal, yet have distinct representations. 20 // 2) 0 is represented as all zeros, -0 isn't. 21 // I'm not sure the language spec actually requires this behavior, 22 // but it's what the current map implementation does. 23 func TestNegativeZero(t *testing.T) { 24 m := make(map[float64]bool, 0) 25 26 m[+0.0] = true 27 m[math.Copysign(0.0, -1.0)] = true // should overwrite +0 entry 28 29 if len(m) != 1 { 30 t.Error("length wrong") 31 } 32 33 /* gccgo fails this test; this is not required by the spec. 34 for k := range m { 35 if math.Copysign(1.0, k) > 0 { 36 t.Error("wrong sign") 37 } 38 } 39 */ 40 41 m = make(map[float64]bool, 0) 42 m[math.Copysign(0.0, -1.0)] = true 43 m[+0.0] = true // should overwrite -0.0 entry 44 45 if len(m) != 1 { 46 t.Error("length wrong") 47 } 48 49 /* gccgo fails this test; this is not required by the spec. 50 for k := range m { 51 if math.Copysign(1.0, k) < 0 { 52 t.Error("wrong sign") 53 } 54 } 55 */ 56 } 57 58 // nan is a good test because nan != nan, and nan has 59 // a randomized hash value. 60 func TestNan(t *testing.T) { 61 m := make(map[float64]int, 0) 62 nan := math.NaN() 63 m[nan] = 1 64 m[nan] = 2 65 m[nan] = 4 66 if len(m) != 3 { 67 t.Error("length wrong") 68 } 69 s := 0 70 for k, v := range m { 71 if k == k { 72 t.Error("nan disappeared") 73 } 74 if (v & (v - 1)) != 0 { 75 t.Error("value wrong") 76 } 77 s |= v 78 } 79 if s != 7 { 80 t.Error("values wrong") 81 } 82 } 83 84 // Maps aren't actually copied on assignment. 85 func TestAlias(t *testing.T) { 86 m := make(map[int]int, 0) 87 m[0] = 5 88 n := m 89 n[0] = 6 90 if m[0] != 6 { 91 t.Error("alias didn't work") 92 } 93 } 94 95 func TestGrowWithNaN(t *testing.T) { 96 t.Skip("fails with gccgo") 97 m := make(map[float64]int, 4) 98 nan := math.NaN() 99 m[nan] = 1 100 m[nan] = 2 101 m[nan] = 4 102 cnt := 0 103 s := 0 104 growflag := true 105 for k, v := range m { 106 if growflag { 107 // force a hashtable resize 108 for i := 0; i < 100; i++ { 109 m[float64(i)] = i 110 } 111 growflag = false 112 } 113 if k != k { 114 cnt++ 115 s |= v 116 } 117 } 118 t.Log("cnt:", cnt, "s:", s) 119 if cnt != 3 { 120 t.Error("NaN keys lost during grow") 121 } 122 if s != 7 { 123 t.Error("NaN values lost during grow") 124 } 125 } 126 127 type FloatInt struct { 128 x float64 129 y int 130 } 131 132 func TestGrowWithNegativeZero(t *testing.T) { 133 t.Skip("fails with gccgo") 134 negzero := math.Copysign(0.0, -1.0) 135 m := make(map[FloatInt]int, 4) 136 m[FloatInt{0.0, 0}] = 1 137 m[FloatInt{0.0, 1}] = 2 138 m[FloatInt{0.0, 2}] = 4 139 m[FloatInt{0.0, 3}] = 8 140 growflag := true 141 s := 0 142 cnt := 0 143 negcnt := 0 144 // The first iteration should return the +0 key. 145 // The subsequent iterations should return the -0 key. 146 // I'm not really sure this is required by the spec, 147 // but it makes sense. 148 // TODO: are we allowed to get the first entry returned again??? 149 for k, v := range m { 150 if v == 0 { 151 continue 152 } // ignore entries added to grow table 153 cnt++ 154 if math.Copysign(1.0, k.x) < 0 { 155 if v&16 == 0 { 156 t.Error("key/value not updated together 1") 157 } 158 negcnt++ 159 s |= v & 15 160 } else { 161 if v&16 == 16 { 162 t.Error("key/value not updated together 2", k, v) 163 } 164 s |= v 165 } 166 if growflag { 167 // force a hashtable resize 168 for i := 0; i < 100; i++ { 169 m[FloatInt{3.0, i}] = 0 170 } 171 // then change all the entries 172 // to negative zero 173 m[FloatInt{negzero, 0}] = 1 | 16 174 m[FloatInt{negzero, 1}] = 2 | 16 175 m[FloatInt{negzero, 2}] = 4 | 16 176 m[FloatInt{negzero, 3}] = 8 | 16 177 growflag = false 178 } 179 } 180 if s != 15 { 181 t.Error("entry missing", s) 182 } 183 if cnt != 4 { 184 t.Error("wrong number of entries returned by iterator", cnt) 185 } 186 if negcnt != 3 { 187 t.Error("update to negzero missed by iteration", negcnt) 188 } 189 } 190 191 func TestIterGrowAndDelete(t *testing.T) { 192 m := make(map[int]int, 4) 193 for i := 0; i < 100; i++ { 194 m[i] = i 195 } 196 growflag := true 197 for k := range m { 198 if growflag { 199 // grow the table 200 for i := 100; i < 1000; i++ { 201 m[i] = i 202 } 203 // delete all odd keys 204 for i := 1; i < 1000; i += 2 { 205 delete(m, i) 206 } 207 growflag = false 208 } else { 209 if k&1 == 1 { 210 t.Error("odd value returned") 211 } 212 } 213 } 214 } 215 216 // make sure old bucket arrays don't get GCd while 217 // an iterator is still using them. 218 func TestIterGrowWithGC(t *testing.T) { 219 m := make(map[int]int, 4) 220 for i := 0; i < 16; i++ { 221 m[i] = i 222 } 223 growflag := true 224 bitmask := 0 225 for k := range m { 226 if k < 16 { 227 bitmask |= 1 << uint(k) 228 } 229 if growflag { 230 // grow the table 231 for i := 100; i < 1000; i++ { 232 m[i] = i 233 } 234 // trigger a gc 235 runtime.GC() 236 growflag = false 237 } 238 } 239 if bitmask != 1<<16-1 { 240 t.Error("missing key", bitmask) 241 } 242 } 243 244 func testConcurrentReadsAfterGrowth(t *testing.T, useReflect bool) { 245 if runtime.GOMAXPROCS(-1) == 1 { 246 if runtime.GOARCH == "s390" { 247 // Test uses too much address space on 31-bit S390. 248 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 249 } else { 250 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16)) 251 } 252 } 253 numLoop := 10 254 numGrowStep := 250 255 numReader := 16 256 if testing.Short() { 257 numLoop, numGrowStep = 2, 500 258 } 259 for i := 0; i < numLoop; i++ { 260 m := make(map[int]int, 0) 261 for gs := 0; gs < numGrowStep; gs++ { 262 m[gs] = gs 263 var wg sync.WaitGroup 264 wg.Add(numReader * 2) 265 for nr := 0; nr < numReader; nr++ { 266 go func() { 267 defer wg.Done() 268 for range m { 269 } 270 }() 271 go func() { 272 defer wg.Done() 273 for key := 0; key < gs; key++ { 274 _ = m[key] 275 } 276 }() 277 if useReflect { 278 wg.Add(1) 279 go func() { 280 defer wg.Done() 281 mv := reflect.ValueOf(m) 282 keys := mv.MapKeys() 283 for _, k := range keys { 284 mv.MapIndex(k) 285 } 286 }() 287 } 288 } 289 wg.Wait() 290 } 291 } 292 } 293 294 func TestConcurrentReadsAfterGrowth(t *testing.T) { 295 testConcurrentReadsAfterGrowth(t, false) 296 } 297 298 func TestConcurrentReadsAfterGrowthReflect(t *testing.T) { 299 testConcurrentReadsAfterGrowth(t, true) 300 } 301 302 func TestBigItems(t *testing.T) { 303 var key [256]string 304 for i := 0; i < 256; i++ { 305 key[i] = "foo" 306 } 307 m := make(map[[256]string][256]string, 4) 308 for i := 0; i < 100; i++ { 309 key[37] = fmt.Sprintf("string%02d", i) 310 m[key] = key 311 } 312 var keys [100]string 313 var values [100]string 314 i := 0 315 for k, v := range m { 316 keys[i] = k[37] 317 values[i] = v[37] 318 i++ 319 } 320 sort.Strings(keys[:]) 321 sort.Strings(values[:]) 322 for i := 0; i < 100; i++ { 323 if keys[i] != fmt.Sprintf("string%02d", i) { 324 t.Errorf("#%d: missing key: %v", i, keys[i]) 325 } 326 if values[i] != fmt.Sprintf("string%02d", i) { 327 t.Errorf("#%d: missing value: %v", i, values[i]) 328 } 329 } 330 } 331 332 type empty struct { 333 } 334 335 func TestEmptyKeyAndValue(t *testing.T) { 336 a := make(map[int]empty, 4) 337 b := make(map[empty]int, 4) 338 c := make(map[empty]empty, 4) 339 a[0] = empty{} 340 b[empty{}] = 0 341 b[empty{}] = 1 342 c[empty{}] = empty{} 343 344 if len(a) != 1 { 345 t.Errorf("empty value insert problem") 346 } 347 if b[empty{}] != 1 { 348 t.Errorf("empty key returned wrong value") 349 } 350 } 351 352 // Tests a map with a single bucket, with same-lengthed short keys 353 // ("quick keys") as well as long keys. 354 func TestSingleBucketMapStringKeys_DupLen(t *testing.T) { 355 testMapLookups(t, map[string]string{ 356 "x": "x1val", 357 "xx": "x2val", 358 "foo": "fooval", 359 "bar": "barval", // same key length as "foo" 360 "xxxx": "x4val", 361 strings.Repeat("x", 128): "longval1", 362 strings.Repeat("y", 128): "longval2", 363 }) 364 } 365 366 // Tests a map with a single bucket, with all keys having different lengths. 367 func TestSingleBucketMapStringKeys_NoDupLen(t *testing.T) { 368 testMapLookups(t, map[string]string{ 369 "x": "x1val", 370 "xx": "x2val", 371 "foo": "fooval", 372 "xxxx": "x4val", 373 "xxxxx": "x5val", 374 "xxxxxx": "x6val", 375 strings.Repeat("x", 128): "longval", 376 }) 377 } 378 379 func testMapLookups(t *testing.T, m map[string]string) { 380 for k, v := range m { 381 if m[k] != v { 382 t.Fatalf("m[%q] = %q; want %q", k, m[k], v) 383 } 384 } 385 } 386 387 // Tests whether the iterator returns the right elements when 388 // started in the middle of a grow, when the keys are NaNs. 389 func TestMapNanGrowIterator(t *testing.T) { 390 m := make(map[float64]int) 391 nan := math.NaN() 392 const nBuckets = 16 393 // To fill nBuckets buckets takes LOAD * nBuckets keys. 394 nKeys := int(nBuckets * /* *runtime.HashLoad */ 6.5) 395 396 // Get map to full point with nan keys. 397 for i := 0; i < nKeys; i++ { 398 m[nan] = i 399 } 400 // Trigger grow 401 m[1.0] = 1 402 delete(m, 1.0) 403 404 // Run iterator 405 found := make(map[int]struct{}) 406 for _, v := range m { 407 if v != -1 { 408 if _, repeat := found[v]; repeat { 409 t.Fatalf("repeat of value %d", v) 410 } 411 found[v] = struct{}{} 412 } 413 if len(found) == nKeys/2 { 414 // Halfway through iteration, finish grow. 415 for i := 0; i < nBuckets; i++ { 416 delete(m, 1.0) 417 } 418 } 419 } 420 if len(found) != nKeys { 421 t.Fatalf("missing value") 422 } 423 } 424 425 func TestMapIterOrder(t *testing.T) { 426 if runtime.Compiler == "gccgo" { 427 t.Skip("skipping for gccgo") 428 } 429 430 for _, n := range [...]int{3, 7, 9, 15} { 431 for i := 0; i < 1000; i++ { 432 // Make m be {0: true, 1: true, ..., n-1: true}. 433 m := make(map[int]bool) 434 for i := 0; i < n; i++ { 435 m[i] = true 436 } 437 // Check that iterating over the map produces at least two different orderings. 438 ord := func() []int { 439 var s []int 440 for key := range m { 441 s = append(s, key) 442 } 443 return s 444 } 445 first := ord() 446 ok := false 447 for try := 0; try < 100; try++ { 448 if !reflect.DeepEqual(first, ord()) { 449 ok = true 450 break 451 } 452 } 453 if !ok { 454 t.Errorf("Map with n=%d elements had consistent iteration order: %v", n, first) 455 break 456 } 457 } 458 } 459 } 460 461 // Issue 8410 462 func TestMapSparseIterOrder(t *testing.T) { 463 // Run several rounds to increase the probability 464 // of failure. One is not enough. 465 if runtime.Compiler == "gccgo" { 466 t.Skip("skipping for gccgo") 467 } 468 NextRound: 469 for round := 0; round < 10; round++ { 470 m := make(map[int]bool) 471 // Add 1000 items, remove 980. 472 for i := 0; i < 1000; i++ { 473 m[i] = true 474 } 475 for i := 20; i < 1000; i++ { 476 delete(m, i) 477 } 478 479 var first []int 480 for i := range m { 481 first = append(first, i) 482 } 483 484 // 800 chances to get a different iteration order. 485 // See bug 8736 for why we need so many tries. 486 for n := 0; n < 800; n++ { 487 idx := 0 488 for i := range m { 489 if i != first[idx] { 490 // iteration order changed. 491 continue NextRound 492 } 493 idx++ 494 } 495 } 496 t.Fatalf("constant iteration order on round %d: %v", round, first) 497 } 498 } 499 500 func TestMapStringBytesLookup(t *testing.T) { 501 if runtime.Compiler == "gccgo" { 502 t.Skip("skipping for gccgo") 503 } 504 // Use large string keys to avoid small-allocation coalescing, 505 // which can cause AllocsPerRun to report lower counts than it should. 506 m := map[string]int{ 507 "1000000000000000000000000000000000000000000000000": 1, 508 "2000000000000000000000000000000000000000000000000": 2, 509 } 510 buf := []byte("1000000000000000000000000000000000000000000000000") 511 if x := m[string(buf)]; x != 1 { 512 t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x) 513 } 514 buf[0] = '2' 515 if x := m[string(buf)]; x != 2 { 516 t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x) 517 } 518 519 var x int 520 n := testing.AllocsPerRun(100, func() { 521 x += m[string(buf)] 522 }) 523 if n != 0 { 524 t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n) 525 } 526 527 x = 0 528 n = testing.AllocsPerRun(100, func() { 529 y, ok := m[string(buf)] 530 if !ok { 531 panic("!ok") 532 } 533 x += y 534 }) 535 if n != 0 { 536 t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n) 537 } 538 } 539 540 func TestMapLargeKeyNoPointer(t *testing.T) { 541 const ( 542 I = 1000 543 N = 64 544 ) 545 type T [N]int 546 m := make(map[T]int) 547 for i := 0; i < I; i++ { 548 var v T 549 for j := 0; j < N; j++ { 550 v[j] = i + j 551 } 552 m[v] = i 553 } 554 runtime.GC() 555 for i := 0; i < I; i++ { 556 var v T 557 for j := 0; j < N; j++ { 558 v[j] = i + j 559 } 560 if m[v] != i { 561 t.Fatalf("corrupted map: want %+v, got %+v", i, m[v]) 562 } 563 } 564 } 565 566 func TestMapLargeValNoPointer(t *testing.T) { 567 const ( 568 I = 1000 569 N = 64 570 ) 571 type T [N]int 572 m := make(map[int]T) 573 for i := 0; i < I; i++ { 574 var v T 575 for j := 0; j < N; j++ { 576 v[j] = i + j 577 } 578 m[i] = v 579 } 580 runtime.GC() 581 for i := 0; i < I; i++ { 582 var v T 583 for j := 0; j < N; j++ { 584 v[j] = i + j 585 } 586 v1 := m[i] 587 for j := 0; j < N; j++ { 588 if v1[j] != v[j] { 589 t.Fatalf("corrupted map: want %+v, got %+v", v, v1) 590 } 591 } 592 } 593 } 594 595 func benchmarkMapPop(b *testing.B, n int) { 596 m := map[int]int{} 597 for i := 0; i < b.N; i++ { 598 for j := 0; j < n; j++ { 599 m[j] = j 600 } 601 for j := 0; j < n; j++ { 602 // Use iterator to pop an element. 603 // We want this to be fast, see issue 8412. 604 for k := range m { 605 delete(m, k) 606 break 607 } 608 } 609 } 610 } 611 612 func BenchmarkMapPop100(b *testing.B) { benchmarkMapPop(b, 100) } 613 func BenchmarkMapPop1000(b *testing.B) { benchmarkMapPop(b, 1000) } 614 func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) } 615 616 func TestNonEscapingMap(t *testing.T) { 617 t.Skip("does not work on gccgo without better escape analysis") 618 n := testing.AllocsPerRun(1000, func() { 619 m := make(map[int]int) 620 m[0] = 0 621 }) 622 if n != 0 { 623 t.Fatalf("want 0 allocs, got %v", n) 624 } 625 }