make container complicated, heap profiles
This commit is contained in:
parent
4dba7b22c7
commit
8dc7583884
4 changed files with 105 additions and 28 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
cpuprofile.pprof
|
cpuprofile.pprof
|
||||||
|
heapprofile.pprof
|
||||||
|
|
|
@ -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{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
24
main.go
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue