From 13a00a4b6b62ff503ed05a23b2261f4f10fd8ddb Mon Sep 17 00:00:00 2001 From: Andrew Woloszyn Date: Wed, 3 Jan 2018 09:46:30 -0500 Subject: [PATCH] Reworked memory functions to rely less on reflection. This re-orders the methods so that we can decode memory much quicker. It brings decoding time down for Vulkan traces by ~75%. When we do have to do reflection, optimize it such that the interfaces that must be matched are smaller. --- gapis/api/templates/api.go.tmpl | 3 + gapis/api/testcmd/cmds.go | 1 + gapis/memory/alignof_sizeof.go | 179 ++++++++++++++++++-------------- gapis/memory/pointer.go | 12 +++ gapis/memory/read.go | 115 +++++++++++--------- gapis/memory/types.go | 2 +- gapis/memory/write.go | 105 +++++++++++-------- 7 files changed, 242 insertions(+), 175 deletions(-) diff --git a/gapis/api/templates/api.go.tmpl b/gapis/api/templates/api.go.tmpl index 35750476dd..7cb22f52b9 100644 --- a/gapis/api/templates/api.go.tmpl +++ b/gapis/api/templates/api.go.tmpl @@ -232,6 +232,9 @@ // IsNullptr returns true if the address is 0 and the pool is ϟmem.ApplicationPool. func (p {{$ptr_ty}}) IsNullptr() bool { return p.addr == 0 && p.pool == ϟmem.ApplicationPool } + // APointer implements the ReflectPointer interface + func (p {{$ptr_ty}}) APointer() { return } + // Address returns the pointer's memory address. func (p {{$ptr_ty}}) Address() uint64 { return p.addr } diff --git a/gapis/api/testcmd/cmds.go b/gapis/api/testcmd/cmds.go index d7621e21d5..82a914dac1 100644 --- a/gapis/api/testcmd/cmds.go +++ b/gapis/api/testcmd/cmds.go @@ -108,6 +108,7 @@ var _ memory.Pointer = &Pointer{} func (p Pointer) String() string { return memory.PointerToString(p) } func (p Pointer) IsNullptr() bool { return p.addr == 0 && p.pool == memory.ApplicationPool } +func (p Pointer) APointer() { return } func (p Pointer) Address() uint64 { return p.addr } func (p Pointer) Pool() memory.PoolID { return p.pool } func (p Pointer) Offset(n uint64) memory.Pointer { panic("not implemented") } diff --git a/gapis/memory/alignof_sizeof.go b/gapis/memory/alignof_sizeof.go index 283bbb0a71..4bcaa018aa 100644 --- a/gapis/memory/alignof_sizeof.go +++ b/gapis/memory/alignof_sizeof.go @@ -24,95 +24,120 @@ import ( // AlignOf returns the byte alignment of the type t. func AlignOf(t reflect.Type, m *device.MemoryLayout) uint64 { - switch { - case t.Implements(tyPointer): - return uint64(m.GetPointer().GetAlignment()) - case t.Implements(tyCharTy): - return uint64(m.GetChar().GetAlignment()) - case t.Implements(tyIntTy), t.Implements(tyUintTy): - return uint64(m.GetInteger().GetAlignment()) - case t.Implements(tySizeTy): - return uint64(m.GetSize().GetAlignment()) - default: + handlePointer := func() (uint64, bool) { + if t.Implements(tyPointer) { + return uint64(m.GetPointer().GetAlignment()), true + } + return 0, false + } - switch t.Kind() { - case reflect.Bool, reflect.Int8, reflect.Uint8: - return uint64(m.GetI8().GetAlignment()) - case reflect.Int16, reflect.Uint16: - return uint64(m.GetI16().GetAlignment()) - case reflect.Int32, reflect.Uint32: - return uint64(m.GetI32().GetAlignment()) - case reflect.Float32: - return uint64(m.GetF32().GetAlignment()) - case reflect.Float64: - return uint64(m.GetF64().GetAlignment()) - case reflect.Int64, reflect.Uint64: - return uint64(m.GetI64().GetAlignment()) - case reflect.Int, reflect.Uint: + switch t.Kind() { + case reflect.Uint8: + if t.Implements(tyCharTy) { + return uint64(m.GetChar().GetAlignment()) + } + return uint64(m.GetI8().GetAlignment()) + case reflect.Bool, reflect.Int8: + return uint64(m.GetI8().GetAlignment()) + case reflect.Int16, reflect.Uint16: + return uint64(m.GetI16().GetAlignment()) + case reflect.Int32, reflect.Uint32: + return uint64(m.GetI32().GetAlignment()) + case reflect.Float32: + return uint64(m.GetF32().GetAlignment()) + case reflect.Float64: + return uint64(m.GetF64().GetAlignment()) + case reflect.Int64, reflect.Uint64: + if t.Implements(tyIntTy) || t.Implements(tyUintTy) { return uint64(m.GetInteger().GetAlignment()) - case reflect.Array, reflect.Slice: - return AlignOf(t.Elem(), m) - case reflect.String: - return 1 - case reflect.Struct: - alignment := uint64(1) - for i, c := 0, t.NumField(); i < c; i++ { - if a := AlignOf(t.Field(i).Type, m); alignment < a { - alignment = a - } + } + if t.Implements(tySizeTy) { + return uint64(m.GetSize().GetAlignment()) + } + return uint64(m.GetI64().GetAlignment()) + case reflect.Int, reflect.Uint: + return uint64(m.GetInteger().GetAlignment()) + case reflect.Array, reflect.Slice: + return AlignOf(t.Elem(), m) + case reflect.String: + return 1 + case reflect.Struct: + if size, ok := handlePointer(); ok { + return size + } + alignment := uint64(1) + for i, c := 0, t.NumField(); i < c; i++ { + if a := AlignOf(t.Field(i).Type, m); alignment < a { + alignment = a } - return alignment - default: - panic(fmt.Errorf("MemoryLayout.AlignOf not implemented for type %v (%v)", t, t.Kind())) } + return alignment + default: + if size, ok := handlePointer(); ok { + return size + } + panic(fmt.Errorf("MemoryLayout.AlignOf not implemented for type %v (%v)", t, t.Kind())) } } // SizeOf returns the byte size of the type t. func SizeOf(t reflect.Type, m *device.MemoryLayout) uint64 { - switch { - case t.Implements(tyPointer): - return uint64(m.GetPointer().GetSize()) - case t.Implements(tyCharTy): - return uint64(m.GetChar().GetSize()) - case t.Implements(tyIntTy), t.Implements(tyUintTy): - return uint64(m.GetInteger().GetSize()) - case t.Implements(tySizeTy): - return uint64(m.GetSize().GetSize()) - default: - switch t.Kind() { - case reflect.Bool, reflect.Int8, reflect.Uint8: - return uint64(m.GetI8().GetSize()) - case reflect.Int16, reflect.Uint16: - return uint64(m.GetI16().GetSize()) - case reflect.Int32, reflect.Uint32: - return uint64(m.GetI32().GetSize()) - case reflect.Float32: - return uint64(m.GetF32().GetSize()) - case reflect.Float64: - return uint64(m.GetF64().GetSize()) - case reflect.Int64, reflect.Uint64: - return uint64(m.GetI64().GetSize()) - case reflect.Int, reflect.Uint: + handlePointer := func() (uint64, bool) { + if t.Implements(tyPointer) { + return uint64(m.GetPointer().GetSize()), true + } + return 0, false + } + + switch t.Kind() { + case reflect.Uint8: + if t.Implements(tyCharTy) { + return uint64(m.GetChar().GetSize()) + } + return uint64(m.GetI8().GetSize()) + case reflect.Bool, reflect.Int8: + return uint64(m.GetI8().GetSize()) + case reflect.Int16, reflect.Uint16: + return uint64(m.GetI16().GetSize()) + case reflect.Int32, reflect.Uint32: + return uint64(m.GetI32().GetSize()) + case reflect.Float32: + return uint64(m.GetF32().GetSize()) + case reflect.Float64: + return uint64(m.GetF64().GetSize()) + case reflect.Int64, reflect.Uint64: + if t.Implements(tyIntTy) || t.Implements(tyUintTy) { return uint64(m.GetInteger().GetSize()) - case reflect.Array: - return SizeOf(t.Elem(), m) * uint64(t.Len()) - case reflect.String: - return 1 - case reflect.Struct: - var size, align uint64 - for i, c := 0, t.NumField(); i < c; i++ { - f := t.Field(i) - a := AlignOf(f.Type, m) - size = u64.AlignUp(size, a) - size += SizeOf(f.Type, m) - align = u64.Max(align, a) - } - size = u64.AlignUp(size, align) + } + if t.Implements(tySizeTy) { + return uint64(m.GetSize().GetSize()) + } + return uint64(m.GetI64().GetSize()) + case reflect.Int, reflect.Uint: + return uint64(m.GetInteger().GetSize()) + case reflect.Array: + return SizeOf(t.Elem(), m) * uint64(t.Len()) + case reflect.String: + return 1 + case reflect.Struct: + if size, ok := handlePointer(); ok { + return size + } + var size, align uint64 + for i, c := 0, t.NumField(); i < c; i++ { + f := t.Field(i) + a := AlignOf(f.Type, m) + size = u64.AlignUp(size, a) + size += SizeOf(f.Type, m) + align = u64.Max(align, a) + } + size = u64.AlignUp(size, align) + return size + default: + if size, ok := handlePointer(); ok { return size - default: - panic(fmt.Errorf("MemoryLayout.SizeOf not implemented for type %v (%v)", t, t.Kind())) } + panic(fmt.Errorf("MemoryLayout.SizeOf not implemented for type %v (%v)", t, t.Kind())) } } diff --git a/gapis/memory/pointer.go b/gapis/memory/pointer.go index 9d7ba8b949..1e5a20e5c6 100644 --- a/gapis/memory/pointer.go +++ b/gapis/memory/pointer.go @@ -31,6 +31,8 @@ const bits32 = uint64(1) << 32 // Pointer is the type representing a memory pointer. type Pointer interface { + ReflectPointer + // IsNullptr returns true if the address is 0 and the pool is memory.ApplicationPool. IsNullptr() bool @@ -54,6 +56,15 @@ type Pointer interface { ISlice(start, end uint64, m *device.MemoryLayout) Slice } +// This is a helper interface, if you want your pointer to be reflected +// then it must ALSO implement this interface. Since reflection is slow +// having a much smaller interface to check is significantly better +// We name this APointer since reflect.Implements checks functions +// in alphabetical order, meaning this should get hit first (or close to). +type ReflectPointer interface { + APointer() +} + // NewPtr returns a new pointer. func NewPtr(addr uint64, pool PoolID, elTy reflect.Type) Pointer { return ptr{addr, pool, elTy} @@ -78,6 +89,7 @@ func (p ptr) Pool() PoolID { return p.pool } func (p ptr) Offset(n uint64) Pointer { return ptr{p.addr + n, p.pool, p.elTy} } func (p ptr) ElementSize(m *device.MemoryLayout) uint64 { return SizeOf(p.elTy, m) } func (p ptr) ElementType() reflect.Type { return p.elTy } +func (p ptr) APointer() { return } func (p ptr) ISlice(start, end uint64, m *device.MemoryLayout) Slice { if start > end { panic(fmt.Errorf("%v.Slice start (%d) is greater than the end (%d)", p, start, end)) diff --git a/gapis/memory/read.go b/gapis/memory/read.go index 7b56ed338e..d01057ca3e 100644 --- a/gapis/memory/read.go +++ b/gapis/memory/read.go @@ -41,56 +41,67 @@ func deref(v reflect.Value) reflect.Value { func decode(d *Decoder, v reflect.Value) { t := v.Type() - switch { - case t.Implements(tyPointer): - ptr := v.Addr().Interface() - if a, ok := ptr.(data.Assignable); ok { - if ok := a.Assign(BytePtr(d.Pointer(), ApplicationPool)); !ok { - panic(fmt.Errorf("Could not assign to %T", ptr)) + + handlePointer := func() bool { + if t.Implements(tyPointer) { + ptr := v.Addr().Interface() + if a, ok := ptr.(data.Assignable); ok { + if ok := a.Assign(BytePtr(d.Pointer(), ApplicationPool)); !ok { + panic(fmt.Errorf("Could not assign to %T", ptr)) + } + } else { + panic(fmt.Errorf("Pointer type %T does not implement data.Assignable", ptr)) } - } else { - panic(fmt.Errorf("Pointer type %T does not implement data.Assignable", ptr)) + return true } - case t.Implements(tyCharTy): - deref(v).SetUint(uint64(d.Char())) - case t.Implements(tyIntTy): - deref(v).SetInt(int64(d.Int())) - case t.Implements(tyUintTy): - deref(v).SetUint(uint64(d.Uint())) - case t.Implements(tySizeTy): - deref(v).SetUint(uint64(d.Size())) - default: + return false + } - switch t.Kind() { - case reflect.Float32: - v.SetFloat(float64(d.F32())) - case reflect.Float64: - v.SetFloat(d.F64()) - case reflect.Int8: - v.SetInt(int64(d.I8())) - case reflect.Int16: - v.SetInt(int64(d.I16())) - case reflect.Int32: - v.SetInt(int64(d.I32())) - case reflect.Int64: + switch t.Kind() { + case reflect.Float32: + v.SetFloat(float64(d.F32())) + case reflect.Float64: + v.SetFloat(d.F64()) + case reflect.Int8: + v.SetInt(int64(d.I8())) + case reflect.Int16: + v.SetInt(int64(d.I16())) + case reflect.Int32: + v.SetInt(int64(d.I32())) + case reflect.Int64: + if t.Implements(tyIntTy) { + v.SetInt(int64(d.Int())) + } else { v.SetInt(d.I64()) - case reflect.Uint8: + } + case reflect.Uint8: + if t.Implements(tyCharTy) { + v.SetUint(uint64(d.Char())) + } else { v.SetUint(uint64(d.U8())) - case reflect.Uint16: - v.SetUint(uint64(d.U16())) - case reflect.Uint32: - v.SetUint(uint64(d.U32())) - case reflect.Uint64: - v.SetUint(d.U64()) - case reflect.Int: - v.SetInt(int64(d.Int())) - case reflect.Uint: + } + case reflect.Uint16: + v.SetUint(uint64(d.U16())) + case reflect.Uint32: + v.SetUint(uint64(d.U32())) + case reflect.Uint64: + if t.Implements(tySizeTy) { + v.SetUint(uint64(d.Size())) + } else if t.Implements(tyUintTy) { v.SetUint(uint64(d.Uint())) - case reflect.Array, reflect.Slice: - for i, c := 0, v.Len(); i < c; i++ { - decode(d, v.Index(i)) - } - case reflect.Struct: + } else { + v.SetUint(d.U64()) + } + case reflect.Int: + v.SetInt(int64(d.Int())) + case reflect.Uint: + v.SetUint(uint64(d.Uint())) + case reflect.Array, reflect.Slice: + for i, c := 0, v.Len(); i < c; i++ { + decode(d, v.Index(i)) + } + case reflect.Struct: + if !handlePointer() { d.Align(AlignOf(v.Type(), d.m)) base := d.o for i, c := 0, v.NumField(); i < c; i++ { @@ -99,14 +110,16 @@ func decode(d *Decoder, v reflect.Value) { read := d.o - base padding := SizeOf(v.Type(), d.m) - read d.Skip(padding) - case reflect.String: - v.SetString(d.String()) - case reflect.Bool: - v.SetBool(d.Bool()) - case reflect.Interface, reflect.Ptr: + } + case reflect.String: + v.SetString(d.String()) + case reflect.Bool: + v.SetBool(d.Bool()) + case reflect.Interface, reflect.Ptr: + if !handlePointer() { decode(d, v.Elem()) - default: - panic(fmt.Errorf("Cannot write type: %v", t)) } + default: + panic(fmt.Errorf("Cannot write type: %v", t)) } } diff --git a/gapis/memory/types.go b/gapis/memory/types.go index fabeed1dff..45dd0a890e 100644 --- a/gapis/memory/types.go +++ b/gapis/memory/types.go @@ -17,7 +17,7 @@ package memory import "reflect" var ( - tyPointer = reflect.TypeOf((*Pointer)(nil)).Elem() + tyPointer = reflect.TypeOf((*ReflectPointer)(nil)).Elem() tyCharTy = reflect.TypeOf((*CharTy)(nil)).Elem() tyIntTy = reflect.TypeOf((*IntTy)(nil)).Elem() tyUintTy = reflect.TypeOf((*UintTy)(nil)).Elem() diff --git a/gapis/memory/write.go b/gapis/memory/write.go index 427f1755ba..1d5144c925 100644 --- a/gapis/memory/write.go +++ b/gapis/memory/write.go @@ -28,48 +28,59 @@ func Write(e *Encoder, v interface{}) { func encode(e *Encoder, v reflect.Value) { t := v.Type() - switch { - case t.Implements(tyPointer): - e.Pointer(v.Interface().(Pointer).Address()) - case t.Implements(tyCharTy): - e.Char(Char(deref(v).Uint())) - case t.Implements(tyIntTy): - e.Int(Int(deref(v).Int())) - case t.Implements(tyUintTy): - e.Uint(Uint(deref(v).Uint())) - case t.Implements(tySizeTy): - e.Size(Size(deref(v).Uint())) - default: - switch t.Kind() { - case reflect.Float32: - e.F32(float32(v.Float())) - case reflect.Float64: - e.F64(v.Float()) - case reflect.Int8: - e.I8(int8(v.Int())) - case reflect.Int16: - e.I16(int16(v.Int())) - case reflect.Int32: - e.I32(int32(v.Int())) - case reflect.Int64: + handlePointer := func() bool { + if t.Implements(tyPointer) { + e.Pointer(v.Interface().(Pointer).Address()) + return true + } + return false + } + + switch t.Kind() { + case reflect.Float32: + e.F32(float32(v.Float())) + case reflect.Float64: + e.F64(v.Float()) + case reflect.Int8: + e.I8(int8(v.Int())) + case reflect.Int16: + e.I16(int16(v.Int())) + case reflect.Int32: + e.I32(int32(v.Int())) + case reflect.Int64: + if t.Implements(tyIntTy) { + e.Int(Int(v.Int())) + } else { e.I64(v.Int()) - case reflect.Uint8: + } + case reflect.Uint8: + if t.Implements(tyCharTy) { + e.Char(Char(v.Uint())) + } else { e.U8(uint8(v.Uint())) - case reflect.Uint16: - e.U16(uint16(v.Uint())) - case reflect.Uint32: - e.U32(uint32(v.Uint())) - case reflect.Uint64: - e.U64(v.Uint()) - case reflect.Int: - e.Int(Int(v.Int())) - case reflect.Uint: + } + case reflect.Uint16: + e.U16(uint16(v.Uint())) + case reflect.Uint32: + e.U32(uint32(v.Uint())) + case reflect.Uint64: + if t.Implements(tySizeTy) { + e.Size(Size(v.Uint())) + } else if t.Implements(tyUintTy) { e.Uint(Uint(v.Uint())) - case reflect.Array, reflect.Slice: - for i, c := 0, v.Len(); i < c; i++ { - encode(e, v.Index(i)) - } - case reflect.Struct: + } else { + e.U64(v.Uint()) + } + case reflect.Int: + e.Int(Int(v.Int())) + case reflect.Uint: + e.Uint(Uint(v.Uint())) + case reflect.Array, reflect.Slice: + for i, c := 0, v.Len(); i < c; i++ { + encode(e, v.Index(i)) + } + case reflect.Struct: + if !handlePointer() { e.Align(AlignOf(v.Type(), e.m)) base := e.o for i, c := 0, v.NumField(); i < c; i++ { @@ -78,14 +89,16 @@ func encode(e *Encoder, v reflect.Value) { written := e.o - base padding := SizeOf(v.Type(), e.m) - written e.Pad(padding) - case reflect.String: - e.String(v.String()) - case reflect.Bool: - e.Bool(v.Bool()) - case reflect.Interface, reflect.Ptr: + } + case reflect.String: + e.String(v.String()) + case reflect.Bool: + e.Bool(v.Bool()) + case reflect.Interface, reflect.Ptr: + if !handlePointer() { encode(e, v.Elem()) - default: - panic(fmt.Errorf("Cannot write type: %v", t)) } + default: + panic(fmt.Errorf("Cannot write type: %v", t)) } }