Skip to content

Commit a40bb81

Browse files
rparolinclaude
andcommitted
feat(cuda.core)!: drop int location shorthand from managed-memory ops (R6, R8)
Per Leo's review on PR #1775 (_managed_buffer.py:165) and Andy's parallel question (line 144), drop the `int` shorthand for prefetch/discard_prefetch/advise locations. The previous design accepted `Device | Host | int` where `int >= 0` meant a device ordinal and `-1` magically meant host. With first-class `Device` and `Host`, the int form was redundant and the `-1 → Host` magic was surprising. Public API change: prefetch(buf, Device(0), stream=...) # was: prefetch(buf, 0, stream=...) prefetch(buf, Host(), stream=...) # was: prefetch(buf, -1, stream=...) This also resolves an inconsistency: ManagedBuffer.preferred_location already accepted only Device | Host | None, but prefetch() and discard_prefetch() accepted int. Now uniformly Device | Host. Pre-1.0 breaking change. Anyone using the int shorthand should switch to the explicit Device(N) / Host() form. Files touched: - _managed_location.py: drop the int branch from _coerce_location; TypeError now reads "Device, Host, or None" - _managed_buffer.py: type signatures `Device | Host | int` → `Device | Host` - _managed_memory_ops.pyx: docstring updates (3 occurrences) - tests/memory/test_managed_ops.py: replace int call sites with Host()/Device(N); collapse three int-branch tests into one test_int_rejected - 1.0.0-notes.rst: drop the "int values are also accepted" sentence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 47d5609 commit a40bb81

5 files changed

Lines changed: 20 additions & 38 deletions

File tree

cuda_core/cuda/core/_memory/_managed_buffer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,14 @@ def accessed_by(self, locations) -> None:
162162
for loc in target - current:
163163
advise(self, _SET_ACCESSED_BY, loc)
164164

165-
def prefetch(self, location: Device | Host | int, *, stream: Stream | GraphBuilder) -> None:
165+
def prefetch(self, location: Device | Host, *, stream: Stream | GraphBuilder) -> None:
166166
"""Prefetch this range to ``location`` on ``stream``."""
167167
prefetch(self, location, stream=stream)
168168

169169
def discard(self, *, stream: Stream | GraphBuilder) -> None:
170170
"""Discard this range's resident pages on ``stream`` (CUDA 13+)."""
171171
discard(self, stream=stream)
172172

173-
def discard_prefetch(self, location: Device | Host | int, *, stream: Stream | GraphBuilder) -> None:
173+
def discard_prefetch(self, location: Device | Host, *, stream: Stream | GraphBuilder) -> None:
174174
"""Discard this range and prefetch to ``location`` on ``stream`` (CUDA 13+)."""
175175
discard_prefetch(self, location, stream=stream)

cuda_core/cuda/core/_memory/_managed_location.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,4 @@ def _coerce_location(value, *, allow_none: bool = False) -> _LocSpec | None:
4848
if allow_none:
4949
return None
5050
raise ValueError("location is required")
51-
if isinstance(value, int):
52-
if value == -1:
53-
return _LocSpec(kind="host")
54-
if value >= 0:
55-
return _LocSpec(kind="device", id=value)
56-
raise ValueError(f"device ordinal must be >= 0 (or -1 for host), got {value}")
57-
raise TypeError(f"location must be a Device, Host, int, or None; got {type(value).__name__}")
51+
raise TypeError(f"location must be a Device, Host, or None; got {type(value).__name__}")

