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
cpuprofile.pprof
heapprofile.pprof

View file

@ -6,11 +6,35 @@ var _ interface {
} = &Container{}
// 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.
func (c Container) Scan(visit func(interface{}) error) error {
for _, x := range c {
func (c *Container) Scan(visit func(interface{}) error) error {
for _, x := range c.Items {
if err := visit(x); err != nil {
return err
}
@ -18,12 +42,55 @@ func (c Container) Scan(visit func(interface{}) error) error {
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" }
// 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 {
if parent == c {
*c = append(*c, component)
if parent != c {
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
}
@ -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
// is compacted.
func (c *Container) Unregister(component interface{}) {
free := 0
for i, x := range *c {
switch x {
case component:
(*c)[i] = nil
free++
case nil:
free++
if i, found := c.reverse[component]; found {
c.Items[i] = nil
c.free[i] = struct{}{}
delete(c.reverse, i)
}
}
if free > len(*c)/2 {
if len(c.free) > len(c.Items)/2 {
c.compact()
}
}
func (c *Container) compact() {
i := 0
for _, x := range *c {
for _, x := range c.Items {
if x != nil {
(*c)[i] = x
c.Items[i] = x
c.reverse[x] = 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{
ID: "level_1",
Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)),
Child: &engine.Container{
Child: engine.MakeContainer(
&engine.Parallax{
CameraID: "game_camera",
Child: &engine.Billboard{
@ -310,6 +310,6 @@ func Level1() *engine.Scene {
},
},
},
},
),
}
}

16
main.go
View file

@ -18,6 +18,7 @@ import (
const (
enableCPUProfile = true
enableHeapProfile = true
enableREPL = true
hardcodedLevel1 = true
rewriteLevel1 = false
@ -64,7 +65,7 @@ func main() {
Z: math.Sqrt(3),
},
Root: &engine.DrawDFS{
Child: &engine.Container{
Child: engine.MakeContainer(
&engine.Fill{
ID: "bg_fill",
Color: color.Gray{100},
@ -78,7 +79,7 @@ func main() {
},
&engine.DebugToast{ID: "toast", Pos: image.Pt(0, 15)},
engine.PerfDisplay{},
},
),
},
}
if err := g.LoadAndPrepare(game.Assets); err != nil {
@ -92,4 +93,15 @@ func main() {
if err := ebiten.RunGame(g); err != nil {
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)
}
}
}