make container complicated, heap profiles

This commit is contained in:
Josh Deprez 2021-09-23 14:54:45 +10:00
parent 4dba7b22c7
commit 8dc7583884
4 changed files with 105 additions and 28 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.DS_Store .DS_Store
cpuprofile.pprof cpuprofile.pprof
heapprofile.pprof

View file

@ -6,11 +6,35 @@ var _ interface {
} = &Container{} } = &Container{}
// Container contains many components, in order. // Container contains many components, in order.
type Container []interface{} type Container struct {
Items []interface{}
free map[int]struct{}
reverse map[interface{}]int
}
func MakeContainer(items ...interface{}) *Container {
c := &Container{Items: items}
c.Prepare(nil)
return c
}
func (c *Container) Prepare(*Game) error {
if c.reverse == nil {
c.reverse = make(map[interface{}]int, len(c.Items))
for i, x := range c.Items {
c.reverse[x] = i
}
}
if c.free == nil {
c.free = make(map[int]struct{})
}
return nil
}
// Scan visits every component in the container. // Scan visits every component in the container.
func (c Container) Scan(visit func(interface{}) error) error { func (c *Container) Scan(visit func(interface{}) error) error {
for _, x := range c { for _, x := range c.Items {
if err := visit(x); err != nil { if err := visit(x); err != nil {
return err return err
} }
@ -18,12 +42,55 @@ func (c Container) Scan(visit func(interface{}) error) error {
return nil return nil
} }
// Len returns the number of items in the container.
func (c *Container) Len() int { return len(c.Items) }
// Swap swaps two items in the container.
func (c *Container) Swap(i, j int) {
if i == j {
return
}
ifree := c.Items[i] == nil
jfree := c.Items[j] == nil
switch {
case ifree && jfree:
return
case ifree:
c.Items[i] = c.Items[j]
c.reverse[c.Items[i]] = i
c.free[j] = struct{}{}
delete(c.free, i)
case jfree:
c.Items[j] = c.Items[i]
c.reverse[c.Items[j]] = j
c.free[i] = struct{}{}
delete(c.free, j)
default:
c.Items[i], c.Items[j] = c.Items[j], c.Items[i]
c.reverse[c.Items[i]] = i
c.reverse[c.Items[j]] = j
}
}
func (c Container) String() string { return "Container" } func (c Container) String() string { return "Container" }
// Register records component in the slice, if parent is the container. // Register records component in the slice, if parent is this container. It
// writes the component to an arbitrary free index in the slice, or appends if
// there are none free.
func (c *Container) Register(component, parent interface{}) error { func (c *Container) Register(component, parent interface{}) error {
if parent == c { if parent != c {
*c = append(*c, component) return nil
}
if len(c.free) == 0 {
c.reverse[component] = len(c.Items)
c.Items = append(c.Items, component)
return nil
}
for i := range c.free {
c.reverse[component] = i
c.Items[i] = component
delete(c.free, i)
return nil
} }
return nil return nil
} }
@ -32,28 +99,25 @@ func (c *Container) Register(component, parent interface{}) error {
// to nil. If the number of nil items is greater than half the slice, the slice // to nil. If the number of nil items is greater than half the slice, the slice
// is compacted. // is compacted.
func (c *Container) Unregister(component interface{}) { func (c *Container) Unregister(component interface{}) {
free := 0 if i, found := c.reverse[component]; found {
for i, x := range *c { c.Items[i] = nil
switch x { c.free[i] = struct{}{}
case component: delete(c.reverse, i)
(*c)[i] = nil
free++
case nil:
free++
}
} }
if free > len(*c)/2 { if len(c.free) > len(c.Items)/2 {
c.compact() c.compact()
} }
} }
func (c *Container) compact() { func (c *Container) compact() {
i := 0 i := 0
for _, x := range *c { for _, x := range c.Items {
if x != nil { if x != nil {
(*c)[i] = x c.Items[i] = x
c.reverse[x] = i
i++ i++
} }
} }
*c = (*c)[:i] c.Items = c.Items[:i]
c.free = make(map[int]struct{})
} }

View file

@ -12,7 +12,7 @@ func Level1() *engine.Scene {
return &engine.Scene{ return &engine.Scene{
ID: "level_1", ID: "level_1",
Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)), Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)),
Child: &engine.Container{ Child: engine.MakeContainer(
&engine.Parallax{ &engine.Parallax{
CameraID: "game_camera", CameraID: "game_camera",
Child: &engine.Billboard{ Child: &engine.Billboard{
@ -310,6 +310,6 @@ func Level1() *engine.Scene {
}, },
}, },
}, },
}, ),
} }
} }

24
main.go
View file

@ -17,10 +17,11 @@ import (
) )
const ( const (
enableCPUProfile = true enableCPUProfile = true
enableREPL = true enableHeapProfile = true
hardcodedLevel1 = true enableREPL = true
rewriteLevel1 = false hardcodedLevel1 = true
rewriteLevel1 = false
) )
func main() { func main() {
@ -64,7 +65,7 @@ func main() {
Z: math.Sqrt(3), Z: math.Sqrt(3),
}, },
Root: &engine.DrawDFS{ Root: &engine.DrawDFS{
Child: &engine.Container{ Child: engine.MakeContainer(
&engine.Fill{ &engine.Fill{
ID: "bg_fill", ID: "bg_fill",
Color: color.Gray{100}, Color: color.Gray{100},
@ -78,7 +79,7 @@ func main() {
}, },
&engine.DebugToast{ID: "toast", Pos: image.Pt(0, 15)}, &engine.DebugToast{ID: "toast", Pos: image.Pt(0, 15)},
engine.PerfDisplay{}, engine.PerfDisplay{},
}, ),
}, },
} }
if err := g.LoadAndPrepare(game.Assets); err != nil { if err := g.LoadAndPrepare(game.Assets); err != nil {
@ -92,4 +93,15 @@ func main() {
if err := ebiten.RunGame(g); err != nil { if err := ebiten.RunGame(g); err != nil {
log.Fatalf("Game error: %v", err) log.Fatalf("Game error: %v", err)
} }
if enableHeapProfile && runtime.GOOS != "js" {
f, err := os.Create("heapprofile.pprof")
if err != nil {
log.Fatal("could not create heap profile: ", err)
}
defer f.Close()
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write heap profile: ", err)
}
}
} }