]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: remove calling mapiterkey, mapiterelem
authorKyle Xiao <xiaost7@gmail.com>
Thu, 12 Sep 2024 14:52:56 +0000 (22:52 +0800)
committerGopher Robot <gobot@golang.org>
Wed, 18 Sep 2024 20:57:20 +0000 (20:57 +0000)
It makes use of the hiter structure which matches runtime.hiter's.

This change mainly improves the performance of Next method of MapIter.

goos: darwin
goarch: arm64
pkg: reflect
cpu: Apple M2
              │  ./old.txt  │              ./new.txt              │
              │   sec/op    │   sec/op     vs base                │
MapIterNext-8   61.95n ± 0%   54.95n ± 0%  -11.28% (p=0.000 n=10)

for the change of `test/escape_reflect.go`:
removing mapiterkey, mapiterelem would cause leaking MapIter content
when calling SetIterKey and SetIterValue,
and this may cause map bucket to be allocated on heap instead of stack.
Reproduce:
```
{
  m := map[int]int{1: 2} // escapes to heap after this change
  it := reflect.ValueOf(m).MapRange()
  it.Next()
  var k, v int
  reflect.ValueOf(&k).Elem().SetIterKey(it)
  reflect.ValueOf(&v).Elem().SetIterValue(it)
  println(k, v)
}
```
This CL would not introduce abi.NoEscape to fix this. It may need futher
optimization and tests on hiter field usage and its escape analysis.

Fixes #69416

Change-Id: Ibaa33bcf86228070b4a505b9512680791aa59f04
Reviewed-on: https://go-review.googlesource.com/c/go/+/612616
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/reflect/map_noswiss.go
src/reflect/map_swiss.go
src/reflect/value.go
src/runtime/map_noswiss.go
test/escape_reflect.go

