From 0c44a7e266f5d6e5305ec348b98b07ec64f21612 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Fri, 7 Jun 2024 17:34:09 +0200 Subject: [PATCH] more performance tests --- various-tests/README.md | 9 ++ various-tests/perf-1dim/go.mod | 3 + various-tests/perf-1dim/main.go | 106 ++++++++++++++++++ various-tests/perf-2dim-pointers/go.mod | 3 + various-tests/perf-2dim-pointers/main.go | 137 +++++++++++++++++++++++ various-tests/perf-2dim/go.mod | 3 + various-tests/perf-2dim/main.go | 102 +++++++++++++++++ 7 files changed, 363 insertions(+) create mode 100644 various-tests/README.md create mode 100644 various-tests/perf-1dim/go.mod create mode 100644 various-tests/perf-1dim/main.go create mode 100644 various-tests/perf-2dim-pointers/go.mod create mode 100644 various-tests/perf-2dim-pointers/main.go create mode 100644 various-tests/perf-2dim/go.mod create mode 100644 various-tests/perf-2dim/main.go diff --git a/various-tests/README.md b/various-tests/README.md new file mode 100644 index 0000000..d84e404 --- /dev/null +++ b/various-tests/README.md @@ -0,0 +1,9 @@ +## Various performance tests + +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 | diff --git a/various-tests/perf-1dim/go.mod b/various-tests/perf-1dim/go.mod new file mode 100644 index 0000000..400edf2 --- /dev/null +++ b/various-tests/perf-1dim/go.mod @@ -0,0 +1,3 @@ +module perf + +go 1.22 diff --git a/various-tests/perf-1dim/main.go b/various-tests/perf-1dim/main.go new file mode 100644 index 0000000..a8a89b1 --- /dev/null +++ b/various-tests/perf-1dim/main.go @@ -0,0 +1,106 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "os" + "runtime/pprof" + "unsafe" +) + +const ( + dim int = 1500 + loops int = 5000 + density int = 8 + debug bool = false +) + +var max 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 Count(grid []bool, x, y int) int { + var sum int + + for nbgX := -1; nbgX < 2; nbgX++ { + for nbgY := -1; nbgY < 2; nbgY++ { + var col, row int + + if x+nbgX < 0 || x+nbgX >= dim || y+nbgY < 0 || y+nbgY >= dim { + continue + } + + col = x + nbgX + row = y + nbgY + + state := grid[row*col] + intstate := bool2int(state) + sum += intstate + } + } + + sum -= bool2int(grid[y*x]) + + return sum +} + +func Init() []bool { + max = dim * dim + + grid := make([]bool, max) + + for y := 0; y < dim; y++ { + for x := 0; x < dim; x++ { + if rand.Intn(density) == 1 { + grid[y*x] = true + } + } + } + + return grid +} + +func Loop(grid []bool) { + c := 0 + for i := 0; i < loops; i++ { + for y := 0; y < dim; y++ { + for x := 0; x < dim; x++ { + state := grid[y*x] + neighbors := Count(grid, x, y) + if state && neighbors > 1 { + if debug { + fmt.Printf("Loop %d - cell at %d,%d is %t and has %d living neighbors\n", i, x, y, state, neighbors) + } + 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 + Loop(grid) +} diff --git a/various-tests/perf-2dim-pointers/go.mod b/various-tests/perf-2dim-pointers/go.mod new file mode 100644 index 0000000..400edf2 --- /dev/null +++ b/various-tests/perf-2dim-pointers/go.mod @@ -0,0 +1,3 @@ +module perf + +go 1.22 diff --git a/various-tests/perf-2dim-pointers/main.go b/various-tests/perf-2dim-pointers/main.go new file mode 100644 index 0000000..7faee95 --- /dev/null +++ b/various-tests/perf-2dim-pointers/main.go @@ -0,0 +1,137 @@ +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 []*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{} + + 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 { + continue + } + + col = x + nbgX + row = y + nbgY + + if col == x && row == y { + continue + } + + cells = append(cells, &grid[row][col]) + } + } + + grid[y][x].Neighbors = make([]*Cell, len(cells)) + 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()) +} diff --git a/various-tests/perf-2dim/go.mod b/various-tests/perf-2dim/go.mod new file mode 100644 index 0000000..400edf2 --- /dev/null +++ b/various-tests/perf-2dim/go.mod @@ -0,0 +1,3 @@ +module perf + +go 1.22 diff --git a/various-tests/perf-2dim/main.go b/various-tests/perf-2dim/main.go new file mode 100644 index 0000000..2e7f82d --- /dev/null +++ b/various-tests/perf-2dim/main.go @@ -0,0 +1,102 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "os" + "runtime/pprof" + "unsafe" +) + +const ( + max int = 1500 + loops int = 5000 + density int = 8 + debug bool = false +) + +// https://dev.to/chigbeef_77/bool-int-but-stupid-in-go-3jb3 +func bool2int(b bool) int { + return int(*(*byte)(unsafe.Pointer(&b))) +} + +func Count(grid [][]bool, x, y int) int { + var sum int + + 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 { + continue + } + + col = x + nbgX + row = y + nbgY + + state := grid[row][col] + intstate := bool2int(state) + sum += intstate + } + } + + sum -= bool2int(grid[y][x]) + + return sum +} + +func Init() [][]bool { + grid := make([][]bool, max) + for y := 0; y < max; y++ { + grid[y] = make([]bool, max) + for x := 0; x < max; x++ { + if rand.Intn(density) == 1 { + grid[y][x] = true + } + } + } + + return grid +} + +func Loop(grid [][]bool) { + c := 0 + for i := 0; i < loops; i++ { + for y := 0; y < max; y++ { + for x := 0; x < max; x++ { + state := grid[y][x] + neighbors := Count(grid, x, y) + if state && neighbors > 1 { + if debug { + fmt.Printf("Loop %d - cell at %d,%d is %t and has %d living neighbors\n", i, x, y, state, neighbors) + } + 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 + Loop(grid) +}