performance optimization for render rate

This commit is contained in:
sqshq 2019-02-23 23:42:52 -05:00
parent 6e27c2e5a8
commit ecd6344269
7 changed files with 114 additions and 58 deletions

View File

@ -8,15 +8,16 @@ import (
) )
const ( const (
RenderRate = 100 * time.Millisecond MaxRenderInterval = 1000 * time.Millisecond
Title = "sampler" MinRenderInterval = 100 * time.Millisecond
AppTitle = "sampler"
) )
type Console struct{} type Console struct{}
func (self *Console) Init() { func (self *Console) Init() {
fmt.Printf("\033]0;%s\007", Title) fmt.Printf("\033]0;%s\007", AppTitle)
if err := ui.Init(); err != nil { if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err) log.Fatalf("failed to initialize termui: %v", err)

View File

@ -33,9 +33,9 @@ type Palette struct {
func GetPalette(theme Theme) Palette { func GetPalette(theme Theme) Palette {
switch theme { switch theme {
case ThemeDark: 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: 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: default:
panic(fmt.Sprintf("Following theme is not supported: %v", theme)) panic(fmt.Sprintf("Following theme is not supported: %v", theme))
} }

View File

@ -24,8 +24,8 @@ func NewSampler(consumer Consumer, item Item, rateMs int) Sampler {
return sampler return sampler
} }
func (self *Sampler) sample() { func (s *Sampler) sample() {
value, err := self.item.nextValue() value, err := s.item.nextValue()
sample := Sample{Value: value, Error: err, Label: *self.item.Label} sample := Sample{Value: value, Error: err, Label: *s.item.Label}
self.consumer.ConsumeSample(sample) s.consumer.ConsumeSample(sample)
} }

View File

@ -8,44 +8,97 @@ import (
"time" "time"
) )
const (
refreshRateToRenderRateRatio = 0.8
)
type Handler struct { type Handler struct {
Layout *widgets.Layout layout *widgets.Layout
RenderEvents <-chan time.Time renderTicker *time.Ticker
ConsoleEvents <-chan ui.Event 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() { func (h *Handler) HandleEvents() {
// initial render
ui.Render(h.layout)
pause := false pause := false
for { for {
select { select {
case <-h.RenderEvents: case mode := <-h.layout.ChangeModeEvents:
if !pause { h.handleModeChange(mode)
ui.Render(h.Layout) case <-h.renderTicker.C:
} ui.Render(h.layout)
case e := <-h.ConsoleEvents: case e := <-h.consoleEvents:
switch e.ID { switch e.ID {
case console.KeyQuit, console.KeyExit: case console.KeyQuit, console.KeyExit:
h.handleExit() h.handleExit()
return return
case console.KeyPause: 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: case console.SignalResize:
payload := e.Payload.(ui.Resize) payload := e.Payload.(ui.Resize)
h.Layout.ChangeDimensions(payload.Width, payload.Height) h.layout.ChangeDimensions(payload.Width, payload.Height)
default: 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() { func (h *Handler) handleExit() {
var settings []config.ComponentSettings var settings []config.ComponentSettings
for _, c := range h.Layout.Components { for _, c := range h.layout.Components {
settings = append(settings, settings = append(settings,
config.ComponentSettings{Type: c.Type, Title: c.Title, Size: c.Size, Position: c.Position}) config.ComponentSettings{Type: c.Type, Title: c.Title, Size: c.Size, Position: c.Position})
} }
config.Update(settings) 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
}

14
main.go
View File

@ -10,7 +10,6 @@ import (
"github.com/sqshq/sampler/widgets/barchart" "github.com/sqshq/sampler/widgets/barchart"
"github.com/sqshq/sampler/widgets/runchart" "github.com/sqshq/sampler/widgets/runchart"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
"time"
) )
func main() { func main() {
@ -27,7 +26,7 @@ func main() {
legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details} legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details}
chart := runchart.NewRunChart(c.Title, *c.Scale, *c.RefreshRateMs, legend) 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 { for _, item := range c.Items {
chart.AddLine(*item.Label, *item.Color) chart.AddLine(*item.Label, *item.Color)
@ -37,14 +36,14 @@ func main() {
for _, a := range cfg.AsciiBoxes { for _, a := range cfg.AsciiBoxes {
box := asciibox.NewAsciiBox(a.Title, *a.Font, *a.Item.Color) 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) data.NewSampler(box, a.Item, *a.RefreshRateMs)
} }
for _, c := range cfg.BarCharts { for _, c := range cfg.BarCharts {
chart := barchart.NewBarChart(c.Title, *c.Scale) 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 { for _, item := range c.Items {
chart.AddBar(*item.Label, *item.Color) chart.AddBar(*item.Label, *item.Color)
@ -52,11 +51,6 @@ func main() {
} }
} }
handler := event.Handler{ handler := event.NewHandler(layout)
Layout: layout,
RenderEvents: time.NewTicker(console.RenderRate).C,
ConsoleEvents: ui.PollEvents(),
}
handler.HandleEvents() handler.HandleEvents()
} }

View File

@ -6,11 +6,12 @@ import (
) )
type Component struct { type Component struct {
Type config.ComponentType
Drawable ui.Drawable Drawable ui.Drawable
Title string Title string
Position config.Position Position config.Position
Size config.Size Size config.Size
Type config.ComponentType RefreshRateMs int
} }
func (c *Component) Move(x, y int) { func (c *Component) Move(x, y int) {

View File

@ -10,8 +10,9 @@ import (
type Layout struct { type Layout struct {
ui.Block ui.Block
Components []Component Components []Component
menu *Menu ChangeModeEvents chan Mode
mode Mode mode Mode
menu *Menu
selection int selection int
} }
@ -42,11 +43,12 @@ func NewLayout(width, height int, menu *Menu) *Layout {
menu: menu, menu: menu,
mode: ModeDefault, mode: ModeDefault,
selection: 0, 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) { 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{drawable, title, position, size, Type}) l.Components = append(l.Components, Component{Type, drawable, title, position, size, refreshRateMs})
} }
func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable { func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable {
@ -62,36 +64,41 @@ func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable {
return components return components
} }
func (l *Layout) changeMode(m Mode) {
l.mode = m
l.ChangeModeEvents <- m
}
func (l *Layout) HandleConsoleEvent(e string) { func (l *Layout) HandleConsoleEvent(e string) {
switch e { switch e {
case console.KeyEnter: case console.KeyEnter:
switch l.mode { switch l.mode {
case ModeComponentSelect: case ModeComponentSelect:
l.menu.choose() l.menu.choose()
l.mode = ModeMenuOptionSelect l.changeMode(ModeMenuOptionSelect)
case ModeMenuOptionSelect: case ModeMenuOptionSelect:
option := l.menu.getSelectedOption() option := l.menu.getSelectedOption()
switch option { switch option {
case MenuOptionMove: case MenuOptionMove:
l.mode = ModeComponentMove l.changeMode(ModeComponentMove)
l.menu.moveOrResize() l.menu.moveOrResize()
case MenuOptionResize: case MenuOptionResize:
l.mode = ModeComponentResize l.changeMode(ModeComponentResize)
l.menu.moveOrResize() l.menu.moveOrResize()
case MenuOptionPinpoint: case MenuOptionPinpoint:
l.mode = ModeChartPinpoint l.changeMode(ModeChartPinpoint)
l.menu.idle() l.menu.idle()
chart := l.getSelectedComponent().Drawable.(*runchart.RunChart) chart := l.getSelectedComponent().Drawable.(*runchart.RunChart)
chart.MoveSelection(0) chart.MoveSelection(0)
case MenuOptionResume: case MenuOptionResume:
l.mode = ModeDefault l.changeMode(ModeDefault)
l.menu.idle() l.menu.idle()
} }
case ModeComponentMove: case ModeComponentMove:
fallthrough fallthrough
case ModeComponentResize: case ModeComponentResize:
l.menu.idle() l.menu.idle()
l.mode = ModeDefault l.changeMode(ModeDefault)
} }
case console.KeyEsc: case console.KeyEsc:
switch l.mode { switch l.mode {
@ -103,12 +110,12 @@ func (l *Layout) HandleConsoleEvent(e string) {
fallthrough fallthrough
case ModeMenuOptionSelect: case ModeMenuOptionSelect:
l.menu.idle() l.menu.idle()
l.mode = ModeDefault l.changeMode(ModeDefault)
} }
case console.KeyLeft: case console.KeyLeft:
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.mode = ModeComponentSelect l.changeMode(ModeComponentSelect)
l.selection = 0 l.selection = 0
l.menu.highlight(l.getComponent(l.selection)) l.menu.highlight(l.getComponent(l.selection))
case ModeChartPinpoint: case ModeChartPinpoint:
@ -127,7 +134,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
case console.KeyRight: case console.KeyRight:
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.mode = ModeComponentSelect l.changeMode(ModeComponentSelect)
l.selection = 0 l.selection = 0
l.menu.highlight(l.getComponent(l.selection)) l.menu.highlight(l.getComponent(l.selection))
case ModeChartPinpoint: case ModeChartPinpoint:
@ -146,7 +153,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
case console.KeyUp: case console.KeyUp:
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.mode = ModeComponentSelect l.changeMode(ModeComponentSelect)
l.selection = 0 l.selection = 0
l.menu.highlight(l.getComponent(l.selection)) l.menu.highlight(l.getComponent(l.selection))
case ModeComponentSelect: case ModeComponentSelect:
@ -164,7 +171,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
case console.KeyDown: case console.KeyDown:
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.mode = ModeComponentSelect l.changeMode(ModeComponentSelect)
l.selection = 0 l.selection = 0
l.menu.highlight(l.getComponent(l.selection)) l.menu.highlight(l.getComponent(l.selection))
case ModeComponentSelect: case ModeComponentSelect: