|
@@ -2,6 +2,7 @@ package gpu
|
|
|
|
|
|
import (
|
|
import (
|
|
"fmt"
|
|
"fmt"
|
|
|
|
+ "log/slog"
|
|
"syscall"
|
|
"syscall"
|
|
"unsafe"
|
|
"unsafe"
|
|
)
|
|
)
|
|
@@ -19,9 +20,10 @@ type MEMORYSTATUSEX struct {
|
|
}
|
|
}
|
|
|
|
|
|
var (
|
|
var (
|
|
- k32 = syscall.NewLazyDLL("kernel32.dll")
|
|
|
|
- globalMemoryStatusExProc = k32.NewProc("GlobalMemoryStatusEx")
|
|
|
|
- sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MEMORYSTATUSEX{}))
|
|
|
|
|
|
+ k32 = syscall.NewLazyDLL("kernel32.dll")
|
|
|
|
+ globalMemoryStatusExProc = k32.NewProc("GlobalMemoryStatusEx")
|
|
|
|
+ sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MEMORYSTATUSEX{}))
|
|
|
|
+ GetLogicalProcessorInformationEx = k32.NewProc("GetLogicalProcessorInformationEx")
|
|
)
|
|
)
|
|
|
|
|
|
var CudartGlobs = []string{
|
|
var CudartGlobs = []string{
|
|
@@ -55,3 +57,178 @@ func GetCPUMem() (memInfo, error) {
|
|
}
|
|
}
|
|
return memInfo{TotalMemory: memStatus.TotalPhys, FreeMemory: memStatus.AvailPhys, FreeSwap: memStatus.AvailPageFile}, nil
|
|
return memInfo{TotalMemory: memStatus.TotalPhys, FreeMemory: memStatus.AvailPhys, FreeSwap: memStatus.AvailPageFile}, nil
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+type LOGICAL_PROCESSOR_RELATIONSHIP uint32
|
|
|
|
+
|
|
|
|
+const (
|
|
|
|
+ RelationProcessorCore LOGICAL_PROCESSOR_RELATIONSHIP = iota
|
|
|
|
+ RelationNumaNode
|
|
|
|
+ RelationCache
|
|
|
|
+ RelationProcessorPackage
|
|
|
|
+ RelationGroup
|
|
|
|
+ RelationProcessorDie
|
|
|
|
+ RelationNumaNodeEx
|
|
|
|
+ RelationProcessorModule
|
|
|
|
+)
|
|
|
|
+const RelationAll LOGICAL_PROCESSOR_RELATIONSHIP = 0xffff
|
|
|
|
+
|
|
|
|
+type GROUP_AFFINITY struct {
|
|
|
|
+ Mask uintptr // KAFFINITY
|
|
|
|
+ Group uint16
|
|
|
|
+ Reserved [3]uint16
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type PROCESSOR_RELATIONSHIP struct {
|
|
|
|
+ Flags byte
|
|
|
|
+ EfficiencyClass byte
|
|
|
|
+ Reserved [20]byte
|
|
|
|
+ GroupCount uint16
|
|
|
|
+ GroupMask [1]GROUP_AFFINITY // len GroupCount
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Omitted unused structs: NUMA_NODE_RELATIONSHIP CACHE_RELATIONSHIP GROUP_RELATIONSHIP
|
|
|
|
+
|
|
|
|
+type SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct {
|
|
|
|
+ Relationship LOGICAL_PROCESSOR_RELATIONSHIP
|
|
|
|
+ Size uint32
|
|
|
|
+ U [1]byte // Union len Size
|
|
|
|
+ // PROCESSOR_RELATIONSHIP
|
|
|
|
+ // NUMA_NODE_RELATIONSHIP
|
|
|
|
+ // CACHE_RELATIONSHIP
|
|
|
|
+ // GROUP_RELATIONSHIP
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (group *GROUP_AFFINITY) IsMember(target *GROUP_AFFINITY) bool {
|
|
|
|
+ if group == nil || target == nil {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ return group.Mask&target.Mask != 0
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type winPackage struct {
|
|
|
|
+ groups []*GROUP_AFFINITY
|
|
|
|
+ coreCount int // performance cores = coreCount - efficiencyCoreCount
|
|
|
|
+ efficiencyCoreCount int
|
|
|
|
+ threadCount int
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (pkg *winPackage) IsMember(target *GROUP_AFFINITY) bool {
|
|
|
|
+ for _, group := range pkg.groups {
|
|
|
|
+ if group.IsMember(target) {
|
|
|
|
+ return true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func getLogicalProcessorInformationEx() ([]byte, error) {
|
|
|
|
+ buf := make([]byte, 1)
|
|
|
|
+ bufSize := len(buf)
|
|
|
|
+ ret, _, err := GetLogicalProcessorInformationEx.Call(
|
|
|
|
+ uintptr(RelationAll),
|
|
|
|
+ uintptr(unsafe.Pointer(&buf[0])),
|
|
|
|
+ uintptr(unsafe.Pointer(&bufSize)),
|
|
|
|
+ )
|
|
|
|
+ if ret != 0 {
|
|
|
|
+ return nil, fmt.Errorf("failed to determine size info ret:%d %w", ret, err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ buf = make([]byte, bufSize)
|
|
|
|
+ ret, _, err = GetLogicalProcessorInformationEx.Call(
|
|
|
|
+ uintptr(RelationAll),
|
|
|
|
+ uintptr(unsafe.Pointer(&buf[0])),
|
|
|
|
+ uintptr(unsafe.Pointer(&bufSize)),
|
|
|
|
+ )
|
|
|
|
+ if ret == 0 {
|
|
|
|
+ return nil, fmt.Errorf("failed to gather processor information ret:%d buflen:%d %w", ret, bufSize, err)
|
|
|
|
+ }
|
|
|
|
+ return buf, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func processSystemLogicalProcessorInforationList(buf []byte) []*winPackage {
|
|
|
|
+ var slpi *SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
|
|
|
|
+ // Find all the packages first
|
|
|
|
+ packages := []*winPackage{}
|
|
|
|
+ for bufOffset := 0; bufOffset < len(buf); bufOffset += int(slpi.Size) {
|
|
|
|
+ slpi = (*SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&buf[bufOffset]))
|
|
|
|
+ if slpi.Relationship != RelationProcessorPackage {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ pr := (*PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&slpi.U[0]))
|
|
|
|
+ pkg := &winPackage{}
|
|
|
|
+ ga0 := unsafe.Pointer(&pr.GroupMask[0])
|
|
|
|
+ for j := range pr.GroupCount {
|
|
|
|
+ gm := (*GROUP_AFFINITY)(unsafe.Pointer(uintptr(ga0) + uintptr(j)*unsafe.Sizeof(GROUP_AFFINITY{})))
|
|
|
|
+ pkg.groups = append(pkg.groups, gm)
|
|
|
|
+ }
|
|
|
|
+ packages = append(packages, pkg)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ slog.Info("packages", "count", len(packages))
|
|
|
|
+
|
|
|
|
+ // To identify efficiency cores we have to compare the relative values
|
|
|
|
+ // Larger values are "less efficient" (aka, more performant)
|
|
|
|
+ var maxEfficiencyClass byte
|
|
|
|
+ for bufOffset := 0; bufOffset < len(buf); bufOffset += int(slpi.Size) {
|
|
|
|
+ slpi = (*SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&buf[bufOffset]))
|
|
|
|
+ if slpi.Relationship != RelationProcessorCore {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ pr := (*PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&slpi.U[0]))
|
|
|
|
+ if pr.EfficiencyClass > maxEfficiencyClass {
|
|
|
|
+ maxEfficiencyClass = pr.EfficiencyClass
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if maxEfficiencyClass > 0 {
|
|
|
|
+ slog.Info("efficiency cores detected", "maxEfficiencyClass", maxEfficiencyClass)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // then match up the Cores to the Packages, count up cores, threads and efficiency cores
|
|
|
|
+ for bufOffset := 0; bufOffset < len(buf); bufOffset += int(slpi.Size) {
|
|
|
|
+ slpi = (*SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&buf[bufOffset]))
|
|
|
|
+ if slpi.Relationship != RelationProcessorCore {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ pr := (*PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&slpi.U[0]))
|
|
|
|
+ ga0 := unsafe.Pointer(&pr.GroupMask[0])
|
|
|
|
+ for j := range pr.GroupCount {
|
|
|
|
+ gm := (*GROUP_AFFINITY)(unsafe.Pointer(uintptr(ga0) + uintptr(j)*unsafe.Sizeof(GROUP_AFFINITY{})))
|
|
|
|
+ for _, pkg := range packages {
|
|
|
|
+ if pkg.IsMember(gm) {
|
|
|
|
+ pkg.coreCount++
|
|
|
|
+ if pr.Flags == 0 {
|
|
|
|
+ pkg.threadCount++
|
|
|
|
+ } else {
|
|
|
|
+ pkg.threadCount += 2
|
|
|
|
+ }
|
|
|
|
+ if pr.EfficiencyClass < maxEfficiencyClass {
|
|
|
|
+ pkg.efficiencyCoreCount++
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Sumarize the results
|
|
|
|
+ for i, pkg := range packages {
|
|
|
|
+ slog.Info("", "package", i, "cores", pkg.coreCount, "efficiency", pkg.efficiencyCoreCount, "threads", pkg.threadCount)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return packages
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func GetCPUDetails() ([]CPU, error) {
|
|
|
|
+ buf, err := getLogicalProcessorInformationEx()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ packages := processSystemLogicalProcessorInforationList(buf)
|
|
|
|
+ cpus := make([]CPU, len(packages))
|
|
|
|
+
|
|
|
|
+ for i, pkg := range packages {
|
|
|
|
+ cpus[i].CoreCount = pkg.coreCount
|
|
|
|
+ cpus[i].EfficiencyCoreCount = pkg.efficiencyCoreCount
|
|
|
|
+ cpus[i].ThreadCount = pkg.threadCount
|
|
|
|
+ }
|
|
|
|
+ return cpus, nil
|
|
|
|
+}
|