mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
reflect: mark mapassign as noescape
The lack of this annotation causes Value.SetMapIndex to allocate when it doesn't need to. Add comments about why it's safe to do so. Add a test to make sure we stay allocation-free. Change-Id: I00826e0d73e317a31bdeae5c7e46bf95b0c6ae6a Reviewed-on: https://go-review.googlesource.com/17060 Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
476aa95015
commit
8d31a86a1e
2 changed files with 44 additions and 7 deletions
|
|
@ -4965,3 +4965,32 @@ func TestPtrToMethods(t *testing.T) {
|
||||||
t.Fatal("does not implement Stringer, but should")
|
t.Fatal("does not implement Stringer, but should")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapAlloc(t *testing.T) {
|
||||||
|
m := ValueOf(make(map[int]int, 10))
|
||||||
|
k := ValueOf(5)
|
||||||
|
v := ValueOf(7)
|
||||||
|
allocs := testing.AllocsPerRun(100, func() {
|
||||||
|
m.SetMapIndex(k, v)
|
||||||
|
})
|
||||||
|
if allocs > 0.5 {
|
||||||
|
t.Errorf("allocs per map assignment: want 0 got %f", allocs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChanAlloc(t *testing.T) {
|
||||||
|
// Note: for a chan int, the return Value must be allocated, so we
|
||||||
|
// use a chan *int instead.
|
||||||
|
c := ValueOf(make(chan *int, 1))
|
||||||
|
v := ValueOf(new(int))
|
||||||
|
allocs := testing.AllocsPerRun(100, func() {
|
||||||
|
c.Send(v)
|
||||||
|
_, _ = c.Recv()
|
||||||
|
})
|
||||||
|
if allocs < 0.5 || allocs > 1.5 {
|
||||||
|
t.Errorf("allocs per chan send/recv: want 1 got %f", allocs)
|
||||||
|
}
|
||||||
|
// Note: there is one allocation in reflect.recv which seems to be
|
||||||
|
// a limitation of escape analysis. If that is ever fixed the
|
||||||
|
// allocs < 0.5 condition will trigger and this test should be fixed.
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ func unpackEface(i interface{}) Value {
|
||||||
if ifaceIndir(t) {
|
if ifaceIndir(t) {
|
||||||
f |= flagIndir
|
f |= flagIndir
|
||||||
}
|
}
|
||||||
return Value{t, unsafe.Pointer(e.word), f}
|
return Value{t, e.word, f}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ValueError occurs when a Value method is invoked on
|
// A ValueError occurs when a Value method is invoked on
|
||||||
|
|
@ -590,7 +590,7 @@ func storeRcvr(v Value, p unsafe.Pointer) {
|
||||||
if t.Kind() == Interface {
|
if t.Kind() == Interface {
|
||||||
// the interface data word becomes the receiver word
|
// the interface data word becomes the receiver word
|
||||||
iface := (*nonEmptyInterface)(v.ptr)
|
iface := (*nonEmptyInterface)(v.ptr)
|
||||||
*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
|
*(*unsafe.Pointer)(p) = iface.word
|
||||||
} else if v.flag&flagIndir != 0 && !ifaceIndir(t) {
|
} else if v.flag&flagIndir != 0 && !ifaceIndir(t) {
|
||||||
*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
|
*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2086,11 +2086,10 @@ func ValueOf(i interface{}) Value {
|
||||||
return Value{}
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rsc): Eliminate this terrible hack.
|
// TODO: Maybe allow contents of a Value to live on the stack.
|
||||||
// In the call to unpackEface, i.typ doesn't escape,
|
// For now we make the contents always escape to the heap. It
|
||||||
// and i.word is an integer. So it looks like
|
// makes life easier in a few places (see chanrecv/mapassign
|
||||||
// i doesn't escape. But really it does,
|
// comment below).
|
||||||
// because i.word is actually a pointer.
|
|
||||||
escapes(i)
|
escapes(i)
|
||||||
|
|
||||||
return unpackEface(i)
|
return unpackEface(i)
|
||||||
|
|
@ -2446,6 +2445,14 @@ func chancap(ch unsafe.Pointer) int
|
||||||
func chanclose(ch unsafe.Pointer)
|
func chanclose(ch unsafe.Pointer)
|
||||||
func chanlen(ch unsafe.Pointer) int
|
func chanlen(ch unsafe.Pointer) int
|
||||||
|
|
||||||
|
// Note: some of the noescape annotations below are technically a lie,
|
||||||
|
// but safe in the context of this package. Functions like chansend
|
||||||
|
// and mapassign don't escape the referent, but may escape anything
|
||||||
|
// the referent points to (they do shallow copies of the referent).
|
||||||
|
// It is safe in this package because the referent may only point
|
||||||
|
// to something a Value may point to, and that is always in the heap
|
||||||
|
// (due to the escapes() call in ValueOf).
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func chanrecv(t *rtype, ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
|
func chanrecv(t *rtype, ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
|
||||||
|
|
||||||
|
|
@ -2458,6 +2465,7 @@ func makemap(t *rtype) (m unsafe.Pointer)
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func mapaccess(t *rtype, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer)
|
func mapaccess(t *rtype, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer)
|
func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue