So I'm trying to get processor groupings by socket. I've got a POC in C that does it, output is as follows.
[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,],[0,1,2,3,4,5,6,7,8,9,10,11,],]
[[12,13,14,15,16,17,18,19,20,21,22,23,],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,],]
And while I could just do something like the following, I'd much rather have code continuity and keep the project pure python. Plus this seemed like a good puzzle to solve for experiences sake.
socket_groups = eval(run_cmd_popen("get_logical_processor_information.exe", return_output=True).strip("\n"))
What I have thus far works, but only on one socket systems, for two socket systems, the second socket doesn't seem to have any data, and prints the following.
[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,][0,1,2,3,4,5,6,7,8,9,10,11,]]
[]
Here is what I have so far:
import os
import math
import platform
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, UINT
from ctypes import Structure, Union, WinDLL, c_uint64, POINTER, byref, get_last_error, WinError
from wmi import WMI
if platform.machine().endswith('64'):
KAFFINITY = c_uint64
else:
KAFFINITY = UINT
wmi = WMI()
NUM_SOCKETS = 0
for wmi_obj in wmi.query(f'SELECT * FROM Win32_processor '):
if wmi_obj is not None:
NUM_SOCKETS = 1
NUM_CPUS = os.cpu_count()
NUM_CPU_GROUPS = math.ceil(NUM_CPUS / 64)
ANYSIZE_ARRAY = NUM_CPU_GROUPS
ERROR_INSUFFICIENT_BUFFER = 122
RELATION_CACHE = 2
RELATION_NUMA_NODE = 1
RELATION_PROCESSOR_CORE = 0
RELATION_PROCESSOR_PACKAGE = 3
RELATION_GROUP = 4
RELATION_ALL = 0xffff
class _GROUP_AFFINITY(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
typedef struct _GROUP_AFFINITY {
KAFFINITY Mask;
WORD Group;
WORD Reserved[3];
} GROUP_AFFINITY, *PGROUP_AFFINITY;
'''
_fields_ = (
('Mask', KAFFINITY),
('Group', WORD),
('Reserved', WORD * 3)
)
class _PROCESSOR_GROUP_INFO(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info
typedef struct _PROCESSOR_GROUP_INFO {
BYTE MaximumProcessorCount;
BYTE ActiveProcessorCount;
BYTE Reserved[38];
KAFFINITY ActiveProcessorMask;
} PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO;
'''
_fields_ = (
('MaximumProcessorCount', BYTE),
('ActiveProcessorCount', BYTE),
('Reserved', BYTE * 38),
('ActiveProcessorMask', KAFFINITY),
)
class _GROUP_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship
typedef struct _GROUP_RELATIONSHIP {
WORD MaximumGroupCount;
WORD ActiveGroupCount;
BYTE Reserved[20];
PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY];
} GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP;
'''
_fields_ = (
('MaximumGroupCount', WORD),
('ActiveGroupCount', WORD),
('Reserved', BYTE * 20),
('GroupInfo', _PROCESSOR_GROUP_INFO * ANYSIZE_ARRAY)
)
class _DUMMYUNIONNAME_CACHE_RELATIONSHIP(Union):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
'''
_fields_ = (
('GroupMask', _GROUP_AFFINITY),
('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY) # * number of groups
)
class _CACHE_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
typedef struct _CACHE_RELATIONSHIP {
BYTE Level;
BYTE Associativity;
WORD LineSize;
DWORD CacheSize;
PROCESSOR_CACHE_TYPE Type;
BYTE Reserved[18];
WORD GroupCount;
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
} CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
'''
_fields_ = (
('Level', BYTE),
('Associativity', BYTE),
('LineSize', WORD),
('CacheSize', DWORD),
('Type', INT), # PROCESSOR_CACHE_TYPE is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Reserved', BYTE * 18),
('GroupCount', WORD),
('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
)
class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
'''
_fields_ = (
('GroupMask', _GROUP_AFFINITY),
('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY) # * number of groups
)
class _NUMA_NODE_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
typedef struct _NUMA_NODE_RELATIONSHIP {
DWORD NodeNumber;
BYTE Reserved[18];
WORD GroupCount;
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
} NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
'''
_fields_ = (
('NodeNumber', DWORD),
('Reserved', BYTE * 18),
('GroupCount', WORD),
('DUMMYUNIONNAME', _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP)
)
class _PROCESSOR_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
typedef struct _PROCESSOR_RELATIONSHIP {
BYTE Flags;
BYTE EfficiencyClass;
BYTE Reserved[20];
WORD GroupCount;
GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
} PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP;
'''
_fields_ = (
('Flags', BYTE),
('EfficiencyClass', BYTE),
('Reserved', BYTE * 20),
('GroupCount', WORD),
('GroupMask', _GROUP_AFFINITY * ANYSIZE_ARRAY) # * number of groups
)
class _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Union):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
union {
PROCESSOR_RELATIONSHIP Processor;
NUMA_NODE_RELATIONSHIP NumaNode;
CACHE_RELATIONSHIP Cache;
GROUP_RELATIONSHIP Group;
} DUMMYUNIONNAME;
'''
_fields_ = (
('Processor', _PROCESSOR_RELATIONSHIP),
('NumaNode', _NUMA_NODE_RELATIONSHIP),
('Cache', _CACHE_RELATIONSHIP),
('Group', _GROUP_RELATIONSHIP)
)
class _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
DWORD Size;
union {
PROCESSOR_RELATIONSHIP Processor;
NUMA_NODE_RELATIONSHIP NumaNode;
CACHE_RELATIONSHIP Cache;
GROUP_RELATIONSHIP Group;
} DUMMYUNIONNAME;
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
'''
_anonymous_ = 'DUMMYUNIONNAME',
_fields_ = (
('Relationship', INT), # _LOGICAL_PROCESSOR_RELATIONSHIP is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Size', DWORD),
('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
)
def GetLogicalProcessorInformationEx(relation_type):
'''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
BOOL GetLogicalProcessorInformationEx(
[in] LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
[out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
[in, out] PDWORD ReturnedLength
);
'''
dll = WinDLL('kernel32', use_last_error=True)
dll.GetLogicalProcessorInformationEx.argtypes = INT, POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX), PDWORD
dll.GetLogicalProcessorInformationEx.restype = BOOL
byte_len = DWORD()
# Call with null buffer to get required buffer size
result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
raise WinError(err)
# Allocate buffer
allocated_structure = (_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * NUM_SOCKETS)()
global byte_len_for_advance
byte_len_for_advance = byte_len
# Now do the call with the allocated buffer to fill it up
result = dll.GetLogicalProcessorInformationEx(relation_type, allocated_structure, byref(byte_len))
if not result:
raise WinError(get_last_error())
return allocated_structure
def _print_mask(mask):
# print(f"{mask=}")
digits = [int(x) for x in mask]
group_core_list = []
print("[", end='')
core = 0
for i in digits:
if i == 1:
print(f"{core},", end='')
group_core_list.append(core)
core = 1
print("]", end='')
# print(group_core_list)
if __name__ == "__main__":
enum_info = GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
for p_info in enum_info:
print("[", end='')
for group_index in range(0, p_info.Processor.GroupCount):
_print_mask(f"{p_info.Processor.GroupMask[group_index].Mask:8b}")
print("]\n", end='')
breakpoint
I think I need to do something with shifting a pointer to the next structure maybe? But I don't know how to go about that. Also I'm not solid on if _print_mask() will do exactly what I want when I get the second sockets info, but that is a later problem.
Any thought internet?
Edit
Thanks to Mark Tolonen for the answer/help!
Here is my working solution:
from typing import List, Dict
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, WPARAM
from ctypes import Structure, Union, WinDLL, POINTER, byref, cast, get_last_error, WinError, c_char_p, create_string_buffer
KAFFINITY = WPARAM # WPARAM is 32-bit or 64-bit unsigned depending on platform
ANYSIZE_ARRAY = 1
ERROR_INSUFFICIENT_BUFFER = 122
RELATION_CACHE = 2
RELATION_NUMA_NODE = 1
RELATION_PROCESSOR_CORE = 0
RELATION_PROCESSOR_PACKAGE = 3
RELATION_GROUP = 4
RELATION_ALL = 0xffff
class _GROUP_AFFINITY(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
typedef struct _GROUP_AFFINITY {
KAFFINITY Mask;
WORD Group;
WORD Reserved[3];
} GROUP_AFFINITY, *PGROUP_AFFINITY;
'''
_fields_ = (
('Mask', KAFFINITY),
('Group', WORD),
('Reserved', WORD * 3)
)
class _PROCESSOR_GROUP_INFO(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info
typedef struct _PROCESSOR_GROUP_INFO {
BYTE MaximumProcessorCount;
BYTE ActiveProcessorCount;
BYTE Reserved[38];
KAFFINITY ActiveProcessorMask;
} PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO;
'''
_fields_ = (
('MaximumProcessorCount', BYTE),
('ActiveProcessorCount', BYTE),
('Reserved', BYTE * 38),
('ActiveProcessorMask', KAFFINITY),
)
class _GROUP_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship
typedef struct _GROUP_RELATIONSHIP {
WORD MaximumGroupCount;
WORD ActiveGroupCount;
BYTE Reserved[20];
PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY];
} GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP;
'''
_fields_ = (
('MaximumGroupCount', WORD),
('ActiveGroupCount', WORD),
('Reserved', BYTE * 20),
('GroupInfo', _PROCESSOR_GROUP_INFO * ANYSIZE_ARRAY)
)
class _DUMMYUNIONNAME_CACHE_RELATIONSHIP(Union):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
'''
_fields_ = (
('GroupMask', _GROUP_AFFINITY),
('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY) # * number of groups
)
class _CACHE_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
typedef struct _CACHE_RELATIONSHIP {
BYTE Level;
BYTE Associativity;
WORD LineSize;
DWORD CacheSize;
PROCESSOR_CACHE_TYPE Type;
BYTE Reserved[18];
WORD GroupCount;
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
} CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
'''
_fields_ = (
('Level', BYTE),
('Associativity', BYTE),
('LineSize', WORD),
('CacheSize', DWORD),
('Type', INT), # PROCESSOR_CACHE_TYPE is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Reserved', BYTE * 18),
('GroupCount', WORD),
('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
)
class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
'''
_fields_ = (
('GroupMask', _GROUP_AFFINITY),
('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY) # * number of groups
)
class _NUMA_NODE_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
typedef struct _NUMA_NODE_RELATIONSHIP {
DWORD NodeNumber;
BYTE Reserved[18];
WORD GroupCount;
union {
GROUP_AFFINITY GroupMask;
GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
} DUMMYUNIONNAME;
} NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
'''
_fields_ = (
('NodeNumber', DWORD),
('Reserved', BYTE * 18),
('GroupCount', WORD),
('DUMMYUNIONNAME', _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP)
)
class _PROCESSOR_RELATIONSHIP(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
typedef struct _PROCESSOR_RELATIONSHIP {
BYTE Flags;
BYTE EfficiencyClass;
BYTE Reserved[20];
WORD GroupCount;
GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
} PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP;
'''
_fields_ = (
('Flags', BYTE),
('EfficiencyClass', BYTE),
('Reserved', BYTE * 20),
('GroupCount', WORD),
('GroupMask', _GROUP_AFFINITY * ANYSIZE_ARRAY) # * number of groups
)
class _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Union):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
union {
PROCESSOR_RELATIONSHIP Processor;
NUMA_NODE_RELATIONSHIP NumaNode;
CACHE_RELATIONSHIP Cache;
GROUP_RELATIONSHIP Group;
} DUMMYUNIONNAME;
'''
_fields_ = (
('Processor', _PROCESSOR_RELATIONSHIP),
('NumaNode', _NUMA_NODE_RELATIONSHIP),
('Cache', _CACHE_RELATIONSHIP),
('Group', _GROUP_RELATIONSHIP)
)
class _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Structure):
'''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
DWORD Size;
union {
PROCESSOR_RELATIONSHIP Processor;
NUMA_NODE_RELATIONSHIP NumaNode;
CACHE_RELATIONSHIP Cache;
GROUP_RELATIONSHIP Group;
} DUMMYUNIONNAME;
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
'''
_anonymous_ = 'DUMMYUNIONNAME',
_fields_ = (
('Relationship', INT), # _LOGICAL_PROCESSOR_RELATIONSHIP is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Size', DWORD),
('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
)
def _GetLogicalProcessorInformationEx(relation_type):
'''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
BOOL GetLogicalProcessorInformationEx(
[in] LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
[out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
[in, out] PDWORD ReturnedLength
);
'''
dll = WinDLL('kernel32', use_last_error=True)
# manage the allocated buffer as a c_char_p for easier pointer manipulation
dll.GetLogicalProcessorInformationEx.argtypes = INT, c_char_p, PDWORD
dll.GetLogicalProcessorInformationEx.restype = BOOL
byte_len = DWORD()
# Call with null buffer to get required buffer size
result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
raise WinError(err)
# Allocate byte buffer
buffer = create_string_buffer(byte_len.value)
# Now do the call with the buffer to fill it up
result = dll.GetLogicalProcessorInformationEx(relation_type, buffer, byref(byte_len))
if not result:
raise WinError(get_last_error())
return buffer, byte_len.value # return buffer and length
def get_group_affinty_mask_info() -> List[Dict[int, list]]:
'''
'''
# The returned structure array can vary in element size, so walk the buffer by the size of its element.
buffer, len = _GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
offset = 0
socket_group_infos = []
while offset < len:
# cast the current offset to the structure and extract contents
processor_info = cast(byref(buffer, offset), POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)).contents
# print(f"{processor_info.Size=} {processor_info.Processor.GroupCount=}")
# cast the group mask array to the correct size
group_affinitys = cast(byref(processor_info.Processor.GroupMask), POINTER(_GROUP_AFFINITY * processor_info.Processor.GroupCount)).contents
groups = {}
for group_affinity in group_affinitys:
# print(f"{group_affinity.Mask=:016X} {group_affinity.Group=}")
group_cores = []
core_index = 0
# Convert the mask to the binary representation of an 8-digit, zero-padded on the left string.
# Then turn that string of 1's and 0's into a list, and reverse it.
for i in [int(x) for x in f"{group_affinity.Mask:8b}"][::-1]:
if i == 1:
group_cores.append(core_index)
core_index = 1
groups[group_affinity.Group] = group_cores
socket_group_infos.append(groups)
# advance by the size consumed
offset = processor_info.Size
return socket_group_infos
CodePudding user response:
I only have a single core with 8 logical processors, so I can't verify I got everything right for your situation, but try this:
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, UINT, WPARAM
from ctypes import (Structure, Union, WinDLL, c_uint64, POINTER, byref, cast,
get_last_error, WinError, c_char_p, create_string_buffer)
KAFFINITY = WPARAM # WPARAM is 32-bit or 64-bit unsigned depending on platform
ANYSIZE_ARRAY = 1 # actual definition
#
# unchanged portion elided
#
def GetLogicalProcessorInformationEx(relation_type):
'''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
BOOL GetLogicalProcessorInformationEx(
[in] LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
[out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
[in, out] PDWORD ReturnedLength
);
'''
dll = WinDLL('kernel32', use_last_error=True)
# manage the allocated buffer as a c_char_p for easier pointer manipulation
dll.GetLogicalProcessorInformationEx.argtypes = INT, c_char_p, PDWORD
dll.GetLogicalProcessorInformationEx.restype = BOOL
byte_len = DWORD()
# Call with null buffer to get required buffer size
result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
raise WinError(err)
# Allocate byte buffer
buffer = create_string_buffer(byte_len.value)
# Now do the call with the buffer to fill it up
result = dll.GetLogicalProcessorInformationEx(relation_type, buffer, byref(byte_len))
if not result:
raise WinError(get_last_error())
return buffer,byte_len.value # return buffer and length
if __name__ == "__main__":
# The returned structure array can vary in element size,
# so walk the buffer by the size of its element.
buffer,len = GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
offset = 0
while offset < len:
# cast the current offset to the structure and extract contents
info = cast(byref(buffer,offset), POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)).contents
print(f'{info.Size=} {info.Processor.GroupCount=}')
# cast the group mask array to the correct size
gaffs = cast(byref(info.Processor.GroupMask),POINTER(_GROUP_AFFINITY * info.Processor.GroupCount)).contents
for gaff in gaffs:
print(f'{gaff.Mask=:016X} {gaff.Group=}')
# advance by the size consumed
offset = info.Size
Output on my one core, 8 logical proc system:
info.Size=48 info.Processor.GroupCount=1
gaff.Mask=00000000000000FF gaff.Group=0
If this doesn't work for your complex system, post the returned buffer,len
from my solution returned by your system and I'll fix it.