diff --git a/various-tests/README.md b/various-tests/README.md index d84e404..c5490af 100644 --- a/various-tests/README.md +++ b/various-tests/README.md @@ -2,8 +2,10 @@ Running with 1500x1500 grid 5k times -| Variation | Description | Duration | -|--------------------|-----------------------------------------------------------------------------|----------| -| perf-2dim | uses 2d grid of bools, no tuning | 00:03:14 | -| perf-2dim-pointers | use 2d grid of `Cell{Neighbors,NeighborCount}`s using pointers to neighbors | 00:03:35 | -| perf-1dim | use 1d grid of bools, access using y*x, no further tuning | 00:03:24 | +| Variation | Description | Duration | +|------------------------------|-----------------------------------------------------------------------------|-------------------| +| perf-2dim | uses 2d grid of bools, no tuning | 00:03:14 | +| perf-2dim-pointers | use 2d grid of `Cell{Neighbors,NeighborCount}`s using pointers to neighbors | 00:03:35/00:04:75 | +| perf-2dim-pointers-array | same as above but array of neighbors instead of slice | 00:02:40 | +| perf-2dim-pointers-all-array | use arrays for everything, static 1500x1500 | infinite, aborted | +| perf-1dim | use 1d grid of bools, access using y*x, no further tuning | 00:03:24 | diff --git a/various-tests/perf-2dim-pointers-all-array/go.mod b/various-tests/perf-2dim-pointers-all-array/go.mod new file mode 100644 index 0000000..400edf2 --- /dev/null +++ b/various-tests/perf-2dim-pointers-all-array/go.mod @@ -0,0 +1,3 @@ +module perf + +go 1.22 diff --git a/various-tests/perf-2dim-pointers-all-array/main.go b/various-tests/perf-2dim-pointers-all-array/main.go new file mode 100644 index 0000000..7bcb7fc --- /dev/null +++ b/various-tests/perf-2dim-pointers-all-array/main.go @@ -0,0 +1,141 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "os" + "runtime/pprof" + "time" + "unsafe" +) + +const ( + max int = 1500 + loops int = 5000 + density int = 8 + debug bool = false +) + +type Cell struct { + State bool + Neighbors [8]*Cell + NeighborCount int +} + +type Grid [1500][1500]Cell + +// https://dev.to/chigbeef_77/bool-int-but-stupid-in-go-3jb3 +func bool2int(b bool) int { + return int(*(*byte)(unsafe.Pointer(&b))) +} + +func (cell *Cell) Count(x, y int) { + cell.NeighborCount = 0 + + for _, neighbor := range cell.Neighbors { + cell.NeighborCount += bool2int(neighbor.State) + } +} + +func SetNeighbors(grid Grid, x, y int) { + cells := []*Cell{} + deadcell := &Cell{} + + for nbgX := -1; nbgX < 2; nbgX++ { + for nbgY := -1; nbgY < 2; nbgY++ { + var col, row int + + if x+nbgX < 0 || x+nbgX >= max || y+nbgY < 0 || y+nbgY >= max { + cells = append(cells, deadcell) + } else { + + col = x + nbgX + row = y + nbgY + + if col == x && row == y { + // do not add self + continue + } + + cells = append(cells, &grid[row][col]) + } + } + } + + for idx, cell := range cells { + grid[y][x].Neighbors[idx] = cell + } +} + +func Init() Grid { + grid := Grid{} + + for y := 0; y < max; y++ { + for x := 0; x < max; x++ { + if rand.Intn(density) == 1 { + grid[y][x].State = true + } + } + } + + for y := 0; y < max; y++ { + for x := 0; x < max; x++ { + SetNeighbors(grid, x, y) + } + } + + return grid +} + +func Loop(grid Grid) { + c := 0 + for i := 0; i < loops; i++ { + for y := 0; y < max; y++ { + for x := 0; x < max; x++ { + cell := &grid[y][x] + state := cell.State + + cell.Count(x, y) + + if state && cell.NeighborCount > 1 { + if debug { + fmt.Printf( + "Loop %d - cell at %d,%d is %t and has %d living neighbors\n", + i, x, y, state, cell.NeighborCount) + } + c = 1 + } + } + } + } + + if c > 1 { + c = 0 + } +} + +func main() { + // enable cpu profiling. Do NOT use q to stop the game but + // close the window to get a profile + fd, err := os.Create("cpu.profile") + if err != nil { + log.Fatal(err) + } + defer fd.Close() + + pprof.StartCPUProfile(fd) + defer pprof.StopCPUProfile() + + // init + grid := Init() + + // main loop + loopstart := time.Now() + + Loop(grid) + + loopend := time.Now() + diff := loopstart.Sub(loopend) + fmt.Printf("Loop took %.04f\n", diff.Seconds()) +} diff --git a/various-tests/perf-2dim-pointers-array/go.mod b/various-tests/perf-2dim-pointers-array/go.mod new file mode 100644 index 0000000..400edf2 --- /dev/null +++ b/various-tests/perf-2dim-pointers-array/go.mod @@ -0,0 +1,3 @@ +module perf + +go 1.22 diff --git a/various-tests/perf-2dim-pointers-array/main.go b/various-tests/perf-2dim-pointers-array/main.go new file mode 100644 index 0000000..b03eac8 --- /dev/null +++ b/various-tests/perf-2dim-pointers-array/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "os" + "runtime/pprof" + "time" + "unsafe" +) + +const ( + max int = 1500 + loops int = 5000 + density int = 8 + debug bool = false +) + +type Cell struct { + State bool + Neighbors [8]*Cell + NeighborCount int +} + +// https://dev.to/chigbeef_77/bool-int-but-stupid-in-go-3jb3 +func bool2int(b bool) int { + return int(*(*byte)(unsafe.Pointer(&b))) +} + +func (cell *Cell) Count(x, y int) { + cell.NeighborCount = 0 + + for _, neighbor := range cell.Neighbors { + cell.NeighborCount += bool2int(neighbor.State) + } +} + +func SetNeighbors(grid [][]Cell, x, y int) { + cells := []*Cell{} + deadcell := &Cell{} + + for nbgX := -1; nbgX < 2; nbgX++ { + for nbgY := -1; nbgY < 2; nbgY++ { + var col, row int + + if x+nbgX < 0 || x+nbgX >= max || y+nbgY < 0 || y+nbgY >= max { + cells = append(cells, deadcell) + } else { + + col = x + nbgX + row = y + nbgY + + if col == x && row == y { + // do not add self + continue + } + + cells = append(cells, &grid[row][col]) + } + } + } + + for idx, cell := range cells { + grid[y][x].Neighbors[idx] = cell + } +} + +func Init() [][]Cell { + grid := make([][]Cell, max) + for y := 0; y < max; y++ { + grid[y] = make([]Cell, max) + for x := 0; x < max; x++ { + if rand.Intn(density) == 1 { + grid[y][x].State = true + } + } + } + + for y := 0; y < max; y++ { + for x := 0; x < max; x++ { + SetNeighbors(grid, x, y) + } + } + + return grid +} + +func Loop(grid [][]Cell) { + c := 0 + for i := 0; i < loops; i++ { + for y := 0; y < max; y++ { + for x := 0; x < max; x++ { + cell := &grid[y][x] + state := cell.State + + cell.Count(x, y) + + if state && cell.NeighborCount > 1 { + if debug { + fmt.Printf( + "Loop %d - cell at %d,%d is %t and has %d living neighbors\n", + i, x, y, state, cell.NeighborCount) + } + c = 1 + } + } + } + } + + if c > 1 { + c = 0 + } +} + +func main() { + // enable cpu profiling. Do NOT use q to stop the game but + // close the window to get a profile + fd, err := os.Create("cpu.profile") + if err != nil { + log.Fatal(err) + } + defer fd.Close() + + pprof.StartCPUProfile(fd) + defer pprof.StopCPUProfile() + + // init + grid := Init() + + // main loop + loopstart := time.Now() + + Loop(grid) + + loopend := time.Now() + diff := loopstart.Sub(loopend) + fmt.Printf("Loop took %.04f\n", diff.Seconds()) +}