Skip to content

Commit 7294c07

Browse files
authored
Merge pull request #1915 from OrbintSoft/cores-count-without-wmi
Cores count without wmi
2 parents 8dbc66d + 0dd3ba1 commit 7294c07

File tree

1 file changed

+78
-20
lines changed

1 file changed

+78
-20
lines changed

cpu/cpu_windows.go

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"errors"
99
"fmt"
10+
"math/bits"
1011
"strconv"
1112
"unsafe"
1213

@@ -57,6 +58,20 @@ const (
5758
win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{})) //nolint:revive //FIXME
5859
)
5960

61+
type relationship uint32
62+
63+
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
64+
const (
65+
relationProcessorCore = relationship(0)
66+
relationProcessorPackage = relationship(3)
67+
)
68+
69+
const (
70+
kAffinitySize = unsafe.Sizeof(int(0))
71+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/interrupt-affinity-and-priority
72+
maxLogicalProcessorsPerGroup = uint32(unsafe.Sizeof(kAffinitySize * 8))
73+
)
74+
6075
// Times returns times stat per cpu and combined for all CPUs
6176
func Times(percpu bool) ([]TimesStat, error) {
6277
return TimesWithContext(context.Background(), percpu)
@@ -101,6 +116,21 @@ func Info() ([]InfoStat, error) {
101116
return InfoWithContext(context.Background())
102117
}
103118

119+
// this function iterates over each set bit in the package affinity mask, each bit represent a logical processor in a group (assuming you are iteriang over a package mask)
120+
// the function is used also to compute the global logical processor number
121+
// https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups
122+
// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
123+
// and https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
124+
// and https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
125+
func forEachSetBit64(mask uint64, fn func(bit int)) {
126+
m := mask
127+
for m != 0 {
128+
b := bits.TrailingZeros64(m)
129+
fn(b)
130+
m &= m - 1
131+
}
132+
}
133+
104134
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
105135
var ret []InfoStat
106136
var dst []win32_Processor
@@ -121,14 +151,42 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
121151
Family: strconv.FormatUint(uint64(l.Family), 10),
122152
VendorID: l.Manufacturer,
123153
ModelName: l.Name,
124-
Cores: int32(l.NumberOfLogicalProcessors),
154+
Cores: int32(l.NumberOfLogicalProcessors), // TO BE REMOVED, set by getSystemLogicalProcessorInformationEx
125155
PhysicalID: procID,
126156
Mhz: float64(l.MaxClockSpeed),
127157
Flags: []string{},
128158
}
129159
ret = append(ret, cpu)
130160
}
131161

162+
processorPackages, err := getSystemLogicalProcessorInformationEx(relationProcessorPackage)
163+
if err != nil {
164+
// return an error whem wmi will be removed
165+
// return ret, fmt.Errorf("failed to get processor package information: %w", err)
166+
return ret, nil
167+
}
168+
169+
if len(processorPackages) != len(ret) {
170+
// this should never happen, but it's kept for safety until wmi is removed
171+
return ret, nil
172+
}
173+
174+
for _, pkg := range processorPackages {
175+
logicalCount := 0
176+
for i, ga := range pkg.processor.groupMask {
177+
g := int(ga.group)
178+
forEachSetBit64(uint64(ga.mask), func(bit int) {
179+
// the global logical processor label
180+
globalLpl := g*int(maxLogicalProcessorsPerGroup) + bit
181+
if globalLpl >= 0 {
182+
logicalCount++
183+
}
184+
})
185+
186+
ret[i].Cores = int32(logicalCount)
187+
}
188+
}
189+
132190
return ret, nil
133191
}
134192

@@ -207,7 +265,7 @@ type systemInfo struct {
207265
}
208266

209267
type groupAffinity struct {
210-
mask uintptr // https://learn.microsoft.com/it-it/windows-hardware/drivers/kernel/interrupt-affinity-and-priority#about-kaffinity
268+
mask uintptr // https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/interrupt-affinity-and-priority#about-kaffinity
211269
group uint16
212270
reserved [3]uint16
213271
}
@@ -223,43 +281,43 @@ type processorRelationship struct {
223281

224282
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
225283
type systemLogicalProcessorInformationEx struct {
226-
Relationship uint32
227-
Size uint32
228-
Processor processorRelationship
284+
relationship uint32
285+
size uint32
286+
processor processorRelationship
229287
}
230288

231-
func getPhysicalCoreCount() (int, error) {
289+
func getSystemLogicalProcessorInformationEx(relationship relationship) ([]systemLogicalProcessorInformationEx, error) {
232290
var length uint32
233-
const relationAll = 0xffff
234-
const relationProcessorCore = 0x0
235-
236291
// First call to determine the required buffer size
237-
_, _, err := procGetLogicalProcessorInformationEx.Call(uintptr(relationAll), 0, uintptr(unsafe.Pointer(&length)))
292+
_, _, err := procGetLogicalProcessorInformationEx.Call(uintptr(relationship), 0, uintptr(unsafe.Pointer(&length)))
238293
if err != nil && !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
239-
return 0, fmt.Errorf("failed to get buffer size: %w", err)
294+
return nil, fmt.Errorf("failed to get buffer size: %w", err)
240295
}
241296

242297
// Allocate the buffer
243298
buffer := make([]byte, length)
244299

245300
// Second call to retrieve the processor information
246-
_, _, err = procGetLogicalProcessorInformationEx.Call(uintptr(relationAll), uintptr(unsafe.Pointer(&buffer[0])), uintptr(unsafe.Pointer(&length)))
301+
_, _, err = procGetLogicalProcessorInformationEx.Call(uintptr(relationship), uintptr(unsafe.Pointer(&buffer[0])), uintptr(unsafe.Pointer(&length)))
247302
if err != nil && !errors.Is(err, windows.NTE_OP_OK) {
248-
return 0, fmt.Errorf("failed to get logical processor information: %w", err)
303+
return nil, fmt.Errorf("failed to get logical processor information: %w", err)
249304
}
250305

251-
// Iterate through the buffer to count physical cores
306+
// Convert the byte slice into a slice of systemLogicalProcessorInformationEx structs
252307
offset := uintptr(0)
253-
ncpus := 0
308+
var infos []systemLogicalProcessorInformationEx
254309
for offset < uintptr(length) {
255310
info := (*systemLogicalProcessorInformationEx)(unsafe.Pointer(uintptr(unsafe.Pointer(&buffer[0])) + offset))
256-
if info.Relationship == relationProcessorCore {
257-
ncpus++
258-
}
259-
offset += uintptr(info.Size)
311+
infos = append(infos, *info)
312+
offset += uintptr(info.size)
260313
}
261314

262-
return ncpus, nil
315+
return infos, nil
316+
}
317+
318+
func getPhysicalCoreCount() (int, error) {
319+
infos, err := getSystemLogicalProcessorInformationEx(relationProcessorCore)
320+
return len(infos), err
263321
}
264322

265323
func CountsWithContext(_ context.Context, logical bool) (int, error) {

0 commit comments

Comments
 (0)