perf: display optimization

This commit is contained in:
Arek 2025-02-11 22:25:57 +01:00
parent 3d57868e9d
commit 11781c0789
3 changed files with 84 additions and 36 deletions

View File

@ -1,18 +1,43 @@
package main
import (
"flag"
"gol/gol"
"gol/term"
"log"
"os"
"os/signal"
"runtime/pprof"
"syscall"
"time"
)
func main() {
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
flag.Parse()
// Jeśli podano flagę cpuprofile, rozpocznij profilowanie
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
game()
}
func game() {
// arenas - front one is printent, then new state is calculated into back one
// next front and back are switched
front := gol.New(20, 40)
back := gol.New(20, 40)
// draw initial objects
// glider
front[0][3] = true
front[1][4] = true
@ -21,35 +46,40 @@ func main() {
front[2][4] = true
// cross
front[16][21] = true
front[17][21] = true
front[18][21] = true
// catch interrupt signals
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
// initialize screen
term.StartFullscreen()
term.TurnCursor(term.CursorOff)
loop := func() {
defer restoreScreen()
// setup timer
ticker := time.NewTicker(250 * time.Millisecond)
defer ticker.Stop()
mainLoop:
for {
term.GoHome()
front.PrintMe()
front.NextGen(back)
front, back = back, front
front, back = back, front // switch arenas
select {
case <-sigs: // przerwanie
return
case <-ticker.C: // upłynął czas do kolejnej generacji
case <-sigs: // interrupt (Ctrl-C etc)
break mainLoop
case <-ticker.C: // wait for next tick
}
}
}
defer func() {
// restoreScreen switch back terminal to normal mode.
func restoreScreen() {
term.FinishFullscreen()
term.TurnCursor(term.CursorOn)
}()
loop()
}

View File

@ -1,6 +1,10 @@
package gol
import "fmt"
import (
"bytes"
"os"
"sync"
)
type Arena [][]bool
@ -12,18 +16,31 @@ func New(ysize, xsize int) Arena {
return arena
}
// bufPool
var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
func (a Arena) PrintMe() {
b := bufPool.Get().(*bytes.Buffer)
b.Reset()
spriteOn := ([]byte)("\ue0b6\ue0b4")
spriteOff := ([]byte)("\u2022\u2022")
for i := 0; i < len(a); i++ {
for j := 0; j < len(a[0]); j++ {
if a[i][j] {
fmt.Print("\ue0b6\ue0b4")
b.Write(spriteOn)
} else {
//fmt.Print("\ue0b7\ue0b5")
fmt.Print("\u2022\u2022")
b.Write(spriteOff)
}
}
fmt.Println()
b.WriteRune('\n')
}
os.Stdout.Write(b.Bytes())
bufPool.Put(b)
}
func (a Arena) NextGen(to Arena) {

View File

@ -13,47 +13,48 @@ const (
// TurnCursor turns the cursor on and off according to the parameter.
func TurnCursor(onOff CursorFlag) {
if onOff == CursorOn {
fmt.Printf("\033[?25h")
} else {
fmt.Printf("\033[?25l")
switch onOff {
case CursorOn:
fmt.Print("\033[?25h")
case CursorOff:
fmt.Print("\033[?25l")
}
}
// ClearScreen clears current screen.
func ClearScreen() {
fmt.Printf("\033[2J")
fmt.Print("\033[2J")
}
// ResetTerminal resets terminal (ESC c)
func ResetTerminal() {
fmt.Printf("\033c")
fmt.Print("\033c")
}
// GoHome moves cursor to the home position (upper left corner)
func GoHome() {
fmt.Printf("\033[H")
fmt.Print("\033[H")
}
// SaveCursor saves curent position of the SaveCursor
func SaveCursor() {
fmt.Printf("\033[s")
fmt.Print("\033[s")
}
// RestoreCursor restore cursor to the position saved previously with SaveCursor().
func RestoreCursor() {
fmt.Printf("\033[u")
fmt.Print("\033[u")
}
// EnableAlternateScreen switches the terminal to the alternative screen.
// The alternative screen doesn't have the scroll buffer.
func EnableAlternateScreen() {
fmt.Printf("\033[?1049h")
fmt.Print("\033[?1049h")
}
// DiableAlternateScreen switches the terminal to the primary screen.
func DisableAlternateScreen() {
fmt.Printf("\033[?1049l")
fmt.Print("\033[?1049l")
}
// StartFullscreen saves the cursor, enables alternate screen and clears it.