1
0
mirror of https://github.com/golang/go synced 2024-11-19 05:04:43 -07:00

cmd/internal/gc, runtime: speed up some cases of _, ok := i.(T)

Some type assertions of the form _, ok := i.(T) allow efficient inlining.
Such type assertions commonly show up in type switches.
For example, with this optimization, using 6g, the length of
encoding/binary's intDataSize function shrinks from 2224 to 1728 bytes (-22%).

benchmark                    old ns/op     new ns/op     delta
BenchmarkAssertI2E2Blank     4.67          0.82          -82.44%
BenchmarkAssertE2T2Blank     4.38          0.83          -81.05%
BenchmarkAssertE2E2Blank     3.88          0.83          -78.61%
BenchmarkAssertE2E2          14.2          14.4          +1.41%
BenchmarkAssertE2T2          10.3          10.4          +0.97%
BenchmarkAssertI2E2          13.4          13.3          -0.75%

Change-Id: Ie9798c3e85432bb8e0f2c723afc376e233639df7
Reviewed-on: https://go-review.googlesource.com/7697
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Josh Bleecher Snyder 2015-03-17 15:14:31 -07:00
parent 55b4516fd6
commit 25e793d7ea
3 changed files with 83 additions and 21 deletions

View File

@ -869,6 +869,32 @@ func walkexpr(np **Node, init **NodeList) {
oktype = ok.Type oktype = ok.Type
} }
fromKind := type2IET(from.Type)
toKind := type2IET(t)
// Avoid runtime calls in a few cases of the form _, ok := i.(T).
// This is faster and shorter and allows the corresponding assertX2X2
// routines to skip nil checks on their last argument.
if isblank(n.List.N) {
var fast *Node
switch {
case fromKind == "E" && toKind == "T":
tab := Nod(OITAB, from, nil) // type:eface::tab:iface
typ := Nod(OCONVNOP, typename(t), nil)
typ.Type = Ptrto(Types[TUINTPTR])
fast = Nod(OEQ, tab, typ)
case fromKind == "I" && toKind == "E",
fromKind == "E" && toKind == "E":
tab := Nod(OITAB, from, nil)
fast = Nod(ONE, tab, nodnil())
}
if fast != nil {
n = Nod(OAS, ok, fast)
typecheck(&n, Etop)
goto ret
}
}
var resptr *Node // &res var resptr *Node // &res
if isblank(n.List.N) { if isblank(n.List.N) {
resptr = nodnil() resptr = nodnil()
@ -877,7 +903,7 @@ func walkexpr(np **Node, init **NodeList) {
} }
resptr.Etype = 1 // addr does not escape resptr.Etype = 1 // addr does not escape
buf := "assert" + type2IET(from.Type) + "2" + type2IET(t) + "2" buf := "assert" + fromKind + "2" + toKind + "2"
fn := syslook(buf, 1) fn := syslook(buf, 1)
substArgTypes(fn, from.Type, t) substArgTypes(fn, from.Type, t)
call := mkcall1(fn, oktype, init, typename(t), from, resptr) call := mkcall1(fn, oktype, init, typename(t), from, resptr)

View File

@ -219,20 +219,17 @@ func assertE2T(t *_type, e interface{}, r unsafe.Pointer) {
} }
} }
// The compiler ensures that r is non-nil.
func assertE2T2(t *_type, e interface{}, r unsafe.Pointer) bool { func assertE2T2(t *_type, e interface{}, r unsafe.Pointer) bool {
ep := (*eface)(unsafe.Pointer(&e)) ep := (*eface)(unsafe.Pointer(&e))
if ep._type != t { if ep._type != t {
if r != nil { memclr(r, uintptr(t.size))
memclr(r, uintptr(t.size))
}
return false return false
} }
if r != nil { if isDirectIface(t) {
if isDirectIface(t) { writebarrierptr((*uintptr)(r), uintptr(ep.data))
writebarrierptr((*uintptr)(r), uintptr(ep.data)) } else {
} else { typedmemmove(t, r, ep.data)
typedmemmove(t, r, ep.data)
}
} }
return true return true
} }
@ -262,17 +259,16 @@ func assertI2E(inter *interfacetype, i fInterface, r *interface{}) {
return return
} }
// The compiler ensures that r is non-nil.
func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool { func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool {
ip := (*iface)(unsafe.Pointer(&i)) ip := (*iface)(unsafe.Pointer(&i))
tab := ip.tab tab := ip.tab
if tab == nil { if tab == nil {
return false return false
} }
if r != nil { rp := (*eface)(unsafe.Pointer(r))
rp := (*eface)(unsafe.Pointer(r)) rp._type = tab._type
rp._type = tab._type rp.data = ip.data
rp.data = ip.data
}
return true return true
} }
@ -386,17 +382,14 @@ func assertE2E(inter *interfacetype, e interface{}, r *interface{}) {
*r = e *r = e
} }
// The compiler ensures that r is non-nil.
func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool { func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool {
ep := (*eface)(unsafe.Pointer(&e)) ep := (*eface)(unsafe.Pointer(&e))
if ep._type == nil { if ep._type == nil {
if r != nil { *r = nil
*r = nil
}
return false return false
} }
if r != nil { *r = e
*r = e
}
return true return true
} }

View File

@ -37,6 +37,7 @@ var (
ts TS ts TS
tm TM tm TM
tl TL tl TL
ok bool
) )
// Issue 9370 // Issue 9370
@ -178,3 +179,45 @@ func BenchmarkAssertE2E(b *testing.B) {
e_ = e e_ = e
} }
} }
func BenchmarkAssertE2T2(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
tm, ok = e.(TM)
}
}
func BenchmarkAssertE2T2Blank(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
_, ok = e.(TM)
}
}
func BenchmarkAssertI2E2(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
e, ok = i1.(interface{})
}
}
func BenchmarkAssertI2E2Blank(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
_, ok = i1.(interface{})
}
}
func BenchmarkAssertE2E2(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
e_, ok = e.(interface{})
}
}
func BenchmarkAssertE2E2Blank(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
_, ok = e.(interface{})
}
}