mirror of
https://github.com/golang/go
synced 2024-11-22 20:50:05 -07:00
reflect: allocate hiter as part of MapIter
This reduces the number of allocations per reflect map iteration from two to one. For #46293 Change-Id: Ibcff5f42fc512e637b6e460bad4518e7ac83d4c3 Reviewed-on: https://go-review.googlesource.com/c/go/+/321889 Trust: Josh Bleecher Snyder <josharian@gmail.com> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
9133245be7
commit
1b2d794ca3
@ -370,10 +370,9 @@ func TestMapIterSet(t *testing.T) {
|
|||||||
iter.SetValue(e)
|
iter.SetValue(e)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
// Making a *MapIter and making an hiter both allocate.
|
// Making a *MapIter allocates. This should be the only allocation.
|
||||||
// Those should be the only two allocations.
|
if got != 1 {
|
||||||
if got != 2 {
|
t.Errorf("wanted 1 alloc, got %d", got)
|
||||||
t.Errorf("wanted 2 allocs, got %d", got)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1549,11 +1549,12 @@ func (v Value) MapKeys() []Value {
|
|||||||
if m != nil {
|
if m != nil {
|
||||||
mlen = maplen(m)
|
mlen = maplen(m)
|
||||||
}
|
}
|
||||||
it := mapiterinit(v.typ, m)
|
var it hiter
|
||||||
|
mapiterinit(v.typ, m, &it)
|
||||||
a := make([]Value, mlen)
|
a := make([]Value, mlen)
|
||||||
var i int
|
var i int
|
||||||
for i = 0; i < len(a); i++ {
|
for i = 0; i < len(a); i++ {
|
||||||
key := mapiterkey(it)
|
key := mapiterkey(&it)
|
||||||
if key == nil {
|
if key == nil {
|
||||||
// Someone deleted an entry from the map since we
|
// Someone deleted an entry from the map since we
|
||||||
// called maplen above. It's a data race, but nothing
|
// called maplen above. It's a data race, but nothing
|
||||||
@ -1561,24 +1562,50 @@ func (v Value) MapKeys() []Value {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
a[i] = copyVal(keyType, fl, key)
|
a[i] = copyVal(keyType, fl, key)
|
||||||
mapiternext(it)
|
mapiternext(&it)
|
||||||
}
|
}
|
||||||
return a[:i]
|
return a[:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hiter's structure matches runtime.hiter's structure.
|
||||||
|
// Having a clone here allows us to embed a map iterator
|
||||||
|
// inside type MapIter so that MapIters can be re-used
|
||||||
|
// without doing any allocations.
|
||||||
|
type hiter struct {
|
||||||
|
key unsafe.Pointer
|
||||||
|
elem unsafe.Pointer
|
||||||
|
t unsafe.Pointer
|
||||||
|
h unsafe.Pointer
|
||||||
|
buckets unsafe.Pointer
|
||||||
|
bptr unsafe.Pointer
|
||||||
|
overflow *[]unsafe.Pointer
|
||||||
|
oldoverflow *[]unsafe.Pointer
|
||||||
|
startBucket uintptr
|
||||||
|
offset uint8
|
||||||
|
wrapped bool
|
||||||
|
B uint8
|
||||||
|
i uint8
|
||||||
|
bucket uintptr
|
||||||
|
checkBucket uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hiter) initialized() bool {
|
||||||
|
return h.t != nil
|
||||||
|
}
|
||||||
|
|
||||||
// A MapIter is an iterator for ranging over a map.
|
// A MapIter is an iterator for ranging over a map.
|
||||||
// See Value.MapRange.
|
// See Value.MapRange.
|
||||||
type MapIter struct {
|
type MapIter struct {
|
||||||
m Value
|
m Value
|
||||||
it unsafe.Pointer
|
hiter hiter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key returns the key of the iterator's current map entry.
|
// Key returns the key of the iterator's current map entry.
|
||||||
func (it *MapIter) Key() Value {
|
func (it *MapIter) Key() Value {
|
||||||
if it.it == nil {
|
if !it.hiter.initialized() {
|
||||||
panic("MapIter.Key called before Next")
|
panic("MapIter.Key called before Next")
|
||||||
}
|
}
|
||||||
iterkey := mapiterkey(it.it)
|
iterkey := mapiterkey(&it.hiter)
|
||||||
if iterkey == nil {
|
if iterkey == nil {
|
||||||
panic("MapIter.Key called on exhausted iterator")
|
panic("MapIter.Key called on exhausted iterator")
|
||||||
}
|
}
|
||||||
@ -1592,10 +1619,10 @@ func (it *MapIter) Key() Value {
|
|||||||
// It is equivalent to dst.Set(it.Key()), but it avoids allocating a new Value.
|
// It is equivalent to dst.Set(it.Key()), but it avoids allocating a new Value.
|
||||||
// As in Go, the key must be assignable to dst's type.
|
// As in Go, the key must be assignable to dst's type.
|
||||||
func (it *MapIter) SetKey(dst Value) {
|
func (it *MapIter) SetKey(dst Value) {
|
||||||
if it.it == nil {
|
if !it.hiter.initialized() {
|
||||||
panic("MapIter.SetKey called before Next")
|
panic("MapIter.SetKey called before Next")
|
||||||
}
|
}
|
||||||
iterkey := mapiterkey(it.it)
|
iterkey := mapiterkey(&it.hiter)
|
||||||
if iterkey == nil {
|
if iterkey == nil {
|
||||||
panic("MapIter.SetKey called on exhausted iterator")
|
panic("MapIter.SetKey called on exhausted iterator")
|
||||||
}
|
}
|
||||||
@ -1616,10 +1643,10 @@ func (it *MapIter) SetKey(dst Value) {
|
|||||||
|
|
||||||
// Value returns the value of the iterator's current map entry.
|
// Value returns the value of the iterator's current map entry.
|
||||||
func (it *MapIter) Value() Value {
|
func (it *MapIter) Value() Value {
|
||||||
if it.it == nil {
|
if !it.hiter.initialized() {
|
||||||
panic("MapIter.Value called before Next")
|
panic("MapIter.Value called before Next")
|
||||||
}
|
}
|
||||||
iterelem := mapiterelem(it.it)
|
iterelem := mapiterelem(&it.hiter)
|
||||||
if iterelem == nil {
|
if iterelem == nil {
|
||||||
panic("MapIter.Value called on exhausted iterator")
|
panic("MapIter.Value called on exhausted iterator")
|
||||||
}
|
}
|
||||||
@ -1633,10 +1660,10 @@ func (it *MapIter) Value() Value {
|
|||||||
// It is equivalent to dst.Set(it.Value()), but it avoids allocating a new Value.
|
// It is equivalent to dst.Set(it.Value()), but it avoids allocating a new Value.
|
||||||
// As in Go, the value must be assignable to dst's type.
|
// As in Go, the value must be assignable to dst's type.
|
||||||
func (it *MapIter) SetValue(dst Value) {
|
func (it *MapIter) SetValue(dst Value) {
|
||||||
if it.it == nil {
|
if !it.hiter.initialized() {
|
||||||
panic("MapIter.SetValue called before Next")
|
panic("MapIter.SetValue called before Next")
|
||||||
}
|
}
|
||||||
iterelem := mapiterelem(it.it)
|
iterelem := mapiterelem(&it.hiter)
|
||||||
if iterelem == nil {
|
if iterelem == nil {
|
||||||
panic("MapIter.SetValue called on exhausted iterator")
|
panic("MapIter.SetValue called on exhausted iterator")
|
||||||
}
|
}
|
||||||
@ -1659,15 +1686,15 @@ func (it *MapIter) SetValue(dst Value) {
|
|||||||
// entry. It returns false when the iterator is exhausted; subsequent
|
// entry. It returns false when the iterator is exhausted; subsequent
|
||||||
// calls to Key, Value, or Next will panic.
|
// calls to Key, Value, or Next will panic.
|
||||||
func (it *MapIter) Next() bool {
|
func (it *MapIter) Next() bool {
|
||||||
if it.it == nil {
|
if !it.hiter.initialized() {
|
||||||
it.it = mapiterinit(it.m.typ, it.m.pointer())
|
mapiterinit(it.m.typ, it.m.pointer(), &it.hiter)
|
||||||
} else {
|
} else {
|
||||||
if mapiterkey(it.it) == nil {
|
if mapiterkey(&it.hiter) == nil {
|
||||||
panic("MapIter.Next called on exhausted iterator")
|
panic("MapIter.Next called on exhausted iterator")
|
||||||
}
|
}
|
||||||
mapiternext(it.it)
|
mapiternext(&it.hiter)
|
||||||
}
|
}
|
||||||
return mapiterkey(it.it) != nil
|
return mapiterkey(&it.hiter) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapRange returns a range iterator for a map.
|
// MapRange returns a range iterator for a map.
|
||||||
@ -3216,19 +3243,17 @@ func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer)
|
|||||||
//go:noescape
|
//go:noescape
|
||||||
func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer)
|
func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer)
|
||||||
|
|
||||||
// m escapes into the return value, but the caller of mapiterinit
|
|
||||||
// doesn't let the return value escape.
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer
|
func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer)
|
func mapiterkey(it *hiter) (key unsafe.Pointer)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func mapiterelem(it unsafe.Pointer) (elem unsafe.Pointer)
|
func mapiterelem(it *hiter) (elem unsafe.Pointer)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func mapiternext(it unsafe.Pointer)
|
func mapiternext(it *hiter)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func maplen(m unsafe.Pointer) int
|
func maplen(m unsafe.Pointer) int
|
||||||
|
@ -160,8 +160,8 @@ type bmap struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A hash iteration structure.
|
// A hash iteration structure.
|
||||||
// If you modify hiter, also change cmd/compile/internal/reflectdata/reflect.go to indicate
|
// If you modify hiter, also change cmd/compile/internal/reflectdata/reflect.go
|
||||||
// the layout of this structure.
|
// and reflect/value.go to match the layout of this structure.
|
||||||
type hiter struct {
|
type hiter struct {
|
||||||
key unsafe.Pointer // Must be in first position. Write nil to indicate iteration end (see cmd/compile/internal/walk/range.go).
|
key unsafe.Pointer // Must be in first position. Write nil to indicate iteration end (see cmd/compile/internal/walk/range.go).
|
||||||
elem unsafe.Pointer // Must be in second position (see cmd/compile/internal/walk/range.go).
|
elem unsafe.Pointer // Must be in second position (see cmd/compile/internal/walk/range.go).
|
||||||
@ -806,6 +806,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
|
|||||||
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapiterinit))
|
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapiterinit))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it.t = t
|
||||||
if h == nil || h.count == 0 {
|
if h == nil || h.count == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -813,7 +814,6 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
|
|||||||
if unsafe.Sizeof(hiter{})/goarch.PtrSize != 12 {
|
if unsafe.Sizeof(hiter{})/goarch.PtrSize != 12 {
|
||||||
throw("hash_iter size incorrect") // see cmd/compile/internal/reflectdata/reflect.go
|
throw("hash_iter size incorrect") // see cmd/compile/internal/reflectdata/reflect.go
|
||||||
}
|
}
|
||||||
it.t = t
|
|
||||||
it.h = h
|
it.h = h
|
||||||
|
|
||||||
// grab snapshot of bucket state
|
// grab snapshot of bucket state
|
||||||
@ -1336,10 +1336,8 @@ func reflect_mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname reflect_mapiterinit reflect.mapiterinit
|
//go:linkname reflect_mapiterinit reflect.mapiterinit
|
||||||
func reflect_mapiterinit(t *maptype, h *hmap) *hiter {
|
func reflect_mapiterinit(t *maptype, h *hmap, it *hiter) {
|
||||||
it := new(hiter)
|
|
||||||
mapiterinit(t, h, it)
|
mapiterinit(t, h, it)
|
||||||
return it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname reflect_mapiternext reflect.mapiternext
|
//go:linkname reflect_mapiternext reflect.mapiternext
|
||||||
|
Loading…
Reference in New Issue
Block a user