Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Commit

Permalink
PowerLibrary v.1.5.0 release (#15)
Browse files Browse the repository at this point in the history
* committing changes for go-multierror to errors pkg (#31)

* Control plane integration/E&P cores (#34)

* feat: control plane integration

* added check on core frequency range before tuning

* feat: unit tests fixed and utility function added

* refactored unit tests
* fixed certain edge cases
* added functions for retrieving frequency ranges

* refactor: refactor to address comments

* added explicite E/P core fields to profile
* added comments
* made adjustments based on comments

* feat: Move type value to core object and add testing

* moved type value to core struct
* added some getters/setters
* added unit tests
* added function refactor based on comments

* feat: further refactoring and commenting

* added function to append unique core types to list
* created seperated constructor for Ecore profiles
* replaced core type enums with public read-only interface

* feat: add core mocking

* formatting

* chore: bumping dependencies and go version (#36)

* chore: set specific go version (#37)

* Expose supported CPU types via constant (#41)

---------

Co-authored-by: Paulina-Osikoya <Paulina.Osikoya@intel.com>
Co-authored-by: Aaron Dorney <85556203+adorney99@users.noreply.github.com>
  • Loading branch information
3 people authored May 30, 2024
1 parent 17f098a commit 74d04c3
Show file tree
Hide file tree
Showing 17 changed files with 497 additions and 178 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ All frequency values are in kHz
````go
performanceProfile, err := NewPowerProfile("powerProfile", 2_600_000, 2_800_000, "performance", "performance")
````
You can also use the ``NewEcorePowerProfile(name, minFreq, maxFreq, emin, emax, governor, epp)`` constructor to
create a profile that supports environments with performance and efficiency cores.

````go
performanceProfile, err := NewEcorePowerProfile("powerProfile", 2_600_000, 2_800_000, 1_600_000, 1_800_000 "performance", "performance")
````

All values and support by hardware is validated during Profile creation.

Expand Down
13 changes: 4 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
module github.com/intel/power-optimization-library

go 1.21
go 1.22.3

require (
github.com/go-logr/logr v1.2.4
github.com/hashicorp/go-multierror v1.1.1
github.com/stretchr/testify v1.8.4
github.com/go-logr/logr v1.4.1
github.com/stretchr/testify v1.9.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
github.com/stretchr/objx v0.5.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
33 changes: 7 additions & 26 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 2 additions & 0 deletions pkg/power/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ value of the name and not the actual Power Profile, that can be retrieved throug

The Profile object is a replica of the Power Profile CRD. It’s just a way that the Power
Library can get the information about a Power Profile without having to constantly query the Kubernetes API.
The NewEcorePowerProfile constructor has 2 extra frequency fields called ``emin`` and ``emax`` and generates a profile
that will set different frequencies based on the core type. This is intended to be used on systems that have performance and efficiency cores.

## CPU

Expand Down
66 changes: 59 additions & 7 deletions pkg/power/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,38 @@ import (
"sync"
)

const (
numOfSupportedCoreTypes uint = 2
)

// uints are references to an array index of frequency sets
type supportedCores struct {
pcore uint
ecore uint
}

func (c *supportedCores) Pcore() uint {
return c.pcore
}
func (c *supportedCores) Ecore() uint {
return c.ecore
}

// public instance with read only access to supported core types
var CpuTypeReferences = supportedCores{}

// Cpu represents a compute unit/thread as seen by the OS
// it is either a physical core ot virtual thread if hyperthreading/SMT is enabled
type Cpu interface {
GetID() uint
GetAbsMinMax() (uint, uint)
SetPool(pool Pool) error

getPool() Pool
doSetPool(pool Pool) error
consolidate() error
consolidate_unsafe() error

GetCore() Core
// C-States stuff
SetCStates(cStates CStates) error

Expand All @@ -29,12 +50,20 @@ type cpuImpl struct {
id uint
mutex sync.Locker
pool Pool
core *cpuCore
core Core
// C-States properties
cStates *CStates
}

func newCpu(coreID uint, core *cpuCore) (Cpu, error) {
func newCpu(coreID uint, core Core) (Cpu, error) {
if featureList.isFeatureIdSupported(FrequencyScalingFeature) {
min, max, err := readCpuFreqLimits(coreID)
if err != nil {
return &cpuImpl{}, err
}
cType := coreTypes.appendIfUnique(min, max)
core.setType(cType)
}
cpu := &cpuImpl{
id: coreID,
mutex: &sync.Mutex{},
Expand All @@ -59,9 +88,6 @@ func (cpu *cpuImpl) consolidate_unsafe() error {
return nil
}




// SetPool moves current core to a specified target pool
// allowed movements are reservedPoolType <-> sharedPoolType and sharedPoolType <-> any exclusive pool
func (cpu *cpuImpl) SetPool(targetPool Pool) error {
Expand Down Expand Up @@ -116,7 +142,7 @@ func (cpu *cpuImpl) SetPool(targetPool Pool) error {
func (cpu *cpuImpl) doSetPool(pool Pool) error {
cpu.pool.poolMutex().Lock()
pool.poolMutex().Lock()
log.V(4).Info("acquired mutexes", "source", cpu.pool.Name(), "target", pool.Name(), "cpu")
log.V(4).Info("acquired mutexes", "source", cpu.pool.Name(), "target", pool.Name(), "cpu", cpu.id)

origPool := cpu.pool
cpu.pool = pool
Expand Down Expand Up @@ -154,6 +180,19 @@ func (cpu *cpuImpl) GetID() uint {
return cpu.id
}

func (cpu *cpuImpl) GetAbsMinMax() (uint, uint) {
// return 0,0 to prevent indexing error on coretype
if !featureList.isFeatureIdSupported(FrequencyScalingFeature) {
return 0, 0
}
typeNum := cpu.core.GetType()
return coreTypes[typeNum].GetMin(), coreTypes[typeNum].GetMax()
}

func (cpu *cpuImpl) GetCore() Core {
return cpu.core
}

func (cpu *cpuImpl) _setPoolProperty(pool Pool) {
cpu.pool = pool
}
Expand All @@ -175,6 +214,19 @@ func readCpuStringProperty(cpuID uint, file string) (string, error) {
return value, nil
}

// reads the min and max frequency of a CPU
func readCpuFreqLimits(id uint) (uint, uint, error) {
maxFreq, err := readCpuUintProperty(id, cpuMaxFreqFile)
if err != nil {
return 0, 0, err
}
minFreq, err := readCpuUintProperty(id, cpuMinFreqFile)
if err != nil {
return 0, 0, err
}
return minFreq, maxFreq, nil
}

type CpuList []Cpu

func (cpus *CpuList) IndexOf(cpu Cpu) int {
Expand Down
16 changes: 14 additions & 2 deletions pkg/power/cpu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ func (m *cpuMock) GetID() uint {
args := m.Called()
return args.Get(0).(uint)
}
func (m *cpuMock) GetAbsMinMax() (uint, uint) {
args := m.Called()
return args.Get(0).(uint), args.Get(1).(uint)
}

func (m *cpuMock) getPool() Pool {
args := m.Called().Get(0)
if args == nil {
Expand All @@ -45,6 +50,11 @@ func (m *cpuMock) getPool() Pool {
return args.(Pool)
}
}

func (m *cpuMock) GetCore() Core {
return m.Called().Get(0).(Core)
}

func (m *cpuMock) SetPool(pool Pool) error {
return m.Called(pool).Error(0)
}
Expand All @@ -64,7 +74,8 @@ func setupCpuScalingTests(cpufiles map[string]map[string]string) func() {
origBasePath := basePath
basePath = "testing/cpus"
defaultDefaultPowerProfile := defaultPowerProfile

typeCopy := coreTypes
referenceCopy := CpuTypeReferences
// backup pointer to function that gets all CPUs
// replace it with our controlled function
origGetNumOfCpusFunc := getNumberOfCpus
Expand Down Expand Up @@ -128,6 +139,8 @@ func setupCpuScalingTests(cpufiles map[string]map[string]string) func() {
getNumberOfCpus = origGetNumOfCpusFunc
// revert scaling driver feature to un initialised state
featureList[FrequencyScalingFeature].err = uninitialisedErr
coreTypes = typeCopy
CpuTypeReferences = referenceCopy
// revert default powerProfile
defaultPowerProfile = defaultDefaultPowerProfile
}
Expand Down Expand Up @@ -155,7 +168,6 @@ func TestNewCore(t *testing.T) {
id: 0,
core: core,
}, cpu)

// now "break" scaling driver by setting a feature error
featureList[FrequencyScalingFeature].err = fmt.Errorf("some error")

Expand Down
42 changes: 38 additions & 4 deletions pkg/power/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ type Host interface {
GetAllExclusivePools() *PoolList

GetAllCpus() *CpuList
GetFreqRanges() CoreTypeList
Topology() Topology

// returns number of distinct core types
NumCoreTypes() uint
AvailableCStates() []string
ValidateCStates(states CStates) error
//IsCStateValid(s string) bool
}

// create a pre-populated Host object
Expand Down Expand Up @@ -65,9 +66,33 @@ func initHost(nodeName string) (Host, error) {
for _, cpu := range *topology.CPUs() {
cpu._setPoolProperty(host.reservedPool)
}

// not very pretty but finds the lowest/highest core ranges
var highest uint
var highIndex uint
var lowIndex uint
for i, frequencies := range coreTypes {
if frequencies.GetMax() > highest {
highest = frequencies.GetMax()
lowIndex = highIndex
highIndex = uint(i)
}
if frequencies.GetMax() < highest {
lowIndex = uint(i)
}
}
CpuTypeReferences.pcore = highIndex
CpuTypeReferences.ecore = lowIndex
log.Info("discovered cpus", "cpus", len(*topology.CPUs()))

// coretypes are populated after default profile is generated so we need to update here
if featureList.isFeatureIdSupported(FrequencyScalingFeature) && host.NumCoreTypes() == 2 {
defaultPowerProfile.max = coreTypes[CpuTypeReferences.Pcore()].GetMax()
defaultPowerProfile.min = coreTypes[CpuTypeReferences.Pcore()].GetMax()
defaultPowerProfile.efficientMax = coreTypes[CpuTypeReferences.Ecore()].GetMax()
defaultPowerProfile.efficientMin = coreTypes[CpuTypeReferences.Ecore()].GetMax()
}
if host.NumCoreTypes() > numOfSupportedCoreTypes {
log.Error(fmt.Errorf("more than %d core types detected. This may result in undefined behavior: %v", numOfSupportedCoreTypes, coreTypes), "topology issues detected")
}
host.topology = topology

// create a shallow copy of pointers, changes to underlying cpu object will reflect in both lists,
Expand All @@ -89,6 +114,11 @@ func (host *hostImpl) GetReservedPool() Pool {
return host.reservedPool
}

// returns default min/max frequency range
func (host *hostImpl) GetFreqRanges() CoreTypeList {
return coreTypes
}

// AddExclusivePool creates new empty pool
func (host *hostImpl) AddExclusivePool(poolName string) (Pool, error) {
if i := host.exclusivePools.IndexOfName(poolName); i >= 0 {
Expand Down Expand Up @@ -128,6 +158,10 @@ func (host *hostImpl) GetAllExclusivePools() *PoolList {
return &host.exclusivePools
}

func (host *hostImpl) NumCoreTypes() uint {
return uint(len(coreTypes))
}

func (host *hostImpl) Topology() Topology {
return host.topology
}
Loading

0 comments on commit 74d04c3

Please sign in to comment.