index 5af50ac77918cc826e9e7bcc5b2d3a567bd6b59c..81d7b6222aeca2262f6aaae8cdc02f0af0c11d7e 100644 (file)
@@ -223,7 +223,7 @@ func (v Value) MapKeys() []Value {
        a := make([]Value, mlen)
        var i int
        for i = 0; i < len(a); i++ {
-               key := mapiterkey(&it)
+               key := it.key
                if key == nil {
                        // Someone deleted an entry from the map since we
                        // called maplen above. It's a data race, but nothing
@@ -274,7 +274,7 @@ func (iter *MapIter) Key() Value {
        if !iter.hiter.initialized() {
                panic("MapIter.Key called before Next")
        }
-       iterkey := mapiterkey(&iter.hiter)
+       iterkey := iter.hiter.key
        if iterkey == nil {
                panic("MapIter.Key called on exhausted iterator")
        }
@@ -292,7 +292,7 @@ func (v Value) SetIterKey(iter *MapIter) {
        if !iter.hiter.initialized() {
                panic("reflect: Value.SetIterKey called before Next")
        }
-       iterkey := mapiterkey(&iter.hiter)
+       iterkey := iter.hiter.key
        if iterkey == nil {
                panic("reflect: Value.SetIterKey called on exhausted iterator")
        }
@@ -317,7 +317,7 @@ func (iter *MapIter) Value() Value {
        if !iter.hiter.initialized() {
                panic("MapIter.Value called before Next")
        }
-       iterelem := mapiterelem(&iter.hiter)
+       iterelem := iter.hiter.elem
        if iterelem == nil {
                panic("MapIter.Value called on exhausted iterator")
        }
@@ -335,7 +335,7 @@ func (v Value) SetIterValue(iter *MapIter) {
        if !iter.hiter.initialized() {
                panic("reflect: Value.SetIterValue called before Next")
        }
-       iterelem := mapiterelem(&iter.hiter)
+       iterelem := iter.hiter.elem
        if iterelem == nil {
                panic("reflect: Value.SetIterValue called on exhausted iterator")
        }
@@ -365,12 +365,12 @@ func (iter *MapIter) Next() bool {
        if !iter.hiter.initialized() {
                mapiterinit(iter.m.typ(), iter.m.pointer(), &iter.hiter)
        } else {
-               if mapiterkey(&iter.hiter) == nil {
+               if iter.hiter.key == nil {
                        panic("MapIter.Next called on exhausted iterator")
                }
                mapiternext(&iter.hiter)
        }
-       return mapiterkey(&iter.hiter) != nil
+       return iter.hiter.key != nil
 }
 
 // Reset modifies iter to iterate over v.
index 4607b6f0f3cc51251e077e41695f40d58cbd6c0e..8978b377c767265979e1020c028d41c700433d12 100644 (file)
@@ -224,7 +224,7 @@ func (v Value) MapKeys() []Value {
        a := make([]Value, mlen)
        var i int
        for i = 0; i < len(a); i++ {
-               key := mapiterkey(&it)
+               key := it.key
                if key == nil {
                        // Someone deleted an entry from the map since we
                        // called maplen above. It's a data race, but nothing
@@ -275,7 +275,7 @@ func (iter *MapIter) Key() Value {
        if !iter.hiter.initialized() {
                panic("MapIter.Key called before Next")
        }
-       iterkey := mapiterkey(&iter.hiter)
+       iterkey := iter.hiter.key
        if iterkey == nil {
                panic("MapIter.Key called on exhausted iterator")
        }
@@ -293,7 +293,7 @@ func (v Value) SetIterKey(iter *MapIter) {
        if !iter.hiter.initialized() {
                panic("reflect: Value.SetIterKey called before Next")
        }
-       iterkey := mapiterkey(&iter.hiter)
+       iterkey := iter.hiter.key
        if iterkey == nil {
                panic("reflect: Value.SetIterKey called on exhausted iterator")
        }
@@ -318,7 +318,7 @@ func (iter *MapIter) Value() Value {
        if !iter.hiter.initialized() {
                panic("MapIter.Value called before Next")
        }
-       iterelem := mapiterelem(&iter.hiter)
+       iterelem := iter.hiter.elem
        if iterelem == nil {
                panic("MapIter.Value called on exhausted iterator")
        }
@@ -336,7 +336,7 @@ func (v Value) SetIterValue(iter *MapIter) {
        if !iter.hiter.initialized() {
                panic("reflect: Value.SetIterValue called before Next")
        }
-       iterelem := mapiterelem(&iter.hiter)
+       iterelem := iter.hiter.elem
        if iterelem == nil {
                panic("reflect: Value.SetIterValue called on exhausted iterator")
        }
@@ -366,12 +366,12 @@ func (iter *MapIter) Next() bool {
        if !iter.hiter.initialized() {
                mapiterinit(iter.m.typ(), iter.m.pointer(), &iter.hiter)
        } else {
-               if mapiterkey(&iter.hiter) == nil {
+               if iter.hiter.key == nil {
                        panic("MapIter.Next called on exhausted iterator")
                }
                mapiternext(&iter.hiter)
        }
-       return mapiterkey(&iter.hiter) != nil
+       return iter.hiter.key != nil
 }
 
 // Reset modifies iter to iterate over v.
index 7bac530b5bc9324354b83242703726cf70f93b92..e02002ff331b6cf8e80f40bc360158abecadebd1 100644 (file)
@@ -3596,12 +3596,6 @@ func mapdelete_faststr(t *abi.Type, m unsafe.Pointer, key string)
 //go:noescape
 func mapiterinit(t *abi.Type, m unsafe.Pointer, it *hiter)
 
-//go:noescape
-func mapiterkey(it *hiter) (key unsafe.Pointer)
-
-//go:noescape
-func mapiterelem(it *hiter) (elem unsafe.Pointer)
-
 //go:noescape
 func mapiternext(it *hiter)
 
index 44a93089ef4fa903c909a935f09beb543eb1a7c4..d7b8a5fe11cea31b9548f844b19623917a6b1660 100644 (file)
@@ -1528,7 +1528,7 @@ func reflect_mapiternext(it *hiter) {
        mapiternext(it)
 }
 
-// reflect_mapiterkey is for package reflect,
+// reflect_mapiterkey was for package reflect,
 // but widely used packages access it using linkname.
 // Notable members of the hall of shame include:
 //   - github.com/goccy/go-json
@@ -1542,7 +1542,7 @@ func reflect_mapiterkey(it *hiter) unsafe.Pointer {
        return it.key
 }
 
-// reflect_mapiterelem is for package reflect,
+// reflect_mapiterelem was for package reflect,
 // but widely used packages access it using linkname.
 // Notable members of the hall of shame include:
 //   - github.com/goccy/go-json
index 99fbada9a98eb4641d151dc4059174f10173224b..e50323702e916238c9c44e911195188fadb80ab8 100644 (file)
@@ -423,7 +423,7 @@ func mapdelete(m map[string]string, k string) { // ERROR "m does not escape" "le
 }
 
 // Unfortunate: v doesn't need to leak.
-func setiterkey1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "it does not escape"
+func setiterkey1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "leaking param content: it$"
        v.SetIterKey(it)
 }
 
@@ -434,7 +434,7 @@ func setiterkey2(v reflect.Value, m map[string]string) { // ERROR "leaking param
 }
 
 // Unfortunate: v doesn't need to leak.
-func setitervalue1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "it does not escape"
+func setitervalue1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "leaking param content: it$"
        v.SetIterValue(it)
 }