From ecd634426989bf4611bb7fa616161b8358edaf3b Mon Sep 17 00:00:00 2001 From: sqshq Date: Sat, 23 Feb 2019 23:42:52 -0500 Subject: [PATCH] performance optimization for render rate --- console/console.go | 7 ++-- console/palette.go | 4 +-- data/sampler.go | 8 ++--- event/handler.go | 77 +++++++++++++++++++++++++++++++++++++------- main.go | 14 +++----- widgets/component.go | 11 ++++--- widgets/layout.go | 51 ++++++++++++++++------------- 7 files changed, 114 insertions(+), 58 deletions(-) diff --git a/console/console.go b/console/console.go index f12b201..e33cdff 100644 --- a/console/console.go +++ b/console/console.go @@ -8,15 +8,16 @@ import ( ) const ( - RenderRate = 100 * time.Millisecond - Title = "sampler" + MaxRenderInterval = 1000 * time.Millisecond + MinRenderInterval = 100 * time.Millisecond + AppTitle = "sampler" ) type Console struct{} func (self *Console) Init() { - fmt.Printf("\033]0;%s\007", Title) + fmt.Printf("\033]0;%s\007", AppTitle) if err := ui.Init(); err != nil { log.Fatalf("failed to initialize termui: %v", err) diff --git a/console/palette.go b/console/palette.go index c5d096a..7772cfb 100644 --- a/console/palette.go +++ b/console/palette.go @@ -33,9 +33,9 @@ type Palette struct { func GetPalette(theme Theme) Palette { switch theme { case ThemeDark: - return Palette{Colors: []ui.Color{ColorOlive, ColorDeepSkyBlue, ColorDeepPink, ColorWhite, ColorPurple, ColorCian, ColorOrange, ColorGreen}} + return Palette{Colors: []ui.Color{ColorOlive, ColorDeepSkyBlue, ColorDeepPink, ColorWhite, ColorGreen, ColorOrange, ColorCian, ColorPurple}} case ThemeLight: - return Palette{Colors: []ui.Color{ColorOlive, ColorDeepSkyBlue, ColorDeepPink, ColorWhite, ColorPurple, ColorCian, ColorOrange, ColorGreen}} + return Palette{Colors: []ui.Color{ColorOlive, ColorDeepSkyBlue, ColorDeepPink, ColorWhite, ColorGreen, ColorOrange, ColorCian, ColorPurple}} default: panic(fmt.Sprintf("Following theme is not supported: %v", theme)) } diff --git a/data/sampler.go b/data/sampler.go index 2407c16..3164c3b 100644 --- a/data/sampler.go +++ b/data/sampler.go @@ -24,8 +24,8 @@ func NewSampler(consumer Consumer, item Item, rateMs int) Sampler { return sampler } -func (self *Sampler) sample() { - value, err := self.item.nextValue() - sample := Sample{Value: value, Error: err, Label: *self.item.Label} - self.consumer.ConsumeSample(sample) +func (s *Sampler) sample() { + value, err := s.item.nextValue() + sample := Sample{Value: value, Error: err, Label: *s.item.Label} + s.consumer.ConsumeSample(sample) } diff --git a/event/handler.go b/event/handler.go index ed759d1..bcdaa2a 100644 --- a/event/handler.go +++ b/event/handler.go @@ -8,44 +8,97 @@ import ( "time" ) +const ( + refreshRateToRenderRateRatio = 0.8 +) + type Handler struct { - Layout *widgets.Layout - RenderEvents <-chan time.Time - ConsoleEvents <-chan ui.Event + layout *widgets.Layout + renderTicker *time.Ticker + consoleEvents <-chan ui.Event + renderRate time.Duration +} + +func NewHandler(layout *widgets.Layout) Handler { + renderRate := calcMinRenderRate(layout) + return Handler{ + layout: layout, + consoleEvents: ui.PollEvents(), + renderTicker: time.NewTicker(renderRate), + renderRate: renderRate, + } } func (h *Handler) HandleEvents() { + // initial render + ui.Render(h.layout) pause := false for { select { - case <-h.RenderEvents: - if !pause { - ui.Render(h.Layout) - } - case e := <-h.ConsoleEvents: + case mode := <-h.layout.ChangeModeEvents: + h.handleModeChange(mode) + case <-h.renderTicker.C: + ui.Render(h.layout) + case e := <-h.consoleEvents: switch e.ID { case console.KeyQuit, console.KeyExit: h.handleExit() return case console.KeyPause: - pause = !pause + pause = !pause // TODO move to layout, since it will show PAUSE sign in status line, pause/unpause can be sent via channel case console.SignalResize: payload := e.Payload.(ui.Resize) - h.Layout.ChangeDimensions(payload.Width, payload.Height) + h.layout.ChangeDimensions(payload.Width, payload.Height) default: - h.Layout.HandleConsoleEvent(e.ID) + h.layout.HandleConsoleEvent(e.ID) } } } } +func (h *Handler) handleModeChange(m widgets.Mode) { + + // render mode change before switching the tickers + ui.Render(h.layout) + h.renderTicker.Stop() + + if m == widgets.ModeDefault { + h.renderTicker = time.NewTicker(h.renderRate) + } else { + h.renderTicker = time.NewTicker(console.MinRenderInterval) + } +} + func (h *Handler) handleExit() { var settings []config.ComponentSettings - for _, c := range h.Layout.Components { + for _, c := range h.layout.Components { settings = append(settings, config.ComponentSettings{Type: c.Type, Title: c.Title, Size: c.Size, Position: c.Position}) } config.Update(settings) } + +func calcMinRenderRate(layout *widgets.Layout) time.Duration { + + minRefreshRateMs := layout.Components[0].RefreshRateMs + for _, c := range layout.Components { + if c.RefreshRateMs < minRefreshRateMs { + minRefreshRateMs = c.RefreshRateMs + } + } + + renderRate := time.Duration( + int(float64(minRefreshRateMs)*refreshRateToRenderRateRatio)) * time.Millisecond + + if renderRate < console.MinRenderInterval { + return console.MinRenderInterval + } + + if renderRate > console.MaxRenderInterval { + return console.MaxRenderInterval + } + + return renderRate +} diff --git a/main.go b/main.go index 4a20d70..6bc75dc 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,6 @@ import ( "github.com/sqshq/sampler/widgets/barchart" "github.com/sqshq/sampler/widgets/runchart" ui "github.com/sqshq/termui" - "time" ) func main() { @@ -27,7 +26,7 @@ func main() { legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details} chart := runchart.NewRunChart(c.Title, *c.Scale, *c.RefreshRateMs, legend) - layout.AddComponent(chart, c.Title, c.Position, c.Size, config.TypeRunChart) + layout.AddComponent(config.TypeRunChart, chart, c.Title, c.Position, c.Size, *c.RefreshRateMs) for _, item := range c.Items { chart.AddLine(*item.Label, *item.Color) @@ -37,14 +36,14 @@ func main() { for _, a := range cfg.AsciiBoxes { box := asciibox.NewAsciiBox(a.Title, *a.Font, *a.Item.Color) - layout.AddComponent(box, a.Title, a.Position, a.Size, config.TypeAsciiBox) + layout.AddComponent(config.TypeAsciiBox, box, a.Title, a.Position, a.Size, *a.RefreshRateMs) data.NewSampler(box, a.Item, *a.RefreshRateMs) } for _, c := range cfg.BarCharts { chart := barchart.NewBarChart(c.Title, *c.Scale) - layout.AddComponent(chart, c.Title, c.Position, c.Size, config.TypeBarChart) + layout.AddComponent(config.TypeBarChart, chart, c.Title, c.Position, c.Size, *c.RefreshRateMs) for _, item := range c.Items { chart.AddBar(*item.Label, *item.Color) @@ -52,11 +51,6 @@ func main() { } } - handler := event.Handler{ - Layout: layout, - RenderEvents: time.NewTicker(console.RenderRate).C, - ConsoleEvents: ui.PollEvents(), - } - + handler := event.NewHandler(layout) handler.HandleEvents() } diff --git a/widgets/component.go b/widgets/component.go index 9980600..38d707e 100644 --- a/widgets/component.go +++ b/widgets/component.go @@ -6,11 +6,12 @@ import ( ) type Component struct { - Drawable ui.Drawable - Title string - Position config.Position - Size config.Size - Type config.ComponentType + Type config.ComponentType + Drawable ui.Drawable + Title string + Position config.Position + Size config.Size + RefreshRateMs int } func (c *Component) Move(x, y int) { diff --git a/widgets/layout.go b/widgets/layout.go index 515f8ab..cad219e 100644 --- a/widgets/layout.go +++ b/widgets/layout.go @@ -9,10 +9,11 @@ import ( type Layout struct { ui.Block - Components []Component - menu *Menu - mode Mode - selection int + Components []Component + ChangeModeEvents chan Mode + mode Mode + menu *Menu + selection int } type Mode rune @@ -37,16 +38,17 @@ func NewLayout(width, height int, menu *Menu) *Layout { block.SetRect(0, 0, width, height) return &Layout{ - Block: block, - Components: make([]Component, 0), - menu: menu, - mode: ModeDefault, - selection: 0, + Block: block, + Components: make([]Component, 0), + menu: menu, + mode: ModeDefault, + selection: 0, + ChangeModeEvents: make(chan Mode, 10), } } -func (l *Layout) AddComponent(drawable ui.Drawable, title string, position config.Position, size config.Size, Type config.ComponentType) { - l.Components = append(l.Components, Component{drawable, title, position, size, Type}) +func (l *Layout) AddComponent(Type config.ComponentType, drawable ui.Drawable, title string, position config.Position, size config.Size, refreshRateMs int) { + l.Components = append(l.Components, Component{Type, drawable, title, position, size, refreshRateMs}) } func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable { @@ -62,36 +64,41 @@ func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable { return components } +func (l *Layout) changeMode(m Mode) { + l.mode = m + l.ChangeModeEvents <- m +} + func (l *Layout) HandleConsoleEvent(e string) { switch e { case console.KeyEnter: switch l.mode { case ModeComponentSelect: l.menu.choose() - l.mode = ModeMenuOptionSelect + l.changeMode(ModeMenuOptionSelect) case ModeMenuOptionSelect: option := l.menu.getSelectedOption() switch option { case MenuOptionMove: - l.mode = ModeComponentMove + l.changeMode(ModeComponentMove) l.menu.moveOrResize() case MenuOptionResize: - l.mode = ModeComponentResize + l.changeMode(ModeComponentResize) l.menu.moveOrResize() case MenuOptionPinpoint: - l.mode = ModeChartPinpoint + l.changeMode(ModeChartPinpoint) l.menu.idle() chart := l.getSelectedComponent().Drawable.(*runchart.RunChart) chart.MoveSelection(0) case MenuOptionResume: - l.mode = ModeDefault + l.changeMode(ModeDefault) l.menu.idle() } case ModeComponentMove: fallthrough case ModeComponentResize: l.menu.idle() - l.mode = ModeDefault + l.changeMode(ModeDefault) } case console.KeyEsc: switch l.mode { @@ -103,12 +110,12 @@ func (l *Layout) HandleConsoleEvent(e string) { fallthrough case ModeMenuOptionSelect: l.menu.idle() - l.mode = ModeDefault + l.changeMode(ModeDefault) } case console.KeyLeft: switch l.mode { case ModeDefault: - l.mode = ModeComponentSelect + l.changeMode(ModeComponentSelect) l.selection = 0 l.menu.highlight(l.getComponent(l.selection)) case ModeChartPinpoint: @@ -127,7 +134,7 @@ func (l *Layout) HandleConsoleEvent(e string) { case console.KeyRight: switch l.mode { case ModeDefault: - l.mode = ModeComponentSelect + l.changeMode(ModeComponentSelect) l.selection = 0 l.menu.highlight(l.getComponent(l.selection)) case ModeChartPinpoint: @@ -146,7 +153,7 @@ func (l *Layout) HandleConsoleEvent(e string) { case console.KeyUp: switch l.mode { case ModeDefault: - l.mode = ModeComponentSelect + l.changeMode(ModeComponentSelect) l.selection = 0 l.menu.highlight(l.getComponent(l.selection)) case ModeComponentSelect: @@ -164,7 +171,7 @@ func (l *Layout) HandleConsoleEvent(e string) { case console.KeyDown: switch l.mode { case ModeDefault: - l.mode = ModeComponentSelect + l.changeMode(ModeComponentSelect) l.selection = 0 l.menu.highlight(l.getComponent(l.selection)) case ModeComponentSelect: