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 package main
import ( import (
"flag"
"gol/gol" "gol/gol"
"gol/term" "gol/term"
"log"
"os" "os"
"os/signal" "os/signal"
"runtime/pprof"
"syscall" "syscall"
"time" "time"
) )
func main() { 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) front := gol.New(20, 40)
back := gol.New(20, 40) back := gol.New(20, 40)
// draw initial objects
// glider // glider
front[0][3] = true front[0][3] = true
front[1][4] = true front[1][4] = true
@ -21,35 +46,40 @@ func main() {
front[2][4] = true front[2][4] = true
// cross // cross
front[16][21] = true front[16][21] = true
front[17][21] = true front[17][21] = true
front[18][21] = true front[18][21] = true
// catch interrupt signals
sigs := make(chan os.Signal, 1) 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.StartFullscreen()
term.TurnCursor(term.CursorOff) term.TurnCursor(term.CursorOff)
loop := func() { defer restoreScreen()
ticker := time.NewTicker(250 * time.Millisecond)
defer ticker.Stop() // setup timer
for { ticker := time.NewTicker(250 * time.Millisecond)
term.GoHome() defer ticker.Stop()
front.PrintMe()
front.NextGen(back) mainLoop:
front, back = back, front for {
select { term.GoHome()
case <-sigs: // przerwanie front.PrintMe()
return front.NextGen(back)
case <-ticker.C: // upłynął czas do kolejnej generacji front, back = back, front // switch arenas
} select {
case <-sigs: // interrupt (Ctrl-C etc)
break mainLoop
case <-ticker.C: // wait for next tick
} }
} }
defer func() { }
term.FinishFullscreen()
term.TurnCursor(term.CursorOn) // restoreScreen switch back terminal to normal mode.
}() func restoreScreen() {
loop() term.FinishFullscreen()
term.TurnCursor(term.CursorOn)
} }

View File

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

View File

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