Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: inline reflect.TypeOf (or make it intrinsic) #16869

Open
bradfitz opened this issue Aug 24, 2016 · 9 comments
Open

cmd/compile: inline reflect.TypeOf (or make it intrinsic) #16869

bradfitz opened this issue Aug 24, 2016 · 9 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. Performance
Milestone

Comments

@bradfitz
Copy link
Contributor

bradfitz commented Aug 24, 2016

@randall77 was mentioning the compiler maybe inlining reflect.TypeOf calls the other day. Or somebody was.

Today I ran across the github.com/vmware/govmomi/vim25/types package which has 5769 calls to reflect.TypeOf, populating a map:

types.go:       t["WillLoseHAProtection"] = reflect.TypeOf((*WillLoseHAProtection)(nil)).Elem()
types.go:       t["WillLoseHAProtectionFault"] = reflect.TypeOf((*WillLoseHAProtectionFault)(nil)).Elem()
types.go:       t["WillModifyConfigCpuRequirements"] = reflect.TypeOf((*WillModifyConfigCpuRequirements)(nil)).Elem()
types.go:       t["WillModifyConfigCpuRequirementsFault"] = reflect.TypeOf((*WillModifyConfigCpuRequirementsFault)(nil)).Elem()
types.go:       t["WillResetSnapshotDirectory"] = reflect.TypeOf((*WillResetSnapshotDirectory)(nil)).Elem()
types.go:       t["WillResetSnapshotDirectoryFault"] = reflect.TypeOf((*WillResetSnapshotDirectoryFault)(nil)).Elem()
types.go:       t["WinNetBIOSConfigInfo"] = reflect.TypeOf((*WinNetBIOSConfigInfo)(nil)).Elem()
types.go:       t["WipeDiskFault"] = reflect.TypeOf((*WipeDiskFault)(nil)).Elem()
types.go:       t["WipeDiskFaultFault"] = reflect.TypeOf((*WipeDiskFaultFault)(nil)).Elem()
types.go:       t["XmlToCustomizationSpecItem"] = reflect.TypeOf((*XmlToCustomizationSpecItem)(nil)).Elem()
types.go:       t["XmlToCustomizationSpecItemRequestType"] = reflect.TypeOf((*XmlToCustomizationSpecItemRequestType)(nil)).Elem()
types.go:       t["ZeroFillVirtualDiskRequestType"] = reflect.TypeOf((*ZeroFillVirtualDiskRequestType)(nil)).Elem()
types.go:       t["ZeroFillVirtualDisk_Task"] = reflect.TypeOf((*ZeroFillVirtualDisk_Task)(nil)).Elem()

If the compiler could inline the reflect.TypeOf calls then they could stop autogenerating the Elem part too, and it's all just static data and their init load could drop a bunch.

reflect.TypeOf is just:

// TypeOf returns the reflection Type that represents the dynamic type of i.                                                                                            
// If i is a nil interface value, TypeOf returns nil.                                                                                                                   
func TypeOf(i interface{}) Type {
        eface := *(*emptyInterface)(unsafe.Pointer(&i))
        return toType(eface.typ)
}

func toType(t *rtype) Type {
        if t == nil {
                return nil
        }
        return t
}

Low priority.

@bradfitz bradfitz added this to the Unplanned milestone Aug 24, 2016
@bradfitz
Copy link
Contributor Author

/cc @josharian

@robpike
Copy link
Contributor

robpike commented Aug 25, 2016

That's a lot of reflect.TypeOfs.

@bradfitz
Copy link
Contributor Author

@robpike, this is generated code from something akin to our protocol buffers or google-api-go-client. So it's the sort of auto-generated gobbledygook you might expect.

@JayNakrani
Copy link
Contributor

Compiler seems to be inlining reflect.TypeOf() already.
I tried with,

$ go build -gcflags="-m" github.com/vmware/govmomi/vim25/types 2>&1 | grep "types\.go.*inlining call to reflect\."
src/github.com/vmware/govmomi/vim25/types/types.go:28: inlining call to reflect.TypeOf
src/github.com/vmware/govmomi/vim25/types/types.go:28: inlining call to reflect.toType
src/github.com/vmware/govmomi/vim25/types/types.go:37: inlining call to reflect.TypeOf
src/github.com/vmware/govmomi/vim25/types/types.go:37: inlining call to reflect.toType
...

