Skip to content

Commit

Permalink
improved support for small terminal windows (#108)
Browse files Browse the repository at this point in the history
* cputimer improvements

* bugfix: better support for small terminal windows

* adaptive progress bar width (termite 0.0.16 -> 0.0.17)
  • Loading branch information
sha1n authored Jun 13, 2021
1 parent 9d10273 commit a32dd82
Show file tree
Hide file tree
Showing 12 changed files with 494 additions and 178 deletions.
12 changes: 12 additions & 0 deletions api/cputimer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package api

import "time"

// ElapsedCPUTimeFn returns a pair of CPU time measurements, one for user time and one for system time.
type ElapsedCPUTimeFn = func() (perceived time.Duration, user time.Duration, system time.Duration)

// CPUTimer an abstraction for a platform system CPU timer
type CPUTimer interface {
Start() ElapsedCPUTimeFn
Elapsed() (perceived time.Duration, usr time.Duration, sys time.Duration)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/leodido/go-urn v1.2.1 // indirect
github.com/montanaflynn/stats v0.6.6
github.com/sha1n/clib v0.0.7
github.com/sha1n/termite v0.0.16
github.com/sha1n/termite v0.0.17
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.3
github.com/stretchr/testify v1.7.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sha1n/clib v0.0.7 h1:TdmHcf5aJoIeZAMkxUYfN6IoIWgqklTFEI+dMnsU84Y=
github.com/sha1n/clib v0.0.7/go.mod h1:V6sTAFNf6NtF8y6G6AD4NzGmXoRi0ybMVS7TbRIaEQ0=
github.com/sha1n/termite v0.0.16 h1:SG1jKvO6m0LgCd6rRlPjezmuZe83HK0DdPdZqcP+6QA=
github.com/sha1n/termite v0.0.16/go.mod h1:NVltlCkIrHs6qxBeXsh2Kf050nEdIBKNU7eEJVtrKZs=
github.com/sha1n/termite v0.0.17 h1:MU5BqPTzosPmzC1AfHGHwkG6gggSHoayFAey0hJQyJQ=
github.com/sha1n/termite v0.0.17/go.mod h1:NVltlCkIrHs6qxBeXsh2Kf050nEdIBKNU7eEJVtrKZs=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
Expand Down
10 changes: 5 additions & 5 deletions internal/cli/main_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func resolveExecutionContext(cmd *cobra.Command, spec api.BenchmarkSpec, ctx api

func resolveExecutionListener(cmd *cobra.Command, spec api.BenchmarkSpec, ctx api.IOContext) api.Listener {
if enableTerminalGUI(cmd, ctx) {
return pkg.NewProgressView(spec, terminalWidthOrFake, ctx)
return pkg.NewProgressView(spec, terminalDimensionsOrFake, ctx)
}

return pkg.NewLoggingProgressListener()
Expand All @@ -183,10 +183,10 @@ func enableTerminalGUI(cmd *cobra.Command, ctx api.IOContext) bool {
return ctx.Tty && enableRichOut && !(silentMode || debugMode || pipeOutputsMode)
}

func terminalWidthOrFake() int {
if w, _, err := termite.GetTerminalDimensions(); err == nil {
return w
func terminalDimensionsOrFake() (int, int) {
if w, h, err := termite.GetTerminalDimensions(); err == nil {
return w, h
}
return 0
return 0, 0

}
44 changes: 31 additions & 13 deletions pkg/cpu_timer_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,56 @@ package pkg
import (
"syscall"
"time"

"github.com/sha1n/benchy/api"
)

type unixChildrenCPUTimer struct {
r syscall.Rusage
sysTimeStart time.Time
usrTimeStart time.Time
r syscall.Rusage
who int
perceivedStartTime time.Time
sysTimeStart time.Time
usrTimeStart time.Time
}

// NewChildrenCPUTimer returns a new CPUTimer that measures sub-processes CPU time using system calls.
func NewChildrenCPUTimer() api.CPUTimer {
return newCPUTimer(syscall.RUSAGE_CHILDREN)
}

// NewSelfCPUTimer returns a new CPUTimer that measures this process' CPU time using system calls.
func NewSelfCPUTimer() api.CPUTimer {
return newCPUTimer(syscall.RUSAGE_SELF)
}

func newChildrenCPUTimer() CPUTimer {
return &unixChildrenCPUTimer{}
func newCPUTimer(who int) api.CPUTimer {
return &unixChildrenCPUTimer{
who: who,
}
}

func (t *unixChildrenCPUTimer) Start() func() (time.Duration, time.Duration) {
err := syscall.Getrusage(syscall.RUSAGE_CHILDREN, &t.r)
func (t *unixChildrenCPUTimer) Start() api.ElapsedCPUTimeFn {
err := syscall.Getrusage(t.who, &t.r)
if err != nil {
panic(err)
}

t.sysTimeStart = time.Unix(t.r.Stime.Unix())
t.perceivedStartTime = time.Now()
t.usrTimeStart = time.Unix(t.r.Utime.Unix())
t.sysTimeStart = time.Unix(t.r.Stime.Unix())

return t.Elapsed
}

func (t *unixChildrenCPUTimer) Elapsed() (usr time.Duration, sys time.Duration) {
err := syscall.Getrusage(syscall.RUSAGE_CHILDREN, &t.r)
func (t *unixChildrenCPUTimer) Elapsed() (perceived time.Duration, usr time.Duration, sys time.Duration) {
err := syscall.Getrusage(t.who, &t.r)
if err != nil {
panic(err)
}

sysTimeEnd := time.Unix(t.r.Stime.Unix())
usrTimeEnd := time.Unix(t.r.Utime.Unix())
perceivedTime := time.Now().Sub(t.perceivedStartTime)
usrTime := time.Unix(t.r.Utime.Unix()).Sub(t.usrTimeStart)
sysTime := time.Unix(t.r.Stime.Unix()).Sub(t.sysTimeStart)

return usrTimeEnd.Sub(t.usrTimeStart), sysTimeEnd.Sub(t.sysTimeStart)
return perceivedTime, usrTime, sysTime
}
75 changes: 75 additions & 0 deletions pkg/cpu_timer_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// +build linux darwin
// +build amd64 arm64

package pkg

import (
"os/exec"
"syscall"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestNewChildrenCPUTimer(t *testing.T) {
timer := NewChildrenCPUTimer()

assert.Equal(t, syscall.RUSAGE_CHILDREN, timer.(*unixChildrenCPUTimer).who)
}

func TestNewSelfCPUTimer(t *testing.T) {
timer := NewSelfCPUTimer()

assert.Equal(t, syscall.RUSAGE_SELF, timer.(*unixChildrenCPUTimer).who)
}

func Test_SelfCPUTimer_Elapsed_Unloaded(t *testing.T) {
timer := NewSelfCPUTimer()

elapsed := timer.Start()
perceived, usr, sys := elapsed()

assert.GreaterOrEqual(t, time.Millisecond*1, perceived)
assert.GreaterOrEqual(t, time.Millisecond*1, usr)
assert.GreaterOrEqual(t, time.Millisecond*1, sys)
}

func Test_SelfCPUTimer_Elapsed_Loaded(t *testing.T) {
timer := NewSelfCPUTimer()

elapsed := timer.Start()

assert.Eventually(t,
func() bool {
perceived, usr, sys := elapsed()

return perceived > time.Nanosecond*1 && usr > time.Nanosecond*1 && sys > time.Nanosecond*1
},
time.Second*1,
time.Nanosecond*1,
)
}

func Test_ChildrenCPUTimer_Elapsed_Loaded(t *testing.T) {
timer := NewChildrenCPUTimer()

elapsed := timer.Start()
exec.Command("go", "env").Run()
perceived, usr, sys := elapsed()

assert.GreaterOrEqual(t, perceived, time.Nanosecond*1)
assert.GreaterOrEqual(t, usr, time.Nanosecond*1)
assert.GreaterOrEqual(t, sys, time.Nanosecond*1)
}

func Test_ChildrenCPUTimer_Elapsed_Unloaded(t *testing.T) {
timer := NewChildrenCPUTimer()

elapsed := timer.Start()
perceived, usr, sys := elapsed()

assert.GreaterOrEqual(t, perceived, time.Nanosecond*0)
assert.Equal(t, time.Nanosecond*0, usr)
assert.Equal(t, time.Nanosecond*0, sys)
}
16 changes: 12 additions & 4 deletions pkg/cpu_timer_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@ package pkg

import (
"time"

"github.com/sha1n/benchy/api"
)

// NoopCPUTimer NOOP implementation of CPUTimer
type NoopCPUTimer struct{}

func newChildrenCPUTimer() CPUTimer {
// NewChildrenCPUTimer returns a NOOP CPUTimer implementation.
func NewChildrenCPUTimer() api.CPUTimer {
return NoopCPUTimer{}
}

// NewSelfCPUTimer returns a NOOP CPUTimer implementation.
func NewSelfCPUTimer() api.CPUTimer {
return NoopCPUTimer{}
}

// Start ...
func (t NoopCPUTimer) Start() func() (time.Duration, time.Duration) {
func (t NoopCPUTimer) Start() api.ElapsedCPUTimeFn {
return t.Elapsed
}

// Elapsed return 0, 0
func (t NoopCPUTimer) Elapsed() (usr time.Duration, sys time.Duration) {
return time.Nanosecond * 0, time.Nanosecond * 0
func (t NoopCPUTimer) Elapsed() (perceived time.Duration, usr time.Duration, sys time.Duration) {
return time.Nanosecond * 0, time.Nanosecond * 0, time.Nanosecond * 0
}
Loading

0 comments on commit a32dd82

Please sign in to comment.