Skip to content

Commit

Permalink
Merge pull request #1579 from shirou/feature/enable_cache_on_host_boo…
Browse files Browse the repository at this point in the history
…ttime

[host]: add EnableBootTimeCache function
  • Loading branch information
shirou authored Jan 14, 2024
2 parents 4ba3cd4 + b10acd4 commit 9627f19
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 13 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ As of v3.23.6, it is now possible to pass a path location using `context`: impor

First priority is given to the value set in `context`, then the value from the environment variable, and finally the default location.

### Caching

As of v3.24.1, it is now possible to cached some values. These values default to false, not cached.

Be very careful that enabling the cache may cause inconsistencies. For example, if you enable caching of boottime on Linux, be aware that unintended values may be returned if [the boottime is changed by NTP after booted](https://github.com/shirou/gopsutil/issues/1070#issuecomment-842512782).

- `host`
- EnableBootTimeCache
- `process`
- EnableBootTimeCache

## Documentation

See https://pkg.go.dev/github.com/shirou/gopsutil/v3 or https://godocs.io/github.com/shirou/gopsutil/v3
Expand Down
7 changes: 7 additions & 0 deletions host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ func (t TemperatureStat) String() string {
return string(s)
}

var enableBootTimeCache bool

// EnableBootTimeCache change cache behavior of BootTime. If true, cache BootTime value. Default is false.
func EnableBootTimeCache(enable bool) {
enableBootTimeCache = enable
}

func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
Expand Down
12 changes: 8 additions & 4 deletions host/host_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ import (
var cachedBootTime uint64

func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
if enableBootTimeCache {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
}
}
tv, err := unix.SysctlTimeval("kern.boottime")
if err != nil {
return 0, err
}

atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec))
if enableBootTimeCache {
atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec))
}

return uint64(tv.Sec), nil
}
Expand Down
2 changes: 1 addition & 1 deletion host/host_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func numProcs(ctx context.Context) (uint64, error) {
}

func BootTimeWithContext(ctx context.Context) (uint64, error) {
return common.BootTimeWithContext(ctx)
return common.BootTimeWithContext(ctx, enableBootTimeCache)
}

func UptimeWithContext(ctx context.Context) (uint64, error) {
Expand Down
14 changes: 14 additions & 0 deletions host/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,17 @@ func TestPlatformInformation(t *testing.T) {

t.Logf("PlatformInformation(): %v, %v, %v", platform, family, version)
}

func BenchmarkBootTimeWithCache(b *testing.B) {
EnableBootTimeCache(true)
for i := 0; i < b.N; i++ {
BootTime()
}
}

func BenchmarkBootTimeWithoutCache(b *testing.B) {
EnableBootTimeCache(false)
for i := 0; i < b.N; i++ {
BootTime()
}
}
14 changes: 9 additions & 5 deletions host/host_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,20 @@ func uptimeMillis() (uint64, error) {
var cachedBootTime uint64

func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
if enableBootTimeCache {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
}
}
up, err := uptimeMillis()
if err != nil {
return 0, err
}
t = uint64((time.Duration(timeSinceMillis(up)) * time.Millisecond).Seconds())
atomic.StoreUint64(&cachedBootTime, t)
t := uint64((time.Duration(timeSinceMillis(up)) * time.Millisecond).Seconds())
if enableBootTimeCache {
atomic.StoreUint64(&cachedBootTime, t)
}
return t, nil
}

Expand Down
26 changes: 24 additions & 2 deletions internal/common/common_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
)

// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64

func DoSysctrl(mib string) ([]string, error) {
cmd := exec.Command("sysctl", "-n", mib)
cmd.Env = getSysctrlEnv(os.Environ())
Expand Down Expand Up @@ -56,7 +60,14 @@ func NumProcsWithContext(ctx context.Context) (uint64, error) {
return cnt, nil
}

func BootTimeWithContext(ctx context.Context) (uint64, error) {
func BootTimeWithContext(ctx context.Context, enableCache bool) (uint64, error) {
if enableCache {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
}
}

system, role, err := VirtualizationWithContext(ctx)
if err != nil {
return 0, err
Expand All @@ -72,7 +83,13 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
}

if useStatFile {
return readBootTimeStat(ctx)
t, err := readBootTimeStat(ctx)
if err != nil {
return 0, err
}
if enableCache {
atomic.StoreUint64(&cachedBootTime, t)
}
}

filename := HostProcWithContext(ctx, "uptime")
Expand All @@ -90,6 +107,11 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
}
currentTime := float64(time.Now().UnixNano()) / float64(time.Second)
t := currentTime - b

if enableCache {
atomic.StoreUint64(&cachedBootTime, uint64(t))
}

return uint64(t), nil
}

Expand Down
7 changes: 7 additions & 0 deletions process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ func (p NumCtxSwitchesStat) String() string {
return string(s)
}

var enableBootTimeCache bool

// EnableBootTimeCache change cache behavior of BootTime. If true, cache BootTime value. Default is false.
func EnableBootTimeCache(enable bool) {
enableBootTimeCache = enable
}

// Pids returns a slice of process ID list which are running now.
func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
Expand Down
2 changes: 1 addition & 1 deletion process/process_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui
Iowait: iotime / float64(clockTicks),
}

bootTime, _ := common.BootTimeWithContext(ctx)
bootTime, _ := common.BootTimeWithContext(ctx, enableBootTimeCache)
t, err := strconv.ParseUint(fields[22], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
Expand Down

0 comments on commit 9627f19

Please sign in to comment.