reflect, runtime: optimize Name method
Several minor changes that remove a good chunk of the overhead added
to the reflect Name method over the 1.7 cycle, as seen from the
non-SSA architectures.
In particular, there are ~20 fewer instructions in reflect.name.name
on 386, and the method now qualifies for inlining.
The simple JSON decoding benchmark on darwin/386:
name old time/op new time/op delta
CodeDecoder-8 49.2ms ± 0% 48.9ms ± 1% -0.77% (p=0.000 n=10+9)
name old speed new speed delta
CodeDecoder-8 39.4MB/s ± 0% 39.7MB/s ± 1% +0.77% (p=0.000 n=10+9)
On darwin/amd64 the effect is less pronounced:
name old time/op new time/op delta
CodeDecoder-8 38.9ms ± 0% 38.7ms ± 1% -0.38% (p=0.005 n=10+10)
name old speed new speed delta
CodeDecoder-8 49.9MB/s ± 0% 50.1MB/s ± 1% +0.38% (p=0.006 n=10+10)
Counterintuitively, I get much more useful benchmark data out of my
MacBook Pro than a linux workstation with more expensive Intel chips.
While the laptop has fewer cores and an active GUI, the single-threaded
performance is significantly better (nearly 1.5x decoding throughput)
so the differences are more pronounced.
For #16117.
Change-Id: I4e0cc1cc2d271d47d5127b1ee1ca926faf34cabf
Reviewed-on: https://go-review.googlesource.com/24510
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 5b800fc..bedfba4 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -466,15 +466,13 @@
func (n name) name() (s string) {
if n.bytes == nil {
- return ""
+ return
}
- nl := n.nameLen()
- if nl == 0 {
- return ""
- }
+ b := (*[4]byte)(unsafe.Pointer(n.bytes))
+
hdr := (*stringHeader)(unsafe.Pointer(&s))
- hdr.Data = unsafe.Pointer(n.data(3))
- hdr.Len = nl
+ hdr.Data = unsafe.Pointer(&b[3])
+ hdr.Len = int(b[1])<<8 | int(b[2])
return s
}
@@ -662,16 +660,10 @@
type textOff int32 // offset from top of text section
func (t *rtype) nameOff(off nameOff) name {
- if off == 0 {
- return name{}
- }
return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
}
func (t *rtype) typeOff(off typeOff) *rtype {
- if off == 0 {
- return nil
- }
return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off)))
}
diff --git a/src/runtime/type.go b/src/runtime/type.go
index 49d3855..5ef11a4 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -170,32 +170,29 @@
return name{}
}
base := uintptr(ptrInModule)
- var md *moduledata
- for next := &firstmoduledata; next != nil; next = next.next {
- if base >= next.types && base < next.etypes {
- md = next
- break
- }
- }
- if md == nil {
- reflectOffsLock()
- res, found := reflectOffs.m[int32(off)]
- reflectOffsUnlock()
- if !found {
- println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
- for next := &firstmoduledata; next != nil; next = next.next {
- println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+ for md := &firstmoduledata; md != nil; md = md.next {
+ if base >= md.types && base < md.etypes {
+ res := md.types + uintptr(off)
+ if res > md.etypes {
+ println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
+ throw("runtime: name offset out of range")
}
- throw("runtime: name offset base pointer out of range")
+ return name{(*byte)(unsafe.Pointer(res))}
}
- return name{(*byte)(res)}
}
- res := md.types + uintptr(off)
- if res > md.etypes {
- println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
- throw("runtime: name offset out of range")
+
+ // No module found. see if it is a run time name.
+ reflectOffsLock()
+ res, found := reflectOffs.m[int32(off)]
+ reflectOffsUnlock()
+ if !found {
+ println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
+ for next := &firstmoduledata; next != nil; next = next.next {
+ println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+ }
+ throw("runtime: name offset base pointer out of range")
}
- return name{(*byte)(unsafe.Pointer(res))}
+ return name{(*byte)(res)}
}
func (t *_type) nameOff(off nameOff) name {