2016-06-10 21:46:33 +00:00
|
|
|
package stats
|
|
|
|
|
|
|
|
import (
|
2018-05-08 21:00:31 +00:00
|
|
|
"context"
|
2023-03-28 13:27:58 +00:00
|
|
|
"errors"
|
2016-06-17 19:13:53 +00:00
|
|
|
"fmt"
|
2016-06-11 15:31:49 +00:00
|
|
|
"sync"
|
2018-05-08 21:00:31 +00:00
|
|
|
"time"
|
2016-06-17 19:13:53 +00:00
|
|
|
|
2021-03-30 18:47:33 +00:00
|
|
|
"github.com/shirou/gopsutil/v3/cpu"
|
2023-03-28 13:27:58 +00:00
|
|
|
"github.com/shoenig/go-m1cpu"
|
2016-06-11 15:31:49 +00:00
|
|
|
)
|
|
|
|
|
2018-05-08 21:00:31 +00:00
|
|
|
const (
|
2018-05-09 17:21:35 +00:00
|
|
|
// cpuInfoTimeout is the timeout used when gathering CPU info. This is used
|
|
|
|
// to override the default timeout in gopsutil which has a tendency to
|
|
|
|
// timeout on Windows.
|
2018-06-21 19:27:56 +00:00
|
|
|
cpuInfoTimeout = 60 * time.Second
|
2018-05-08 21:00:31 +00:00
|
|
|
)
|
|
|
|
|
2016-06-11 15:31:49 +00:00
|
|
|
var (
|
2023-03-28 13:27:58 +00:00
|
|
|
cpuPowerCoreCount int
|
|
|
|
cpuPowerCoreMHz uint64
|
|
|
|
cpuEfficiencyCoreCount int
|
|
|
|
cpuEfficiencyCoreMHz uint64
|
|
|
|
cpuTotalTicks uint64
|
|
|
|
cpuModelName string
|
|
|
|
)
|
2016-06-17 19:13:53 +00:00
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
var (
|
2017-03-14 19:56:31 +00:00
|
|
|
initErr error
|
2016-06-17 19:13:53 +00:00
|
|
|
onceLer sync.Once
|
2016-06-10 21:46:33 +00:00
|
|
|
)
|
|
|
|
|
2016-06-17 19:13:53 +00:00
|
|
|
func Init() error {
|
|
|
|
onceLer.Do(func() {
|
2023-03-28 13:27:58 +00:00
|
|
|
switch {
|
|
|
|
case m1cpu.IsAppleSilicon():
|
|
|
|
cpuModelName = m1cpu.ModelName()
|
|
|
|
cpuPowerCoreCount = m1cpu.PCoreCount()
|
|
|
|
cpuPowerCoreMHz = m1cpu.PCoreHz() / 1_000_000
|
|
|
|
cpuEfficiencyCoreCount = m1cpu.ECoreCount()
|
|
|
|
cpuEfficiencyCoreMHz = m1cpu.ECoreHz() / 1_000_000
|
|
|
|
bigTicks := uint64(cpuPowerCoreCount) * cpuPowerCoreMHz
|
|
|
|
littleTicks := uint64(cpuEfficiencyCoreCount) * cpuEfficiencyCoreMHz
|
|
|
|
cpuTotalTicks = bigTicks + littleTicks
|
|
|
|
default:
|
|
|
|
// for now, all other cpu types assume only power cores
|
|
|
|
// todo: this is already not true for Intel 13th generation
|
2016-06-17 19:13:53 +00:00
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
var err error
|
|
|
|
if cpuPowerCoreCount, err = cpu.Counts(true); err != nil {
|
|
|
|
initErr = errors.Join(initErr, fmt.Errorf("failed to detect number of CPU cores: %w", err))
|
|
|
|
}
|
2016-06-17 19:13:53 +00:00
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), cpuInfoTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
var cpuInfoStats []cpu.InfoStat
|
|
|
|
if cpuInfoStats, err = cpu.InfoWithContext(ctx); err != nil {
|
|
|
|
initErr = errors.Join(initErr, fmt.Errorf("Unable to obtain CPU information: %w", err))
|
|
|
|
}
|
2016-06-22 22:01:36 +00:00
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
for _, infoStat := range cpuInfoStats {
|
|
|
|
cpuModelName = infoStat.ModelName
|
2023-03-31 16:25:32 +00:00
|
|
|
if uint64(infoStat.Mhz) > cpuPowerCoreMHz {
|
|
|
|
cpuPowerCoreMHz = uint64(infoStat.Mhz)
|
|
|
|
}
|
2023-03-28 13:27:58 +00:00
|
|
|
}
|
2017-06-27 18:54:15 +00:00
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
// compute ticks using only power core, until we add support for
|
|
|
|
// detecting little cores on non-apple platforms
|
|
|
|
cpuTotalTicks = uint64(cpuPowerCoreCount) * cpuPowerCoreMHz
|
|
|
|
|
|
|
|
initErr = err
|
|
|
|
}
|
2016-06-17 19:13:53 +00:00
|
|
|
})
|
2017-03-14 19:56:31 +00:00
|
|
|
return initErr
|
2016-06-17 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
// CPUNumCores returns the number of CPU cores available.
|
|
|
|
//
|
|
|
|
// This is represented with two values - (Power (P), Efficiency (E)) so we can
|
|
|
|
// correctly compute total compute for processors with asymetric cores such as
|
|
|
|
// Apple Silicon.
|
|
|
|
//
|
|
|
|
// For platforms with symetric cores (or where we do not correcly detect asymetric
|
|
|
|
// cores), all cores are presented as P cores.
|
|
|
|
func CPUNumCores() (int, int) {
|
|
|
|
return cpuPowerCoreCount, cpuEfficiencyCoreCount
|
2016-06-17 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
// CPUMHzPerCore returns the MHz per CPU (P, E) core type.
|
|
|
|
//
|
|
|
|
// As with CPUNumCores, asymetric core detection currently only works with
|
|
|
|
// Apple Silicon CPUs.
|
|
|
|
func CPUMHzPerCore() (uint64, uint64) {
|
|
|
|
return cpuPowerCoreMHz, cpuEfficiencyCoreMHz
|
2016-06-17 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
// CPUModelName returns the model name of the CPU.
|
2016-06-17 19:13:53 +00:00
|
|
|
func CPUModelName() string {
|
|
|
|
return cpuModelName
|
|
|
|
}
|
|
|
|
|
2023-03-28 13:27:58 +00:00
|
|
|
// TotalTicksAvailable calculates the total MHz available across all cores.
|
|
|
|
//
|
|
|
|
// Where asymetric cores are correctly detected, the total ticks is the sum of
|
|
|
|
// the performance across both core types.
|
|
|
|
//
|
|
|
|
// Where asymetric cores are not correctly detected (such as Intel 13th gen),
|
|
|
|
// the total ticks available is over-estimated, as we assume all cores are P
|
|
|
|
// cores.
|
|
|
|
func TotalTicksAvailable() uint64 {
|
2016-06-17 19:13:53 +00:00
|
|
|
return cpuTotalTicks
|
2016-06-10 21:46:33 +00:00
|
|
|
}
|