]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/gc, runtime: speed up some cases of _, ok := i.(T)
authorJosh Bleecher Snyder <josharian@gmail.com>
Tue, 17 Mar 2015 22:14:31 +0000 (15:14 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Thu, 19 Mar 2015 16:20:32 +0000 (16:20 +0000)
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>
src/cmd/internal/gc/walk.go
src/runtime/iface.go
src/runtime/iface_test.go

index e55b41581c37856a6785e56b99988cdf3bc1e002..1626c11e45b2c125a497d08534220175da29e32e 100644 (file)
@@ -869,6 +869,32 @@ func walkexpr(np **Node, init **NodeList) {
                        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
                if isblank(n.List.N) {
                        resptr = nodnil()
@@ -877,7 +903,7 @@ func walkexpr(np **Node, init **NodeList) {
                }
                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)
                substArgTypes(fn, from.Type, t)
                call := mkcall1(fn, oktype, init, typename(t), from, resptr)
index 811a31bcd9cf292391f412b5c59174bf75574b9e..d94c3919c8cdee70324930382b7d2e69ba8fc801 100644 (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 {
        ep := (*eface)(unsafe.Pointer(&e))
        if ep._type != t {
-               if r != nil {
-                       memclr(r, uintptr(t.size))
-               }
+               memclr(r, uintptr(t.size))
                return false
        }
-       if r != nil {
-               if isDirectIface(t) {
-                       writebarrierptr((*uintptr)(r), uintptr(ep.data))
-               } else {
-                       typedmemmove(t, r, ep.data)
-               }
+       if isDirectIface(t) {
+               writebarrierptr((*uintptr)(r), uintptr(ep.data))
+       } else {
+               typedmemmove(t, r, ep.data)
        }
        return true
 }
@@ -262,17 +259,16 @@ func assertI2E(inter *interfacetype, i fInterface, r *interface{}) {
        return
 }
 
+// The compiler ensures that r is non-nil.
 func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool {
        ip := (*iface)(unsafe.Pointer(&i))
        tab := ip.tab
        if tab == nil {
                return false
        }
-       if r != nil {
-               rp := (*eface)(unsafe.Pointer(r))
-               rp._type = tab._type
-               rp.data = ip.data
-       }
+       rp := (*eface)(unsafe.Pointer(r))
+       rp._type = tab._type
+       rp.data = ip.data
        return true
 }
 
@@ -386,17 +382,14 @@ func assertE2E(inter *interfacetype, e interface{}, r *interface{}) {
        *r = e
 }
 
+// The compiler ensures that r is non-nil.
 func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool {
        ep := (*eface)(unsafe.Pointer(&e))
        if ep._type == nil {
-               if r != nil {
-                       *r = nil
-               }
+               *r = nil
                return false
        }
-       if r != nil {
-               *r = e
-       }
+       *r = e
        return true
 }
 
index bfeb94b8aa768898e4a22108f1e1c424df0e0c7f..f632a65629a2c4e7549bc08c2034311bf52acdd9 100644 (file)
@@ -37,6 +37,7 @@ var (
        ts TS
        tm TM
        tl TL
+       ok bool
 )
 
 // Issue 9370
@@ -178,3 +179,45 @@ func BenchmarkAssertE2E(b *testing.B) {
                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{})
+       }
+}