@egorse
Copy link

egorse commented Jan 23, 2018

@bradfitz does that means that something similar to

type A struct {
	V1 int
}

type B struct {
	V1 string
	V2 int
}

var table = []reflect.Type{
	reflect.TypeOf(A{}),
	reflect.TypeOf(B{}),
}

should lead to no calls to reflect.TypeOf at all ?

@bradfitz
Copy link
Contributor Author

@egorse, correct. No calls to reflect.TypeOf. Currently with your example code I see:

"".init STEXT size=317 args=0x0 locals=0x48
        0x0000 00000 (<autogenerated>:1)        TEXT    "".init(SB), $72-0
        0x0000 00000 (<autogenerated>:1)        MOVQ    (TLS), CX
        0x0009 00009 (<autogenerated>:1)        CMPQ    SP, 16(CX)
        0x000d 00013 (<autogenerated>:1)        JLS     307
        0x0013 00019 (<autogenerated>:1)        SUBQ    $72, SP
        0x0017 00023 (<autogenerated>:1)        MOVQ    BP, 64(SP)
        0x001c 00028 (<autogenerated>:1)        LEAQ    64(SP), BP
        0x0021 00033 (<autogenerated>:1)        FUNCDATA        $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
        0x0021 00033 (<autogenerated>:1)        FUNCDATA        $1, gclocals·713abd6cdf5e052e4dcd3eb297c82601(SB)
        0x0021 00033 (<autogenerated>:1)        MOVBLZX "".initdone·(SB), AX
        0x0028 00040 (<autogenerated>:1)        CMPB    AL, $1
        0x002a 00042 (<autogenerated>:1)        JLS     54
        0x002c 00044 (<autogenerated>:1)        MOVQ    64(SP), BP
        0x0031 00049 (<autogenerated>:1)        ADDQ    $72, SP
        0x0035 00053 (<autogenerated>:1)        RET
        0x0036 00054 (<autogenerated>:1)        JNE     63
        0x0038 00056 (<autogenerated>:1)        PCDATA  $0, $0
        0x0038 00056 (<autogenerated>:1)        CALL    runtime.throwinit(SB)
        0x003d 00061 (<autogenerated>:1)        UNDEF
        0x003f 00063 (<autogenerated>:1)        MOVB    $1, "".initdone·(SB)
        0x0046 00070 (<autogenerated>:1)        PCDATA  $0, $0
        0x0046 00070 (<autogenerated>:1)        CALL    reflect.init(SB)
        0x004b 00075 (x.go:15)  MOVQ    $0, ""..autotmp_0+32(SP)
        0x0054 00084 (x.go:15)  LEAQ    type."".A(SB), AX
        0x005b 00091 (x.go:15)  MOVQ    AX, (SP)
        0x005f 00095 (x.go:15)  LEAQ    ""..autotmp_0+32(SP), AX
        0x0064 00100 (x.go:15)  MOVQ    AX, 8(SP)
        0x0069 00105 (x.go:15)  PCDATA  $0, $0
        0x0069 00105 (x.go:15)  CALL    runtime.convT2E64(SB)
        0x006e 00110 (x.go:15)  MOVUPS  16(SP), X0
        0x0073 00115 (x.go:15)  MOVUPS  X0, (SP)
        0x0077 00119 (x.go:15)  PCDATA  $0, $0
        0x0077 00119 (x.go:15)  CALL    reflect.TypeOf(SB)
        0x007c 00124 (x.go:15)  MOVQ    16(SP), AX
        0x0081 00129 (x.go:15)  MOVQ    24(SP), CX
        0x0086 00134 (x.go:15)  MOVQ    AX, "".statictmp_0(SB)
        0x008d 00141 (x.go:15)  MOVL    runtime.writeBarrier(SB), AX
        0x0093 00147 (x.go:15)  TESTL   AX, AX
        0x0095 00149 (x.go:15)  JNE     287
        0x009b 00155 (x.go:15)  MOVQ    CX, "".statictmp_0+8(SB)
        0x00a2 00162 (x.go:16)  XORPS   X0, X0
        0x00a5 00165 (x.go:16)  MOVUPS  X0, ""..autotmp_1+40(SP)
        0x00aa 00170 (x.go:16)  MOVQ    $0, ""..autotmp_1+56(SP)
        0x00b3 00179 (x.go:16)  LEAQ    type."".B(SB), AX
        0x00ba 00186 (x.go:16)  MOVQ    AX, (SP)
        0x00be 00190 (x.go:16)  LEAQ    ""..autotmp_1+40(SP), AX
        0x00c3 00195 (x.go:16)  MOVQ    AX, 8(SP)
        0x00c8 00200 (x.go:16)  PCDATA  $0, $1
        0x00c8 00200 (x.go:16)  CALL    runtime.convT2E(SB)
        0x00cd 00205 (x.go:16)  MOVUPS  16(SP), X0
        0x00d2 00210 (x.go:16)  MOVUPS  X0, (SP)
        0x00d6 00214 (x.go:16)  PCDATA  $0, $1
        0x00d6 00214 (x.go:16)  CALL    reflect.TypeOf(SB)
        0x00db 00219 (x.go:16)  MOVQ    16(SP), AX
        0x00e0 00224 (x.go:16)  MOVQ    24(SP), CX
        0x00e5 00229 (x.go:16)  MOVQ    AX, "".statictmp_0+16(SB)
        0x00ec 00236 (x.go:16)  MOVL    runtime.writeBarrier(SB), AX
        0x00f2 00242 (x.go:16)  TESTL   AX, AX
        0x00f4 00244 (x.go:16)  JNE     270
        0x00f6 00246 (x.go:16)  MOVQ    CX, "".statictmp_0+24(SB)
        0x00fd 00253 (<autogenerated>:1)        MOVB    $2, "".initdone·(SB)
        0x0104 00260 (<autogenerated>:1)        MOVQ    64(SP), BP
        0x0109 00265 (<autogenerated>:1)        ADDQ    $72, SP
        0x010d 00269 (<autogenerated>:1)        RET
        0x010e 00270 (x.go:16)  LEAQ    "".statictmp_0+24(SB), DI
        0x0115 00277 (x.go:16)  MOVQ    CX, AX
        0x0118 00280 (x.go:16)  CALL    runtime.gcWriteBarrier(SB)
        0x011d 00285 (x.go:16)  JMP     253
        0x011f 00287 (x.go:15)  LEAQ    "".statictmp_0+8(SB), DI
        0x0126 00294 (x.go:15)  MOVQ    CX, AX
        0x0129 00297 (x.go:15)  CALL    runtime.gcWriteBarrier(SB)
        0x012e 00302 (x.go:15)  JMP     162
        0x0133 00307 (x.go:15)  NOP