cuda_core/cuda/core/_memory/_managed_memory_ops.pyx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def advise(
239239
``"unset_read_mostly"``, ``"set_preferred_location"``,
240240
``"unset_preferred_location"``, ``"set_accessed_by"``,
241241
``"unset_accessed_by"``) and ``CUmem_advise`` enum values are accepted.
242-
location : :class:`~cuda.core.Device` | :class:`~cuda.core.Host` | int | Sequence[...]
242+
location : :class:`~cuda.core.Device` | :class:`~cuda.core.Host` | Sequence[...]
243243
Target location(s). Required for advice values that consult a
244244
location; ignored (may be ``None``) for ``set_read_mostly``,
245245
``unset_read_mostly``, and ``unset_preferred_location``. A sequence
@@ -313,7 +313,7 @@ def prefetch(
313313
----------
314314
targets : :class:`Buffer` | Sequence[:class:`Buffer`]
315315
One or more managed allocations to operate on.
316-
location : :class:`~cuda.core.Device` | :class:`~cuda.core.Host` | int | Sequence[...]
316+
location : :class:`~cuda.core.Device` | :class:`~cuda.core.Host` | Sequence[...]
317317
Target location(s). A single location applies to all targets; a
318318
sequence must match ``len(targets)``. ``int`` values are coerced
319319
to a location (``-1`` maps to host, ``>=0`` to that device ordinal).
@@ -407,7 +407,7 @@ def discard_prefetch(
407407
----------
408408
targets : :class:`Buffer` | Sequence[:class:`Buffer`]
409409
One or more managed allocations to discard and re-prefetch.
410-
location : :class:`~cuda.core.Device` | :class:`~cuda.core.Host` | int | Sequence[...]
410+
location : :class:`~cuda.core.Device` | :class:`~cuda.core.Host` | Sequence[...]
411411
Target location(s). A single location applies to all targets;
412412
a sequence must match ``len(targets)``.
413413
options : :class:`DiscardPrefetchOptions`, optional

cuda_core/docs/source/release/1.0.0-notes.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ New features
4343
single managed :class:`Buffer` or a sequence; with cuda.bindings 12.8+
4444
the N>1 case dispatches to the corresponding ``cuMem*BatchAsync`` driver
4545
entry point, addressing the managed-memory portion of #1333. Locations
46-
are expressed via :class:`Device` or :class:`Host`; ``int`` values are
47-
also accepted (``-1`` maps to host, ``>=0`` to that device ordinal).
46+
are expressed via :class:`Device` or :class:`Host`.
4847
Per-call options use frozen dataclasses
4948
(:class:`~utils.AdviseOptions`, :class:`~utils.PrefetchOptions`,
5049
:class:`~utils.DiscardOptions`, :class:`~utils.DiscardPrefetchOptions`)

cuda_core/tests/memory/test_managed_ops.py

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_managed_memory_prefetch_supports_managed_pool_allocations(init_cuda):
6868
buffer = mr.allocate(_MANAGED_TEST_ALLOCATION_SIZE)
6969
stream = device.create_stream()
7070

71-
utils.prefetch(buffer, _HOST_LOCATION_ID, stream=stream)
71+
utils.prefetch(buffer, Host(), stream=stream)
7272
stream.sync()
7373
last_location = _get_int_mem_range_attr(
7474
buffer,
@@ -143,7 +143,7 @@ def test_managed_memory_discard_prefetch_supports_managed_pool_allocations(init_
143143
buffer = mr.allocate(_MANAGED_TEST_ALLOCATION_SIZE)
144144
stream = device.create_stream()
145145

146-
utils.prefetch(buffer, _HOST_LOCATION_ID, stream=stream)
146+
utils.prefetch(buffer, Host(), stream=stream)
147147
stream.sync()
148148

149149
utils.discard_prefetch(buffer, device, stream=stream)
@@ -166,7 +166,7 @@ def test_managed_memory_discard_prefetch_supports_external_managed_allocations(i
166166
buffer = DummyUnifiedMemoryResource(device).allocate(_MANAGED_TEST_ALLOCATION_SIZE)
167167
stream = device.create_stream()
168168

169-
utils.prefetch(buffer, _HOST_LOCATION_ID, stream=stream)
169+
utils.prefetch(buffer, Host(), stream=stream)
170170
stream.sync()
171171

172172
utils.discard_prefetch(buffer, device, stream=stream)
@@ -227,8 +227,8 @@ def test_managed_memory_advise_location_validation(init_cuda):
227227
# set_read_mostly works without a location (location is ignored)
228228
utils.advise(buffer, "set_read_mostly")
229229

230-
# set_preferred_location requires a location; device ordinal works
231-
utils.advise(buffer, "set_preferred_location", device.device_id)
230+
# set_preferred_location requires a location; Device works
231+
utils.advise(buffer, "set_preferred_location", device)
232232

233233
# set_preferred_location with host location
234234
utils.advise(buffer, "set_preferred_location", Host())
@@ -241,9 +241,9 @@ def test_managed_memory_advise_location_validation(init_cuda):
241241
with pytest.raises(ValueError, match="does not support location_type='host_numa_current'"):
242242
utils.advise(buffer, "set_accessed_by", Host.numa_current())
243243

244-
# Inferred location from int: -1 maps to host, 0 maps to device
245-
utils.advise(buffer, "set_preferred_location", -1)
246-
utils.advise(buffer, "set_preferred_location", 0)
244+
# Both Host and Device locations are accepted
245+
utils.advise(buffer, "set_preferred_location", Host())
246+
utils.advise(buffer, "set_preferred_location", Device(0))
247247

248248
buffer.close()
249249

@@ -356,18 +356,6 @@ def test_host_numa_current_passthrough(self):
356356
spec = _coerce_location(Host.numa_current())
357357
assert spec.kind == "host_numa_current"
358358

359-
def test_int_device(self):
360-
from cuda.core._memory._managed_location import _coerce_location
361-
362-
spec = _coerce_location(0)
363-
assert spec.kind == "device"
364-
assert spec.id == 0
365-
366-
def test_int_minus_one_is_host(self):
367-
from cuda.core._memory._managed_location import _coerce_location
368-
369-
assert _coerce_location(-1).kind == "host"
370-
371359
def test_none_when_disallowed(self):
372360
from cuda.core._memory._managed_location import _coerce_location
373361

@@ -379,16 +367,17 @@ def test_none_when_allowed(self):
379367

380368
assert _coerce_location(None, allow_none=True) is None
381369

382-
def test_bad_int(self):
370+
def test_int_rejected(self):
383371
from cuda.core._memory._managed_location import _coerce_location
384372

385-
with pytest.raises(ValueError, match="device ordinal"):
386-
_coerce_location(-2)
373+
# int shorthand was removed in favor of explicit Device/Host
374+
with pytest.raises(TypeError, match="Device, Host, or None"):
375+
_coerce_location(0)
387376

388377
def test_bad_type(self):
389378
from cuda.core._memory._managed_location import _coerce_location
390379

391-
with pytest.raises(TypeError, match="Device, Host, int, or None"):
380+
with pytest.raises(TypeError, match="Device, Host, or None"):
392381
_coerce_location("device")
393382

394383

0 commit comments

Comments
 (0)