From e26606acfd52a623fc617ae42584c19ecbde6592 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Fri, 17 Jun 2016 12:13:53 -0700 Subject: [PATCH] Memoize the CPU stats. Error if CPU fingerprinting fails. --- client/fingerprint/cpu.go | 65 ++++++++++++------------------------ helper/stats/cpu.go | 70 ++++++++++++++++++++++++++++----------- 2 files changed, 72 insertions(+), 63 deletions(-) diff --git a/client/fingerprint/cpu.go b/client/fingerprint/cpu.go index 8c04cd951..bd5d43165 100644 --- a/client/fingerprint/cpu.go +++ b/client/fingerprint/cpu.go @@ -5,8 +5,8 @@ import ( "log" "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/helper/stats" "github.com/hashicorp/nomad/nomad/structs" - "github.com/shirou/gopsutil/cpu" ) // CPUFingerprint is used to fingerprint the CPU @@ -22,55 +22,32 @@ func NewCPUFingerprint(logger *log.Logger) Fingerprint { } func (f *CPUFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - cpuInfo, err := cpu.Info() - if err != nil { - f.logger.Println("[WARN] Error reading CPU information:", err) + if err := stats.Init(); err != nil { + f.logger.Printf("[FATAL] fingerprint.cpu: unable to obtain CPU information: %v", err) return false, err } - // Assume all CPUs found have same Model and MHz. If cpu.Info() - // returns nil above, this loop is still safe. - var mhz float64 - var modelName string - for _, c := range cpuInfo { - mhz = c.Mhz - modelName = c.ModelName - break - } - - // Allow for a little precision slop - if mhz < 1.0 { - f.logger.Println("[WARN] fingerprint.cpu: Unable to obtain the CPU Mhz") - } else { - node.Attributes["cpu.frequency"] = fmt.Sprintf("%.6f", mhz) - f.logger.Printf("[DEBUG] fingerprint.cpu: frequency: %02.1f MHz", mhz) - } - - var numCores int - if numCores, err = cpu.Counts(true); err != nil { - numCores = 1 - f.logger.Println("[WARN] Unable to obtain the number of CPUs, defaulting to %d CPU", numCores) - } - - if numCores > 0 { - node.Attributes["cpu.numcores"] = fmt.Sprintf("%d", numCores) - f.logger.Printf("[DEBUG] fingerprint.cpu: core count: %d", numCores) - } - - if mhz > 0 && numCores > 0 { - tc := float64(numCores) * mhz - node.Attributes["cpu.totalcompute"] = fmt.Sprintf("%.6f", tc) - - if node.Resources == nil { - node.Resources = &structs.Resources{} - } - - node.Resources.CPU = int(tc) - } - + modelName := stats.CPUModelName() if modelName != "" { node.Attributes["cpu.modelname"] = modelName } + mhz := stats.CPUMHzPerCore() + node.Attributes["cpu.frequency"] = fmt.Sprintf("%.6f", mhz) + f.logger.Printf("[DEBUG] fingerprint.cpu: frequency: %02.1f MHz", mhz) + + numCores := stats.CPUNumCores() + node.Attributes["cpu.numcores"] = fmt.Sprintf("%d", numCores) + f.logger.Printf("[DEBUG] fingerprint.cpu: core count: %d", numCores) + + tt := stats.TotalTicksAvailable() + node.Attributes["cpu.totalcompute"] = fmt.Sprintf("%.6f", tt) + + if node.Resources == nil { + node.Resources = &structs.Resources{} + } + + node.Resources.CPU = int(tt) + return true, nil } diff --git a/helper/stats/cpu.go b/helper/stats/cpu.go index 0c6373dfd..624252ff3 100644 --- a/helper/stats/cpu.go +++ b/helper/stats/cpu.go @@ -1,30 +1,62 @@ package stats import ( - "github.com/shirou/gopsutil/cpu" + "fmt" "sync" + + "github.com/shirou/gopsutil/cpu" ) var ( - clkSpeed float64 - ticksLock sync.Mutex + cpuMhzPerCore float64 + cpuModelName string + cpuNumCores int + cpuTotalTicks float64 + + onceLer sync.Once ) -// TotalTicksAvailable calculates the total frequency available across all cores -func TotalTicksAvailable() float64 { - ticksLock.Lock() - defer ticksLock.Unlock() - if clkSpeed == 0.0 { - var cpuInfo []cpu.InfoStat - var err error - - var totalTicks float64 - if cpuInfo, err = cpu.Info(); err == nil { - for _, cpu := range cpuInfo { - totalTicks += cpu.Mhz - } - clkSpeed = totalTicks +func Init() error { + var err error + onceLer.Do(func() { + if cpuNumCores, err = cpu.Counts(true); err != nil { + err = fmt.Errorf("Unable to determine the number of CPU cores available: %v", err) + return } - } - return clkSpeed + + var cpuInfo []cpu.InfoStat + if cpuInfo, err = cpu.Info(); err != nil { + err = fmt.Errorf("Unable to obtain CPU information: %v", err) + return + } + + for _, cpu := range cpuInfo { + cpuModelName = cpu.ModelName + cpuMhzPerCore = cpu.Mhz + break + } + cpuTotalTicks = float64(cpuNumCores) * cpuMhzPerCore + }) + return err +} + +// CPUModelName returns the number of CPU cores available +func CPUNumCores() int { + return cpuNumCores +} + +// CPUMHzPerCore returns the MHz per CPU core +func CPUMHzPerCore() float64 { + return cpuMhzPerCore +} + +// CPUModelName returns the model name of the CPU +func CPUModelName() string { + return cpuModelName +} + +// TotalTicksAvailable calculates the total frequency available across all +// cores +func TotalTicksAvailable() float64 { + return cpuTotalTicks }