diff options
author | Vladimir Kuzmin <vkuzmin@uber.com> | 2018-02-01 21:33:56 -0800 |
---|---|---|
committer | Matthew Dempsky <mdempsky@google.com> | 2018-03-12 19:27:44 +0000 |
commit | 7395083136539331537d46875ab9d196797a2173 (patch) | |
tree | 1442dc0384c3d7916339e84d296d22f554985373 /src/runtime/map_test.go | |
parent | 85a8d25d535a9b70f6c908e44f8558c207366ff1 (diff) | |
download | go-7395083136539331537d46875ab9d196797a2173.tar.gz go-7395083136539331537d46875ab9d196797a2173.zip |
cmd/compile: avoid extra mapaccess in "m[k] op= r"
Currently, order desugars map assignment operations like
m[k] op= r
into
m[k] = m[k] op r
which in turn is transformed during walk into:
tmp := *mapaccess(m, k)
tmp = tmp op r
*mapassign(m, k) = tmp
However, this is suboptimal, as we could instead produce just:
*mapassign(m, k) op= r
One complication though is if "r == 0", then "m[k] /= r" and "m[k] %=
r" will panic, and they need to do so *before* calling mapassign,
otherwise we may insert a new zero-value element into the map.
It would be spec compliant to just emit the "r != 0" check before
calling mapassign (see #23735), but currently these checks aren't
generated until SSA construction. For now, it's simpler to continue
desugaring /= and %= into two map indexing operations.
Fixes #23661.
Change-Id: I46e3739d9adef10e92b46fdd78b88d5aabe68952
Reviewed-on: https://go-review.googlesource.com/91557
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/map_test.go')
-rw-r--r-- | src/runtime/map_test.go | 105 |
1 files changed, 92 insertions, 13 deletions
diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go index d1b268bda4..05fe986b33 100644 --- a/src/runtime/map_test.go +++ b/src/runtime/map_test.go @@ -52,14 +52,7 @@ func TestNegativeZero(t *testing.T) { } } -// nan is a good test because nan != nan, and nan has -// a randomized hash value. -func TestNan(t *testing.T) { - m := make(map[float64]int, 0) - nan := math.NaN() - m[nan] = 1 - m[nan] = 2 - m[nan] = 4 +func testMapNan(t *testing.T, m map[float64]int) { if len(m) != 3 { t.Error("length wrong") } @@ -78,6 +71,49 @@ func TestNan(t *testing.T) { } } +// nan is a good test because nan != nan, and nan has +// a randomized hash value. +func TestMapAssignmentNan(t *testing.T) { + m := make(map[float64]int, 0) + nan := math.NaN() + + // Test assignment. + m[nan] = 1 + m[nan] = 2 + m[nan] = 4 + testMapNan(t, m) +} + +// nan is a good test because nan != nan, and nan has +// a randomized hash value. +func TestMapOperatorAssignmentNan(t *testing.T) { + m := make(map[float64]int, 0) + nan := math.NaN() + + // Test assignment operations. + m[nan] += 1 + m[nan] += 2 + m[nan] += 4 + testMapNan(t, m) +} + +func TestMapOperatorAssignment(t *testing.T) { + m := make(map[int]int, 0) + + // "m[k] op= x" is rewritten into "m[k] = m[k] op x" + // differently when op is / or % than when it isn't. + // Simple test to make sure they all work as expected. + m[0] = 12345 + m[0] += 67890 + m[0] /= 123 + m[0] %= 456 + + const want = (12345 + 67890) / 123 % 456 + if got := m[0]; got != want { + t.Errorf("got %d, want %d", got, want) + } +} + // Maps aren't actually copied on assignment. func TestAlias(t *testing.T) { m := make(map[int]int, 0) @@ -92,18 +128,25 @@ func TestAlias(t *testing.T) { func TestGrowWithNaN(t *testing.T) { m := make(map[float64]int, 4) nan := math.NaN() + + // Use both assignment and assignment operations as they may + // behave differently. m[nan] = 1 m[nan] = 2 - m[nan] = 4 + m[nan] += 4 + cnt := 0 s := 0 growflag := true for k, v := range m { if growflag { // force a hashtable resize - for i := 0; i < 100; i++ { + for i := 0; i < 50; i++ { m[float64(i)] = i } + for i := 50; i < 100; i++ { + m[float64(i)] += i + } growflag = false } if k != k { @@ -128,8 +171,8 @@ func TestGrowWithNegativeZero(t *testing.T) { negzero := math.Copysign(0.0, -1.0) m := make(map[FloatInt]int, 4) m[FloatInt{0.0, 0}] = 1 - m[FloatInt{0.0, 1}] = 2 - m[FloatInt{0.0, 2}] = 4 + m[FloatInt{0.0, 1}] += 2 + m[FloatInt{0.0, 2}] += 4 m[FloatInt{0.0, 3}] = 8 growflag := true s := 0 @@ -211,9 +254,12 @@ func TestIterGrowAndDelete(t *testing.T) { // an iterator is still using them. func TestIterGrowWithGC(t *testing.T) { m := make(map[int]int, 4) - for i := 0; i < 16; i++ { + for i := 0; i < 8; i++ { m[i] = i } + for i := 8; i < 16; i++ { + m[i] += i + } growflag := true bitmask := 0 for k := range m { @@ -786,6 +832,13 @@ func benchmarkMapAssignInt32(b *testing.B, n int) { } } +func benchmarkMapOperatorAssignInt32(b *testing.B, n int) { + a := make(map[int32]int) + for i := 0; i < b.N; i++ { + a[int32(i&(n-1))] += i + } +} + func benchmarkMapDeleteInt32(b *testing.B, n int) { a := make(map[int32]int, n) b.ResetTimer() @@ -808,6 +861,13 @@ func benchmarkMapAssignInt64(b *testing.B, n int) { } } +func benchmarkMapOperatorAssignInt64(b *testing.B, n int) { + a := make(map[int64]int) + for i := 0; i < b.N; i++ { + a[int64(i&(n-1))] += i + } +} + func benchmarkMapDeleteInt64(b *testing.B, n int) { a := make(map[int64]int, n) b.ResetTimer() @@ -835,6 +895,19 @@ func benchmarkMapAssignStr(b *testing.B, n int) { } } +func benchmarkMapOperatorAssignStr(b *testing.B, n int) { + k := make([]string, n) + for i := 0; i < len(k); i++ { + k[i] = strconv.Itoa(i) + } + b.ResetTimer() + a := make(map[string]string) + for i := 0; i < b.N; i++ { + key := k[i&(n-1)] + a[key] += key + } +} + func benchmarkMapDeleteStr(b *testing.B, n int) { i2s := make([]string, n) for i := 0; i < n; i++ { @@ -870,6 +943,12 @@ func BenchmarkMapAssign(b *testing.B) { b.Run("Str", runWith(benchmarkMapAssignStr, 1<<8, 1<<16)) } +func BenchmarkMapOperatorAssign(b *testing.B) { + b.Run("Int32", runWith(benchmarkMapOperatorAssignInt32, 1<<8, 1<<16)) + b.Run("Int64", runWith(benchmarkMapOperatorAssignInt64, 1<<8, 1<<16)) + b.Run("Str", runWith(benchmarkMapOperatorAssignStr, 1<<8, 1<<16)) +} + func BenchmarkMapDelete(b *testing.B) { b.Run("Int32", runWith(benchmarkMapDeleteInt32, 100, 1000, 10000)) b.Run("Int64", runWith(benchmarkMapDeleteInt64, 100, 1000, 10000)) |