@josharian
Copy link
Contributor

Note that there are actually two separate things here: treating reflect.TypeOf as an intrinsic, and handling reflect.TypeOf when generating static data. I think we should do both, and I just encountered some hot code in which the former would help. Note that treating reflect.TypeOf as an intrinsic is better than inlining it, because we can avoid creating an interface value, which often allocates. (Purity analysis might also help here, but reflect.TypeOf is a great intrinsic candidate anyway.)

@bradfitz bradfitz changed the title cmd/compile: inline reflect.TypeOf? cmd/compile: inline reflect.TypeOf (or make it intrinsic) Jun 23, 2020
@bradfitz
Copy link
Contributor Author

@randall77, I ran into this again. Know anybody on the compiler team who is looking for a project? :)

@randall77
Copy link
Contributor

So reflect.TypeOf does inline. Try this program:

package p

import "reflect"

type A struct {
	V1 int
}

type B struct {
	V1 string
	V2 int
}

var Table = []reflect.Type{
	reflect.TypeOf(A{}),
	reflect.TypeOf(B{}),
}

func f() {
	Table = []reflect.Type{
		reflect.TypeOf(A{}),
		reflect.TypeOf(B{}),
	}
}

reflect.TypeOf does inline in f, but it does not inline in the generated init function. It looks like we're not calling the inliner in generated init functions. This patch fixes it:

--- a/src/cmd/compile/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -60,6 +60,7 @@ func fninit(n []*Node) {
                Curfn = fn
                typecheckslice(nf, ctxStmt)
                Curfn = nil
+               inlcalls(fn)
                funccompile(fn)
                fns = append(fns, initializers.Linksym())
        }

The inlined code isn't great either, in that it doesn't realize that it doesn't need the data word of the interface, just type word, so it is still calling one of the runtime.convT2E functions. The result ends up getting thrown away.

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. Performance
Projects
None yet
Development

No branches or pull requests

7 participants