gpu_windows.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package discover
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "syscall"
  6. "unsafe"
  7. )
  8. type MEMORYSTATUSEX struct {
  9. length uint32
  10. MemoryLoad uint32
  11. TotalPhys uint64
  12. AvailPhys uint64
  13. TotalPageFile uint64
  14. AvailPageFile uint64
  15. TotalVirtual uint64
  16. AvailVirtual uint64
  17. AvailExtendedVirtual uint64
  18. }
  19. var (
  20. k32 = syscall.NewLazyDLL("kernel32.dll")
  21. globalMemoryStatusExProc = k32.NewProc("GlobalMemoryStatusEx")
  22. sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MEMORYSTATUSEX{}))
  23. GetLogicalProcessorInformationEx = k32.NewProc("GetLogicalProcessorInformationEx")
  24. )
  25. var CudartGlobs = []string{
  26. "c:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v*\\bin\\cudart64_*.dll",
  27. }
  28. var NvmlGlobs = []string{
  29. "c:\\Windows\\System32\\nvml.dll",
  30. }
  31. var NvcudaGlobs = []string{
  32. "c:\\windows\\system*\\nvcuda.dll",
  33. }
  34. var OneapiGlobs = []string{
  35. "c:\\Windows\\System32\\DriverStore\\FileRepository\\*\\ze_intel_gpu64.dll",
  36. }
  37. var (
  38. CudartMgmtName = "cudart64_*.dll"
  39. NvcudaMgmtName = "nvcuda.dll"
  40. NvmlMgmtName = "nvml.dll"
  41. OneapiMgmtName = "ze_intel_gpu64.dll"
  42. )
  43. func GetCPUMem() (memInfo, error) {
  44. memStatus := MEMORYSTATUSEX{length: sizeofMemoryStatusEx}
  45. r1, _, err := globalMemoryStatusExProc.Call(uintptr(unsafe.Pointer(&memStatus)))
  46. if r1 == 0 {
  47. return memInfo{}, fmt.Errorf("GlobalMemoryStatusEx failed: %w", err)
  48. }
  49. return memInfo{TotalMemory: memStatus.TotalPhys, FreeMemory: memStatus.AvailPhys, FreeSwap: memStatus.AvailPageFile}, nil
  50. }
  51. type LOGICAL_PROCESSOR_RELATIONSHIP uint32
  52. const (
  53. RelationProcessorCore LOGICAL_PROCESSOR_RELATIONSHIP = iota
  54. RelationNumaNode
  55. RelationCache
  56. RelationProcessorPackage
  57. RelationGroup
  58. RelationProcessorDie
  59. RelationNumaNodeEx
  60. RelationProcessorModule
  61. )
  62. const RelationAll LOGICAL_PROCESSOR_RELATIONSHIP = 0xffff
  63. type GROUP_AFFINITY struct {
  64. Mask uintptr // KAFFINITY
  65. Group uint16
  66. Reserved [3]uint16
  67. }
  68. type PROCESSOR_RELATIONSHIP struct {
  69. Flags byte
  70. EfficiencyClass byte
  71. Reserved [20]byte
  72. GroupCount uint16
  73. GroupMask [1]GROUP_AFFINITY // len GroupCount
  74. }
  75. // Omitted unused structs: NUMA_NODE_RELATIONSHIP CACHE_RELATIONSHIP GROUP_RELATIONSHIP
  76. type SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct {
  77. Relationship LOGICAL_PROCESSOR_RELATIONSHIP
  78. Size uint32
  79. U [1]byte // Union len Size
  80. // PROCESSOR_RELATIONSHIP
  81. // NUMA_NODE_RELATIONSHIP
  82. // CACHE_RELATIONSHIP
  83. // GROUP_RELATIONSHIP
  84. }
  85. func (group *GROUP_AFFINITY) IsMember(target *GROUP_AFFINITY) bool {
  86. if group == nil || target == nil {
  87. return false
  88. }
  89. return group.Mask&target.Mask != 0
  90. }
  91. type winPackage struct {
  92. groups []*GROUP_AFFINITY
  93. coreCount int // performance cores = coreCount - efficiencyCoreCount
  94. efficiencyCoreCount int
  95. threadCount int
  96. }
  97. func (pkg *winPackage) IsMember(target *GROUP_AFFINITY) bool {
  98. for _, group := range pkg.groups {
  99. if group.IsMember(target) {
  100. return true
  101. }
  102. }
  103. return false
  104. }
  105. func getLogicalProcessorInformationEx() ([]byte, error) {
  106. buf := make([]byte, 1)
  107. bufSize := len(buf)
  108. ret, _, err := GetLogicalProcessorInformationEx.Call(
  109. uintptr(RelationAll),
  110. uintptr(unsafe.Pointer(&buf[0])),
  111. uintptr(unsafe.Pointer(&bufSize)),
  112. )
  113. if ret != 0 {
  114. return nil, fmt.Errorf("failed to determine size info ret:%d %w", ret, err)
  115. }
  116. buf = make([]byte, bufSize)
  117. ret, _, err = GetLogicalProcessorInformationEx.Call(
  118. uintptr(RelationAll),
  119. uintptr(unsafe.Pointer(&buf[0])),
  120. uintptr(unsafe.Pointer(&bufSize)),
  121. )
  122. if ret == 0 {
  123. return nil, fmt.Errorf("failed to gather processor information ret:%d buflen:%d %w", ret, bufSize, err)
  124. }
  125. return buf, nil
  126. }
  127. func processSystemLogicalProcessorInforationList(buf []byte) []*winPackage {
  128. var slpi *SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
  129. // Find all the packages first
  130. packages := []*winPackage{}
  131. for bufOffset := 0; bufOffset < len(buf); bufOffset += int(slpi.Size) {
  132. slpi = (*SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&buf[bufOffset]))
  133. if slpi.Relationship != RelationProcessorPackage {
  134. continue
  135. }
  136. pr := (*PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&slpi.U[0]))
  137. pkg := &winPackage{}
  138. ga0 := unsafe.Pointer(&pr.GroupMask[0])
  139. for j := range pr.GroupCount {
  140. gm := (*GROUP_AFFINITY)(unsafe.Pointer(uintptr(ga0) + uintptr(j)*unsafe.Sizeof(GROUP_AFFINITY{})))
  141. pkg.groups = append(pkg.groups, gm)
  142. }
  143. packages = append(packages, pkg)
  144. }
  145. slog.Info("packages", "count", len(packages))
  146. // To identify efficiency cores we have to compare the relative values
  147. // Larger values are "less efficient" (aka, more performant)
  148. var maxEfficiencyClass byte
  149. for bufOffset := 0; bufOffset < len(buf); bufOffset += int(slpi.Size) {
  150. slpi = (*SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&buf[bufOffset]))
  151. if slpi.Relationship != RelationProcessorCore {
  152. continue
  153. }
  154. pr := (*PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&slpi.U[0]))
  155. if pr.EfficiencyClass > maxEfficiencyClass {
  156. maxEfficiencyClass = pr.EfficiencyClass
  157. }
  158. }
  159. if maxEfficiencyClass > 0 {
  160. slog.Info("efficiency cores detected", "maxEfficiencyClass", maxEfficiencyClass)
  161. }
  162. // then match up the Cores to the Packages, count up cores, threads and efficiency cores
  163. for bufOffset := 0; bufOffset < len(buf); bufOffset += int(slpi.Size) {
  164. slpi = (*SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&buf[bufOffset]))
  165. if slpi.Relationship != RelationProcessorCore {
  166. continue
  167. }
  168. pr := (*PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&slpi.U[0]))
  169. ga0 := unsafe.Pointer(&pr.GroupMask[0])
  170. for j := range pr.GroupCount {
  171. gm := (*GROUP_AFFINITY)(unsafe.Pointer(uintptr(ga0) + uintptr(j)*unsafe.Sizeof(GROUP_AFFINITY{})))
  172. for _, pkg := range packages {
  173. if pkg.IsMember(gm) {
  174. pkg.coreCount++
  175. if pr.Flags == 0 {
  176. pkg.threadCount++
  177. } else {
  178. pkg.threadCount += 2
  179. }
  180. if pr.EfficiencyClass < maxEfficiencyClass {
  181. pkg.efficiencyCoreCount++
  182. }
  183. }
  184. }
  185. }
  186. }
  187. // Summarize the results
  188. for i, pkg := range packages {
  189. slog.Info("", "package", i, "cores", pkg.coreCount, "efficiency", pkg.efficiencyCoreCount, "threads", pkg.threadCount)
  190. }
  191. return packages
  192. }
  193. func GetCPUDetails() ([]CPU, error) {
  194. buf, err := getLogicalProcessorInformationEx()
  195. if err != nil {
  196. return nil, err
  197. }
  198. packages := processSystemLogicalProcessorInforationList(buf)
  199. cpus := make([]CPU, len(packages))
  200. for i, pkg := range packages {
  201. cpus[i].CoreCount = pkg.coreCount
  202. cpus[i].EfficiencyCoreCount = pkg.efficiencyCoreCount
  203. cpus[i].ThreadCount = pkg.threadCount
  204. }
  205. return cpus, nil
  206. }