Skip to content

Commit ecd558a

Browse files
authored
Re-wrap most of the enums in cuda.bindings.nvml for cuda.core.system. (#2014)
* Re-wrap most of the enums in cuda.bindings.nvml for cuda.core.system. * Fixes for Python 3.10 * Fix Windows test * Line wrapping * Fix typo * Address comments in robo-review * Add assert * Add sync checks * Fix spacing * Clean up test
1 parent 7bd6397 commit ecd558a

17 files changed

Lines changed: 1149 additions & 200 deletions

cuda_core/cuda/core/system/_clock.pxi

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,71 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55

6-
ClockId = nvml.ClockId
7-
ClocksEventReasons = nvml.ClocksEventReasons
8-
ClockType = nvml.ClockType
6+
class ClockId(StrEnum):
7+
"""
8+
Clock Ids. These are used in combination with :class:`ClockType` to specify a single clock value.
9+
"""
10+
CURRENT = "current"
11+
CUSTOMER_BOOST_MAX = "customer_boost_max"
12+
# APP_CLOCK_TARGET and APP_CLOCK_DEFAULT are deprecated so not included here
13+
14+
15+
ClockId.CURRENT.__doc__ = "Current actual clock value."
16+
ClockId.CUSTOMER_BOOST_MAX.__doc__ = "OEM-defined maximum clock rate"
17+
18+
19+
_CLOCK_ID_MAPPING = {
20+
ClockId.CURRENT: nvml.ClockId.CURRENT,
21+
ClockId.CUSTOMER_BOOST_MAX: nvml.ClockId.CUSTOMER_BOOST_MAX,
22+
}
23+
24+
25+
class ClocksEventReasons(StrEnum):
26+
"""
27+
Reasons for a clocks event. These are used in combination with :class:`ClockType` to specify the reason for a clocks event.
28+
"""
29+
NONE = "none"
30+
GPU_IDLE = "gpu_idle"
31+
APPLICATIONS_CLOCKS_SETTING = "applications_clocks_setting"
32+
SW_POWER_CAP = "sw_power_cap"
33+
HW_SLOWDOWN = "hw_slowdown"
34+
SYNC_BOOST = "sync_boost"
35+
SW_THERMAL_SLOWDOWN = "sw_thermal_slowdown"
36+
HW_THERMAL_SLOWDOWN = "hw_thermal_slowdown"
37+
HW_POWER_BRAKE_SLOWDOWN = "hw_power_brake_slowdown"
38+
DISPLAY_CLOCK_SETTING = "display_clock_setting"
39+
40+
41+
_CLOCKS_EVENT_REASONS_MAPPING = {
42+
nvml.ClocksEventReasons.EVENT_REASON_NONE: ClocksEventReasons.NONE,
43+
nvml.ClocksEventReasons.EVENT_REASON_GPU_IDLE: ClocksEventReasons.GPU_IDLE,
44+
nvml.ClocksEventReasons.EVENT_REASON_APPLICATIONS_CLOCKS_SETTING: ClocksEventReasons.APPLICATIONS_CLOCKS_SETTING,
45+
nvml.ClocksEventReasons.EVENT_REASON_SW_POWER_CAP: ClocksEventReasons.SW_POWER_CAP,
46+
nvml.ClocksEventReasons.THROTTLE_REASON_HW_SLOWDOWN: ClocksEventReasons.HW_SLOWDOWN,
47+
nvml.ClocksEventReasons.EVENT_REASON_SYNC_BOOST: ClocksEventReasons.SYNC_BOOST,
48+
nvml.ClocksEventReasons.EVENT_REASON_SW_THERMAL_SLOWDOWN: ClocksEventReasons.SW_THERMAL_SLOWDOWN,
49+
nvml.ClocksEventReasons.THROTTLE_REASON_HW_THERMAL_SLOWDOWN: ClocksEventReasons.HW_THERMAL_SLOWDOWN,
50+
nvml.ClocksEventReasons.THROTTLE_REASON_HW_POWER_BRAKE_SLOWDOWN: ClocksEventReasons.HW_POWER_BRAKE_SLOWDOWN,
51+
nvml.ClocksEventReasons.EVENT_REASON_DISPLAY_CLOCK_SETTING: ClocksEventReasons.DISPLAY_CLOCK_SETTING,
52+
}
53+
54+
55+
class ClockType(StrEnum):
56+
"""
57+
Clock types. All speeds are in Mhz.
58+
"""
59+
GRAPHICS = "graphics"
60+
SM = "sm"
61+
MEMORY = "memory"
62+
VIDEO = "video"
63+
64+
65+
_CLOCK_TYPE_MAPPING = {
66+
ClockType.GRAPHICS: nvml.ClockType.CLOCK_GRAPHICS,
67+
ClockType.SM: nvml.ClockType.CLOCK_SM,
68+
ClockType.MEMORY: nvml.ClockType.CLOCK_MEM,
69+
ClockType.VIDEO: nvml.ClockType.CLOCK_VIDEO,
70+
}
971

1072

1173
cdef class ClockOffsets:
@@ -48,26 +110,40 @@ cdef class ClockInfo:
48110
cdef intptr_t _handle
49111
cdef int _clock_type
50112

51-
def __init__(self, handle, clock_type: ClockType):
113+
def __init__(self, handle, clock_type: ClockType | str):
52114
self._handle = handle
115+
try:
116+
clock_type = _CLOCK_TYPE_MAPPING[clock_type]
117+
except KeyError:
118+
raise ValueError(
119+
f"Invalid clock type: {clock_type}. "
120+
f"Must be one of {list(ClockType.__members__.values())}"
121+
) from None
53122
self._clock_type = int(clock_type)
54123

55-
def get_current_mhz(self, clock_id: ClockId = ClockId.CURRENT) -> int:
124+
def get_current_mhz(self, clock_id: ClockId | str = ClockId.CURRENT) -> int:
56125
"""
57126
Get the current clock speed of a specific clock domain, in MHz.
58127

59128
For Kepler™ or newer fully supported devices.
60129

61130
Parameters
62131
----------
63-
clock_id: :class:`ClockId`
64-
The clock ID to query.
132+
clock_id: :class:`ClockId` | str
133+
The clock ID to query. Defaults to the current clock value.
65134

66135
Returns
67136
-------
68137
int
69138
The clock speed in MHz.
70139
"""
140+
try:
141+
clock_id = _CLOCK_ID_MAPPING[clock_id]
142+
except KeyError:
143+
raise ValueError(
144+
f"Invalid clock ID: {clock_id}. "
145+
f"Must be one of {list(ClockId.__members__.values())}"
146+
) from None
71147
return nvml.device_get_clock(self._handle, self._clock_type, clock_id)
72148

73149
def get_max_mhz(self) -> int:
@@ -99,37 +175,41 @@ cdef class ClockInfo:
99175
"""
100176
return nvml.device_get_max_customer_boost_clock(self._handle, self._clock_type)
101177

102-
def get_min_max_clock_of_pstate_mhz(self, pstate: Pstates) -> tuple[int, int]:
178+
def get_min_max_clock_of_pstate_mhz(self, pstate: int) -> tuple[int, int]:
103179
"""
104180
Get the minimum and maximum clock speeds for this clock domain
105181
at a given performance state (Pstate), in MHz.
106182

107183
Parameters
108184
----------
109-
pstate: :class:`Pstates`
110-
The performance state to query.
185+
pstate: int
186+
The performance state to query. Must be an int between 0 and 15,
187+
where 0 is the highest performance state (P0) and 15 is the lowest
188+
(P15).
111189

112190
Returns
113191
-------
114192
tuple[int, int]
115193
A tuple containing the minimum and maximum clock speeds in MHz.
116194
"""
117-
return nvml.device_get_min_max_clock_of_p_state(self._handle, self._clock_type, pstate)
195+
return nvml.device_get_min_max_clock_of_p_state(self._handle, self._clock_type, _pstate_to_enum(pstate))
118196

119-
def get_offsets(self, pstate: Pstates) -> ClockOffsets:
197+
def get_offsets(self, pstate: int) -> ClockOffsets:
120198
"""
121199
Retrieve min, max and current clock offset of some clock domain for a given Pstate.
122200

123201
For Maxwell™ or newer fully supported devices.
124202

125203
Parameters
126204
----------
127-
pstate: :class:`Pstates`
128-
The performance state to query.
205+
pstate: int
206+
The performance state to query. Must be an int between 0 and 15,
207+
where 0 is the highest performance state (P0) and 15 is the lowest
208+
(P15).
129209

130210
Returns
131211
-------
132212
:obj:`~_device.ClockOffsets`
133213
An object with the min, max and current clock offset.
134214
"""
135-
return ClockOffsets(nvml.device_get_clock_offsets(self._handle, self._clock_type, pstate))
215+
return ClockOffsets(nvml.device_get_clock_offsets(self._handle, self._clock_type, _pstate_to_enum(pstate)))

cuda_core/cuda/core/system/_cooler.pxi

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,52 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55

6-
CoolerControl = nvml.CoolerControl
7-
CoolerTarget = nvml.CoolerTarget
6+
class CoolerControl(StrEnum):
7+
"""
8+
Cooler control type.
9+
"""
10+
TOGGLE = "toggle"
11+
VARIABLE = "variable"
12+
13+
14+
CoolerControl.TOGGLE.__doc__ = """
15+
This cooler can only be toggled either ON or OFF (e.g. a switch).
16+
"""
17+
CoolerControl.VARIABLE.__doc__ = """
18+
This cooler's level can be adjusted from some minimum to some maximum (e.g. a knob).
19+
"""
20+
21+
22+
_COOLER_CONTROL_MAPPING = {
23+
nvml.CoolerControl.THERMAL_COOLER_SIGNAL_TOGGLE: CoolerControl.TOGGLE,
24+
nvml.CoolerControl.THERMAL_COOLER_SIGNAL_VARIABLE: CoolerControl.VARIABLE,
25+
}
26+
27+
28+
class CoolerTarget(StrEnum):
29+
"""
30+
Cooler target.
31+
"""
32+
NONE = "none"
33+
GPU = "gpu"
34+
MEMORY = "memory"
35+
POWER_SUPPLY = "power_supply"
36+
# THERMAL_GPU_RELATED is a composite target, so it is omitted here and will
37+
# get returned as 3 separate targets: GPU, MEMORY, and POWER_SUPPLY.
38+
39+
40+
CoolerTarget.NONE.__doc__ = "This cooler controls nothing."
41+
CoolerTarget.GPU.__doc__ = "This cooler can cool the GPU."
42+
CoolerTarget.MEMORY.__doc__ = "This cooler can cool the memory."
43+
CoolerTarget.POWER_SUPPLY.__doc__ = "This cooler can cool the power supply."
44+
45+
46+
_COOLER_TARGET_MAPPING = {
47+
nvml.CoolerTarget.THERMAL_NONE: CoolerTarget.NONE,
48+
nvml.CoolerTarget.THERMAL_GPU: CoolerTarget.GPU,
49+
nvml.CoolerTarget.THERMAL_MEMORY: CoolerTarget.MEMORY,
50+
nvml.CoolerTarget.THERMAL_POWER_SUPPLY: CoolerTarget.POWER_SUPPLY,
51+
}
852

953

1054
cdef class CoolerInfo:
@@ -14,14 +58,13 @@ cdef class CoolerInfo:
1458
self._cooler_info = cooler_info
1559

1660
@property
17-
def signal_type(self) -> CoolerControl:
61+
def signal_type(self) -> CoolerControl | None:
1862
"""
1963
The cooler's control signal characteristics.
2064

21-
The possible types are restricted, variable and toggle. See
22-
:class:`CoolerControl` for details.
65+
The possible types are variable and toggle.
2366
"""
24-
return CoolerControl(self._cooler_info.signal_type)
67+
return _COOLER_CONTROL_MAPPING.get(self._cooler_info.signal_type, None)
2568

2669
@property
2770
def target(self) -> list[CoolerTarget]:
@@ -32,4 +75,11 @@ cdef class CoolerInfo:
3275
:class:`CoolerTarget` for details.
3376
"""
3477
cdef uint64_t[1] targets = [self._cooler_info.target]
35-
return [CoolerTarget(1 << ev) for ev in _unpack_bitmask(targets)]
78+
output_targets = []
79+
for target in _unpack_bitmask(targets):
80+
try:
81+
output_target = _COOLER_TARGET_MAPPING[1 << target]
82+
except KeyError:
83+
raise ValueError(f"Unknown cooler target bit: {1 << target}")
84+
output_targets.append(output_target)
85+
return output_targets

0 commit comments

Comments
 (0)