aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/map_test.go
diff options
context:
space:
mode:
authorVladimir Kuzmin <vkuzmin@uber.com>2018-02-01 21:33:56 -0800
committerMatthew Dempsky <mdempsky@google.com>2018-03-12 19:27:44 +0000
commit7395083136539331537d46875ab9d196797a2173 (patch)
tree1442dc0384c3d7916339e84d296d22f554985373 /src/runtime/map_test.go
parent85a8d25d535a9b70f6c908e44f8558c207366ff1 (diff)
downloadgo-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.go105
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))