Skip to content

Commit

Permalink
Reworked memory functions to rely less on reflection.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
AWoloszyn committed Jan 6, 2018
1 parent f69c791 commit 13a00a4
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 175 deletions.
3 changes: 3 additions & 0 deletions gapis/api/templates/api.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down
1 change: 1 addition & 0 deletions gapis/api/testcmd/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -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") }
Expand Down
179 changes: 102 additions & 77 deletions gapis/memory/alignof_sizeof.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
}
}
12 changes: 12 additions & 0 deletions gapis/memory/pointer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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}
Expand All @@ -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))
Expand Down
115 changes: 64 additions & 51 deletions gapis/memory/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -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++ {
Expand All @@ -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))
}
}
2 changes: 1 addition & 1 deletion gapis/memory/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading

0 comments on commit 13a00a4

Please sign in to comment.