Coverage for cuda/core/_memoryview.pyx: 85.11%
712 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-29 01:38 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-29 01:38 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
5from __future__ import annotations
7cimport cython
8from ._dlpack cimport *
9from ._dlpack import classify_dl_device
10from libc.stdint cimport intptr_t
11from cuda.core._layout cimport _StridedLayout, get_strides_ptr
12from cuda.core._stream import Stream
14import ctypes
15import functools
16import sys
17import warnings
18from collections.abc import Callable # no-cython-lint # used in string annotations below
19from typing import Any # no-cython-lint # used in string annotations below
21import numpy
23from cuda.bindings cimport cydriver
24from cuda.core._resource_handles cimport (
25 EventHandle,
26 create_event_handle_noctx,
27 as_cu,
28)
30from cuda.core._utils.cuda_utils import handle_return, driver
31from cuda.core._utils.cuda_utils cimport HANDLE_RETURN
34from cuda.core._memory import Buffer
37# ---------------------------------------------------------------------------
38# Lazy tensor bridge (avoids loading _tensor_bridge.so until torch is used)
39# ---------------------------------------------------------------------------
41cdef object _tensor_bridge = None
42# Cache: type(obj) -> True/False for the torch tensor check.
43# Once a type is seen, we never re-check.
44cdef dict _torch_type_cache = {}
45# Tri-state: None = not checked, True/False = result of version check
46cdef object _torch_version_ok = None
48cdef inline bint _torch_version_check():
49 """Return True if 2.3 <= torch <= 2.12 (known AOTI ABI range). Memoized.
51 Lower bound: AOTI functions we use were introduced in PyTorch 2.3.
52 Upper bound: the ``pyobj_to_aten_handle`` trick relies on the
53 THPVariable struct layout (PyObject_HEAD followed by at::Tensor cdata)
54 and the identity ``AtenTensorHandle == at::Tensor*``. Both are
55 undocumented internals that could change in a future PyTorch version.
56 We cap at the latest version we have tested against; unknown versions
57 fall back to the standard DLPack/CAI paths. Bump the upper bound
58 after verifying a new PyTorch release.
59 """
60 global _torch_version_ok
61 if _torch_version_ok is not None:
62 return <bint>_torch_version_ok
63 torch = sys.modules.get("torch")
64 if torch is None:
65 _torch_version_ok = False
66 return False
67 try:
68 major, minor = int(torch.__version__.split(".")[0]), \
69 int(torch.__version__.split(".")[1])
70 _torch_version_ok = (2, 3) <= (major, minor) <= (2, 12)
71 except (ValueError, IndexError):
72 _torch_version_ok = False
73 return <bint>_torch_version_ok
76cdef inline bint _is_torch_tensor(object obj):
77 cdef type tp = type(obj) 24 5 Y ! X R L M N O P S T U V W x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbab8 9 i K 6 J 7 D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
78 cdef object cached = _torch_type_cache.get(tp) 24 5 Y ! X R L M N O P S T U V W x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbab8 9 i K 6 J 7 D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
79 if cached is not None: 24 5 Y ! X R L M N O P S T U V W x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbab8 9 i K 6 J 7 D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
80 return <bint>cached 2! X R L M N O P S T U V W z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a
81 cdef str mod = tp.__module__ or "" 24 5 Y x / Kbab7 b h a Ob
82 cdef bint result = mod.startswith("torch") and hasattr(obj, "data_ptr") \ 24 5 Y x / Kbab7 b h a Ob
83 and _torch_version_check()
84 _torch_type_cache[tp] = result # setdefault not needed for bools 24 5 Y x / Kbab7 b h a Ob
85 return result 24 5 Y x / Kbab7 b h a Ob
88cdef object _get_tensor_bridge():
89 """Bootstrap AOTI symbols, then import _tensor_bridge on first use."""
90 global _tensor_bridge
91 if _tensor_bridge is not None:
92 return _tensor_bridge
93 torch_C = sys.modules.get("torch._C")
94 if torch_C is None:
95 raise RuntimeError(
96 "torch._C is not loaded; cannot initialise the tensor bridge. "
97 "Make sure PyTorch is imported before passing a torch.Tensor.")
98 ctypes.CDLL(torch_C.__file__, mode=ctypes.RTLD_GLOBAL)
99 from cuda.core import _tensor_bridge as tb
100 _tensor_bridge = tb
101 return _tensor_bridge
104try:
105 from ml_dtypes import bfloat16
106except ImportError:
107 bfloat16 = None
109# TODO(leofang): support NumPy structured dtypes
112cdef extern from "Python.h":
113 ctypedef struct PyTypeObject:
114 void* tp_dict
115 void PyType_Modified(PyTypeObject*)
118cdef DLPackExchangeAPI _SMV_DLPACK_EXCHANGE_API
119cdef bint _SMV_DLPACK_EXCHANGE_API_INITED = False
120_SMV_DLPACK_EXCHANGE_API_CAPSULE = cpython.PyCapsule_New(
121 <void*>&_SMV_DLPACK_EXCHANGE_API,
122 b"dlpack_exchange_api",
123 NULL,
124)
127cdef class StridedMemoryView:
128 """A class holding metadata of a strided dense array/tensor.
130 A :obj:`StridedMemoryView` instance can be created in three ways:
132 1. Using the :obj:`args_viewable_as_strided_memory` decorator (recommended)
133 2. Explicit construction relying on DLPack or CUDA Array Interface, see below.
134 3. From :obj:`~_memory.Buffer` and shape and size tuples (see
135 :meth:`from_buffer` classmethod)
137 ``StridedMemoryView(obj, stream_ptr)`` can be used to create a view from
138 objects supporting either DLPack (up to v1.0) or CUDA Array Interface
139 (CAI) v3. When wrapping an arbitrary object it will try the DLPack protocol
140 first, then the CAI protocol. A :obj:`BufferError` is raised if neither is
141 supported.
143 Since either way would take a consumer stream, for DLPack it is passed to
144 ``obj.__dlpack__()`` as-is (except for :obj:`None`, see below); for CAI, a
145 stream order will be established between the consumer stream and the
146 producer stream (from ``obj.__cuda_array_interface__()["stream"]``), as if
147 ``cudaStreamWaitEvent`` is called by this method.
149 To opt-out of the stream ordering operation in either DLPack or CAI,
150 please pass ``stream_ptr=-1``. Note that this deviates (on purpose)
151 from the semantics of ``obj.__dlpack__(stream=None, ...)`` since ``cuda.core``
152 does not encourage using the (legacy) default/null stream, but is
153 consistent with the CAI's semantics. For DLPack, ``stream=-1`` will be
154 internally passed to ``obj.__dlpack__()`` instead.
156 Parameters
157 ----------
158 obj : Any
159 Any objects that supports either DLPack (up to v1.0) or CUDA Array
160 Interface (v3).
161 stream_ptr: int
162 The pointer address (as Python `int`) to the **consumer** stream.
163 Stream ordering will be properly established unless ``-1`` is passed.
166 Attributes
167 -----------
168 ptr : int
169 Pointer to the tensor buffer (as a Python `int`).
170 device_id : int
171 The device ID for where the tensor is located. It is -1 for CPU tensors
172 (meaning those only accessible from the host).
173 is_device_accessible : bool
174 Whether the tensor data can be accessed on the GPU.
175 readonly: bool
176 Whether the tensor data can be modified in place.
177 exporting_obj : Any
178 A reference to the original tensor object that is being viewed.
179 If the view is created with :meth:`from_buffer`,
180 it will be the Buffer instance passed to the method.
182 """
183 def __init__(self, obj: object = None, stream_ptr: int | None = None) -> None:
184 cdef str clsname = self.__class__.__name__ 2Z 0 1 2 3 Qbbb
185 if obj is not None: 2B Z 0 1 2 3 Qbbb
186 # populate self's attributes
187 if check_has_dlpack(obj): 2Z 0 1 2 3 bb
188 warnings.warn( 1Z0123
189 f"Constructing a {clsname} directly from a DLPack-supporting object is deprecated; " 1Z0123
190 "Use `StridedMemoryView.from_dlpack` or `StridedMemoryView.from_any_interface` instead.",
191 DeprecationWarning, 1Z0123
192 stacklevel=2,
193 )
194 view_as_dlpack(obj, stream_ptr, self) 1BZ0123
195 else:
196 warnings.warn( 2bb
197 f"Constructing a {clsname} directly from a CUDA-array-interface-supporting object is deprecated; " 2bb
198 "Use `StridedMemoryView.from_cuda_array_interface` or `StridedMemoryView.from_any_interface` instead.",
199 DeprecationWarning, 2bb
200 stacklevel=2,
201 )
202 view_as_cai(obj, stream_ptr, self) 2bb
203 else:
204 warnings.warn( 2Qb
205 f"Constructing an empty {clsname} is deprecated; " 2Qb
206 "use one of the classmethods `from_dlpack`, `from_cuda_array_interface` or `from_any_interface` "
207 "to construct a StridedMemoryView from an object",
208 DeprecationWarning, 2Qb
209 stacklevel=2,
210 )
212 @classmethod
213 def from_dlpack(cls, obj: object, stream_ptr: int | None=None) -> StridedMemoryView:
214 """Create a view from an object supporting the `DLPack <https://dmlc.github.io/dlpack/latest/>`_ protocol.
216 Parameters
217 ----------
218 obj : object
219 An object implementing the `DLPack <https://dmlc.github.io/dlpack/latest/>`_ protocol
220 (via ``__dlpack__``).
221 stream_ptr : int, optional
222 Stream pointer for synchronization. If ``None``, no synchronization is performed.
223 """
224 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 24 5 Y ! X R L M N O P S T U V W z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
225 if _is_torch_tensor(obj): 24 5 Y ! X R L M N O P S T U V W z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
226 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf)
227 return buf
228 view_as_dlpack(obj, stream_ptr, buf) 24 5 Y ! X R L M N O P S T U V W z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
229 return buf 145Y!XRLMNOPSTUVWzAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
231 @classmethod
232 def from_cuda_array_interface(cls, obj: object, stream_ptr: int | None=None) -> StridedMemoryView:
233 """Create a view from an object supporting the `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_ protocol.
235 Parameters
236 ----------
237 obj : object
238 An object implementing the `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_ protocol.
239 stream_ptr : int, optional
240 Stream pointer for synchronization. If ``None``, no synchronization is performed.
241 """
242 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 2x / Kbab7
243 if _is_torch_tensor(obj): 2x / Kbab7
244 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf)
245 return buf
246 view_as_cai(obj, stream_ptr, buf) 2B x / Kbab7
247 return buf 2x / ab7
249 @classmethod
250 def from_array_interface(cls, obj: object) -> StridedMemoryView:
251 """Create a view from an object supporting the `__array_interface__ <https://numpy.org/doc/stable/reference/arrays.interface.html>`_ protocol.
253 Parameters
254 ----------
255 obj : object
256 An object implementing the `__array_interface__ <https://numpy.org/doc/stable/reference/arrays.interface.html>`_ protocol (e.g., a numpy array).
257 """
258 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 2B $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
259 if _is_torch_tensor(obj): 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
260 _get_tensor_bridge().view_as_torch_tensor(obj, None, buf)
261 return buf
262 view_as_array_interface(obj, buf) 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
263 return buf 1B$%:';(=)*+,?@[]^_`-{|}~.
265 @classmethod
266 def from_any_interface(cls, obj: object, stream_ptr: int | None = None) -> StridedMemoryView:
267 """Create a view by automatically selecting the best available protocol.
269 Tries `DLPack <https://dmlc.github.io/dlpack/latest/>`_ first, then falls back to
270 `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_.
271 ``torch.Tensor`` objects are transparently handled via a fast AOTI path
272 regardless of which protocol is selected.
274 Parameters
275 ----------
276 obj : object
277 An object implementing `DLPack <https://dmlc.github.io/dlpack/latest/>`_ or
278 `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_.
279 stream_ptr : int, optional
280 Stream pointer for synchronization. If ``None``, no synchronization is performed.
281 """
282 if check_has_dlpack(obj): 24 5 Y ! X R S T U V W 1bz A y 8 9 i K 6 J C c u e f j k l q r s t m n o p g w v d b h a
283 return cls.from_dlpack(obj, stream_ptr) 145Y!XRSTUVWzAy89iK6JCcuefjklqrstmnopgwvdbha
284 return cls.from_cuda_array_interface(obj, stream_ptr)
286 @classmethod
287 def from_buffer(
288 cls,
289 buffer : Buffer,
290 shape : tuple[int, ...],
291 strides : tuple[int, ...] | None = None,
292 *,
293 itemsize : int | None = None,
294 dtype : numpy.dtype | None = None,
295 is_readonly : bool = False
296 ) -> StridedMemoryView:
297 """
298 Creates a :obj:`StridedMemoryView` instance from a :obj:`~_memory.Buffer` and shape and strides tuples.
299 The Buffer can be either allocation coming from a :obj:`MemoryResource` or an external allocation
300 wrapped in a :obj:`~_memory.Buffer` object with ``Buffer.from_handle(ptr, size, owner=...)``.
302 .. caution::
303 When creating a :obj:`StridedMemoryView` from a :obj:`~_memory.Buffer`,
304 no synchronization is performed. It is the user's responsibility to ensure
305 the data in ``buffer`` is properly synchronized when consuming the view.
307 Parameters
308 ----------
309 buffer : :obj:`~_memory.Buffer`
310 The buffer to create the view from.
311 shape : :obj:`tuple`
312 The layout describing the shape, strides and itemsize of the elements in
313 the buffer.
314 strides : :obj:`tuple`
315 The layout describing the shape, strides and itemsize of the elements in
316 the buffer.
317 dtype : :obj:`numpy.dtype`
318 Optional dtype.
319 If specified, the dtype's itemsize must match the layout's itemsize.
320 is_readonly : bool, optional
321 Whether the mark the view as readonly.
322 """
323 cdef StridedMemoryView view = StridedMemoryView.__new__(cls) 2TbUbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbWbSbcbdbPbLbQ #
324 if itemsize is None and dtype is None: 2TbUbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbWbSbcbdbPbLbQ #
325 raise ValueError("Either itemsize or dtype must be specified") 2Wb
326 if itemsize is not None and dtype is not None and itemsize != dtype.itemsize: 2TbUbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbSbcbdbPbLbQ #
327 raise ValueError( 2Sb
328 f"itemsize ({itemsize}) does not match dtype.itemsize ({dtype.itemsize})" 2Sb
329 )
330 # (itemsize is None XOR dtype is None) OR they are equal
331 view_buffer_strided( 2ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbQ #
332 view,
333 buffer,
334 _StridedLayout(shape=shape, strides=strides, itemsize=getattr(dtype, "itemsize", itemsize)), 2TbUbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbQ #
335 dtype,
336 is_readonly,
337 )
338 return view 2B ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ #
340 def __dealloc__(self) -> None:
341 if self.dl_tensor == NULL: 2TbUb4 5 Y ! X R L M N O P S T U V W Z 0 1 2 3 x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . MbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbWbSbcbdbPbLb/ Kbab8 9 i Q Qbbb# K 6 J 7 D E F G H I C u e f j k l q r s t m n o p g w v d Nbb h a Ob
342 return 2TbUbx z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . MbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbWbSbcbdbPbLb/ KbabQ Qbbb# 6 J 7 D E F G H I NbOb
344 if cpython.PyCapsule_IsValid( 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvdbha
345 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME): 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvdbha
346 data = cpython.PyCapsule_GetPointer( 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvdha
347 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME) 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvdha
348 dlm_tensor_ver = <DLManagedTensorVersioned*>data 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvdha
349 if dlm_tensor_ver.deleter != NULL: 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvdha
350 dlm_tensor_ver.deleter(dlm_tensor_ver) 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICuefjklqrstmnopgwvda
351 elif cpython.PyCapsule_IsValid( 1Bba
352 self.metadata, DLPACK_TENSOR_USED_NAME): 1ba
353 data = cpython.PyCapsule_GetPointer( 1ba
354 self.metadata, DLPACK_TENSOR_USED_NAME) 1ba
355 dlm_tensor = <DLManagedTensor*>data 1ba
356 if dlm_tensor.deleter != NULL: 1ba
357 dlm_tensor.deleter(dlm_tensor) 1a
359 def view(
360 self, layout : _StridedLayout | None = None, dtype : numpy.dtype | None = None
361 ) -> StridedMemoryView:
362 """
363 Creates a new view with adjusted layout and dtype.
364 Same as calling :meth:`from_buffer` with the current buffer.
365 """
366 cdef StridedMemoryView view = StridedMemoryView.__new__(self.__class__) 2z A y cbdb6 J D E F G H I
367 if layout is None and dtype is None: 2z A y cbdb6 J D E F G H I
368 return self 16
369 if layout is None: 2z A y cbdbJ D E F G H I
370 layout = self.get_layout() 1BzAyJ
371 if dtype is None: 2z A y cbdbJ D E F G H I
372 dtype = self.get_dtype() 2cbdbD E F G H I
373 view_buffer_strided(view, self.get_buffer(), layout, dtype, self.readonly) 2z A y cbdbJ D E F G H I
374 return view 2z A y cbdbJ D E F G H I
376 def as_tensor_map(
377 self,
378 box_dim: tuple[int, ...] | None = None,
379 *,
380 options: object = None,
381 element_strides: tuple[int, ...] | None = None,
382 data_type: object = None,
383 interleave: object = None,
384 swizzle: object = None,
385 l2_promotion: object = None,
386 oob_fill: object = None,
387 ) -> object:
388 """Create a tiled :obj:`TensorMapDescriptor` from this view.
390 This is the public entry point for creating tiled tensor map
391 descriptors in ``cuda.core``. Pass either ``box_dim`` and the
392 individual keyword arguments directly, or provide bundled tiled
393 options via ``options=``.
394 """
395 from cuda.core._tensor_map import TensorMapDescriptor 1R
397 kwargs = {} 1R
398 if options is not None: 1R
399 kwargs["options"] = options
400 if element_strides is not None: 1R
401 kwargs["element_strides"] = element_strides 1BR
402 if data_type is not None: 1R
403 kwargs["data_type"] = data_type 1R
404 if interleave is not None: 1R
405 kwargs["interleave"] = interleave
406 if swizzle is not None: 1R
407 kwargs["swizzle"] = swizzle 1R
408 if l2_promotion is not None: 1R
409 kwargs["l2_promotion"] = l2_promotion 1R
410 if oob_fill is not None: 1R
411 kwargs["oob_fill"] = oob_fill 1R
412 return TensorMapDescriptor._from_tiled(self, box_dim, **kwargs) 1R
414 def copy_from(
415 self,
416 other: StridedMemoryView,
417 stream: Stream,
418 allocator: object = None,
419 blocking: bool | None = None,
420 ) -> None:
421 """
422 Copies the data from the other view into this view.
424 The copy can be performed between following memory spaces:
425 host-to-device, device-to-host, device-to-device (on the same device).
427 Parameters
428 ----------
429 other : StridedMemoryView
430 The view to copy data from.
431 stream : Stream | None, optional
432 The stream to schedule the copy on.
433 allocator : MemoryResource | None, optional
434 If temporary buffers are needed, the specified memory resources
435 will be used to allocate the memory. If not specified, default
436 resources will be used.
437 blocking : bool | None, optional
438 Whether the call should block until the copy is complete.
439 * ``True``: the ``stream`` is synchronized with the host at the end of the call,
440 blocking until the copy is complete.
441 * ``False``: if possible, the call returns immediately once the copy is scheduled.
442 However, in some cases of host-to-device or device-to-host copies, the call may
443 still synchronize with the host if necessary.
444 * ``None`` (default):
445 * for device-to-device, it defaults to ``False`` (non-blocking),
446 * for host-to-device or device-to-host, it defaults to ``True`` (blocking).
447 """
448 raise NotImplementedError("Sorry, not supported: copy_from") 18
450 def copy_to(
451 self,
452 other: StridedMemoryView,
453 stream: Stream | None = None,
454 allocator: object = None,
455 blocking: bool | None = None,
456 ) -> None:
457 """
458 Copies the data from this view into the ``other`` view.
460 For details, see :meth:`copy_from`.
461 """
462 raise NotImplementedError("Sorry, not supported: copy_to") 19
464 def __dlpack__(
465 self,
466 *,
467 stream: int | None = None,
468 max_version: tuple[int, int] | None = None,
469 dl_device: tuple[int, int] | None = None,
470 copy: bool | None = None,
471 ) -> object:
472 # Similar to Buffer.__dlpack__: no implicit synchronization is performed.
473 if dl_device is not None: 1YxzAyiQuefjklqrstmnopgwvdbha
474 raise BufferError("Sorry, not supported: dl_device other than None") 1Y
475 if copy is True: 1YxzAyiQuefjklqrstmnopgwvdbha
476 raise BufferError("Sorry, not supported: copy=True") 1Y
478 cdef bint versioned
479 if max_version is None: 1YxzAyiQuefjklqrstmnopgwvdbha
480 versioned = False 1xzAyQvba
481 else:
482 if not isinstance(max_version, tuple) or len(max_version) != 2: 1Yiuefjklqrstmnopgwdha
483 raise BufferError(f"Expected max_version tuple[int, int], got {max_version}") 1Y
484 versioned = max_version >= (1, 0) 1iuefjklqrstmnopgwdha
486 # NOTE: stream is accepted for protocol compatibility but not used.
487 cdef object capsule = _smv_make_py_capsule(self, versioned) 1xzAyiQuefjklqrstmnopgwvdbha
488 return capsule 1xiuefjklqrstmnopgwvdbha
490 def __dlpack_device__(self) -> tuple[int, int]:
491 cdef _DLDeviceType device_type
492 cdef int32_t device_id
493 _smv_get_dl_device(self, &device_type, &device_id) 145xibha
494 return (<int>device_type, int(device_id)) 145xibha
496 @property
497 def _layout(self) -> _StridedLayout:
498 """
499 The layout of the tensor. For StridedMemoryView created from DLPack or CAI,
500 the layout is inferred from the tensor object's metadata.
501 """
502 return self.get_layout() 2ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbQbJ D E F G H I
504 @property
505 def size(self) -> int:
506 return self.get_layout().get_volume() 1LMNOPSTUVWZ0123$%:';(=)*+,?@[]^_`-{|}~./
508 @property
509 def shape(self) -> tuple[int, ...]:
510 """
511 Shape of the tensor.
512 """
513 return self.get_layout().get_shape_tuple() 2X L M N O P S T U V W Z 0 1 2 3 $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdb/ abbb# K 7 D E F G H I c
515 @property
516 def strides(self) -> tuple[int, ...] | None:
517 """
518 Strides of the tensor (in **counts**, not bytes).
519 """
520 return self.get_layout().get_strides_tuple() 2X L M N O P S T U V W Z 0 1 2 3 $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJb/ ab# K
522 @property
523 def dtype(self) -> numpy.dtype | None:
524 """
525 Data type of the tensor.
527 Supports standard NumPy dtypes as well as narrow data types (e.g., ``bfloat16``)
528 when the optional `ml_dtypes <https://github.com/jax-ml/ml_dtypes>`_ package is
529 installed. If ``ml_dtypes`` is not available and such a tensor is encountered,
530 a :obj:`NotImplementedError` will be raised.
531 """
532 return self.get_dtype() 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbLb# K J D E F G H I
534 def __repr__(self) -> str:
535 return (f"StridedMemoryView(ptr={self.ptr},\n" 1#K
536 + f" shape={self.shape},\n" 1#K
537 + f" strides={self.strides},\n" 1#K
538 + f" itemsize={self._layout.itemsize},\n" 1#K
539 + f" dtype={get_simple_repr(self.dtype)},\n" 1#K
540 + f" device_id={self.device_id},\n" 1#K
541 + f" is_device_accessible={self.is_device_accessible},\n" 1B#K
542 + f" readonly={self.readonly},\n" 1#K
543 + f" exporting_obj={get_simple_repr(self.exporting_obj)})") 1#K
545 @cython.critical_section
546 cdef inline _StridedLayout get_layout(self):
547 if self._layout is None: 2X L M N O P S T U V W Z 0 1 2 3 x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . MbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdb/ Kbabi Q Qbbb# K J 7 D E F G H I C c u e f j k l q r s t m n o p g w v d b h a
548 if self.dl_tensor: 2B X L M N O P S T U V W Z 0 1 2 3 x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabi QbbbK J 7 D E F G H I C c u e f j k l q r s t m n o p g w v d b h a
549 self._layout = layout_from_dlpack(self.dl_tensor) 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
550 elif self.metadata is not None: 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ KbabQbbb7
551 self._layout = layout_from_cai(self.metadata) 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabbb7
552 else:
553 raise ValueError("Cannot infer layout from the exporting object") 2Qb
554 return self._layout 2X L M N O P S T U V W Z 0 1 2 3 x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdb/ abi Q bb# K J 7 D E F G H I C c u e f j k l q r s t m n o p g w v d b h a
556 @cython.critical_section
557 cdef inline object get_buffer(self):
558 """
559 Returns Buffer instance with the underlying data.
560 If the SMV was created from a Buffer, it will return the same Buffer instance.
561 Otherwise, it will create a new instance with owner set to the exporting object.
562 """
563 if self._buffer is None: 2x z A y cbdbJ D E F G H I
564 if isinstance(self.exporting_obj, Buffer): 1xzAyJDEFGHI
565 self._buffer = self.exporting_obj
566 else:
567 self._buffer = Buffer.from_handle(self.ptr, 0, owner=self.exporting_obj) 1xzAyJDEFGHI
568 return self._buffer 2x z A y cbdbJ D E F G H I
570 @cython.critical_section
571 cdef inline object get_dtype(self):
572 if self._dtype is None: 2x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbi Q # K J D E F G H I C c u e f j k l q r s t m n o p g w v d b h a
573 if self.dl_tensor != NULL: 1x$%:';(=)*+,?@[]^_`-{|}~.iQ#KDEFGHICcuefjklqrstmnopgwvdbha
574 self._dtype = dtype_dlpack_to_numpy(&self.dl_tensor.dtype) 1iKDEFGHICcuefjklqrstmnopgwvdbha
575 elif isinstance(self.metadata, int): 1x$%:';(=)*+,?@[]^_`-{|}~.Q#
576 # AOTI dtype code stored by the torch tensor bridge
577 self._dtype = _get_tensor_bridge().resolve_aoti_dtype(
578 self.metadata)
579 elif self.metadata is not None: 1x$%:';(=)*+,?@[]^_`-{|}~.Q#
580 self._dtype = _typestr2dtype(self.metadata["typestr"]) 1x$%:';(=)*+,?@[]^_`-{|}~.
581 return self._dtype 2x z A y $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbi Q # K J D E F G H I C c u e f j k l q r s t m n o p g w v d b h a
584cdef void _smv_pycapsule_deleter(object capsule) noexcept:
585 cdef DLManagedTensor* dlm_tensor
586 cdef DLManagedTensorVersioned* dlm_tensor_ver
587 # Do not invoke the deleter on a used capsule.
588 if cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME): 1xiuefjklqrstmnopgwvda
589 dlm_tensor = <DLManagedTensor*>( 1xv
590 cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME) 1xv
591 )
592 if dlm_tensor.deleter: 1xv
593 dlm_tensor.deleter(dlm_tensor) 1xv
594 elif cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1iuefjklqrstmnopgwda
595 dlm_tensor_ver = <DLManagedTensorVersioned*>( 1Bd
596 cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 1d
597 )
598 if dlm_tensor_ver.deleter: 1d
599 dlm_tensor_ver.deleter(dlm_tensor_ver) 1d
602cdef inline void _smv_release_export_resources(void* manager_ctx, int64_t* shape_ptr) noexcept with gil:
603 if shape_ptr: 1xzAyiQuefjklqrstmnopgwvdbha
604 stdlib.free(shape_ptr) 1xiuefjklqrstmnopgvdbha
605 if manager_ctx: 1xzAyiQuefjklqrstmnopgwvdbha
606 cpython.Py_DECREF(<object>manager_ctx) 1xzAyiQuefjklqrstmnopgwvdbha
609cdef void _smv_deleter(DLManagedTensor* tensor) noexcept with gil:
610 if tensor: 1xzAyQvba
611 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1xzAyQvba
612 tensor.manager_ctx = NULL 1xzAyQvba
613 stdlib.free(tensor) 1BxzAyQvba
616cdef void _smv_versioned_deleter(DLManagedTensorVersioned* tensor) noexcept with gil:
617 if tensor: 1zAyiQuefjklqrstmnopgwdha
618 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1iuefjklqrstmnopgwdha
619 tensor.manager_ctx = NULL 1iuefjklqrstmnopgwdha
620 stdlib.free(tensor) 1iuefjklqrstmnopgwdha
623cdef inline DLManagedTensorVersioned* _smv_allocate_dlm_tensor_versioned() except? NULL:
624 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1icuefjklqrstmnopgwdha
625 dlm_tensor_ver = <DLManagedTensorVersioned*>stdlib.malloc(sizeof(DLManagedTensorVersioned)) 1icuefjklqrstmnopgwdha
626 if dlm_tensor_ver == NULL: 1icuefjklqrstmnopgwdha
627 raise MemoryError()
628 dlm_tensor_ver.dl_tensor.shape = NULL 1icuefjklqrstmnopgwdha
629 dlm_tensor_ver.manager_ctx = NULL 1icuefjklqrstmnopgwdha
630 return dlm_tensor_ver 1icuefjklqrstmnopgwdha
633cdef inline DLManagedTensor* _smv_allocate_dlm_tensor() except? NULL:
634 cdef DLManagedTensor* dlm_tensor = NULL 1xzAyQvba
635 dlm_tensor = <DLManagedTensor*>stdlib.malloc(sizeof(DLManagedTensor)) 1xzAyQvba
636 if dlm_tensor == NULL: 1xzAyQvba
637 raise MemoryError()
638 dlm_tensor.dl_tensor.shape = NULL 1xzAyQvba
639 dlm_tensor.manager_ctx = NULL 1xzAyQvba
640 return dlm_tensor 1BxzAyQvba
643cdef inline int _smv_dtype_numpy_to_dlpack(object dtype_obj, DLDataType* out_dtype) except -1:
644 cdef object np_dtype = numpy.dtype(dtype_obj) 1xzAyiCcuefjklqrstmnopgwvdbha
645 if np_dtype.fields is not None: 1xzAyiCcuefjklqrstmnopgwvdbha
646 raise BufferError("Structured dtypes are not supported for DLPack export") 1A
647 if not np_dtype.isnative and np_dtype.byteorder not in ("=", "|"): 1xzyiCcuefjklqrstmnopgwvdbha
648 raise BufferError("Non-native-endian dtypes are not supported for DLPack export") 1Bz
650 cdef str kind = np_dtype.kind 1xyiCcuefjklqrstmnopgwvdbha
651 cdef int bits = np_dtype.itemsize * 8 1xyiCcuefjklqrstmnopgwvdbha
652 cdef uint8_t code
653 if kind == "b": 1xyiCcuefjklqrstmnopgwvdbha
654 if bits != 8: 1u
655 raise BufferError(f"Unsupported bool dtype itemsize: {np_dtype.itemsize}")
656 code = <uint8_t>kDLBool 1u
657 elif kind == "i": 1xyiCcefjklqrstmnopgwvdbha
658 if bits not in (8, 16, 32, 64): 1iCqrstvdbha
659 raise BufferError(f"Unsupported signed integer dtype: {np_dtype}")
660 code = <uint8_t>kDLInt 1iCqrstvdbha
661 elif kind == "u": 1xycefjklmnopgw
662 if bits not in (8, 16, 32, 64): 1mnop
663 raise BufferError(f"Unsupported unsigned integer dtype: {np_dtype}")
664 code = <uint8_t>kDLUInt 1mnop
665 elif kind == "f": 1xycefjklgw
666 if bits not in (16, 32, 64): 1xcjkl
667 raise BufferError(f"Unsupported floating dtype: {np_dtype}")
668 code = <uint8_t>kDLFloat 1Bxcjkl
669 elif kind == "c": 1yefgw
670 if bits not in (64, 128): 1efgw
671 raise BufferError(f"Unsupported complex dtype: {np_dtype}")
672 code = <uint8_t>kDLComplex 1efgw
673 else:
674 raise BufferError(f"Unsupported dtype for DLPack export: {np_dtype}") 1y
676 out_dtype.code = code 1xiCcuefjklqrstmnopgwvdbha
677 out_dtype.bits = <uint8_t>bits 1xiCcuefjklqrstmnopgwvdbha
678 out_dtype.lanes = <uint16_t>1 1BxiCcuefjklqrstmnopgwvdbha
679 return 0 1xiCcuefjklqrstmnopgwvdbha
682cdef inline int _smv_get_dl_device(
683 StridedMemoryView view,
684 _DLDeviceType* out_device_type,
685 int32_t* out_device_id,
686) except -1:
687 cdef _DLDeviceType device_type
688 cdef int32_t device_id
689 cdef object buf
690 if view.dl_tensor != NULL: 145xiCcuefjklqrstmnopgwvdbha
691 device_type = view.dl_tensor.device.device_type 145iCcuefjklqrstmnopgwvdbha
692 if device_type == _kDLCUDA: 145iCcuefjklqrstmnopgwvdbha
693 device_id = view.dl_tensor.device.device_id
694 else:
695 # CPU, CUDAHost, and CUDAManaged use device_id=0 in DLPack.
696 device_id = 0 145iCcuefjklqrstmnopgwvdbha
697 elif view.is_device_accessible: 1x
698 buf = view.get_buffer() 1x
699 dev_type, dev_id = classify_dl_device(buf) 1x
700 device_type = <_DLDeviceType>dev_type 1x
701 device_id = <int32_t>dev_id 1x
702 else:
703 device_type = _kDLCPU
704 device_id = 0
706 out_device_type[0] = device_type 145xiCcuefjklqrstmnopgwvdbha
707 out_device_id[0] = device_id 145xiCcuefjklqrstmnopgwvdbha
708 return 0 145xiCcuefjklqrstmnopgwvdbha
711cdef inline int _smv_setup_dl_tensor_common(
712 DLTensor* dl_tensor,
713 StridedMemoryView view,
714 _StridedLayout layout,
715) except -1:
716 cdef object dtype_obj = view.get_dtype() 1xzAyiQCcuefjklqrstmnopgwvdbha
717 if dtype_obj is None: 1xzAyiQCcuefjklqrstmnopgwvdbha
718 raise BufferError( 1Q
719 "Cannot export StridedMemoryView via DLPack without dtype information; "
720 "create the view with dtype specified."
721 )
722 _smv_dtype_numpy_to_dlpack(dtype_obj, &dl_tensor.dtype) 1xzAyiCcuefjklqrstmnopgwvdbha
723 _smv_get_dl_device(view, &dl_tensor.device.device_type, &dl_tensor.device.device_id) 1xiCcuefjklqrstmnopgwvdbha
725 cdef int ndim = layout.base.ndim 1xiCcuefjklqrstmnopgwvdbha
726 dl_tensor.ndim = ndim 1xiCcuefjklqrstmnopgwvdbha
727 if layout.get_volume() == 0: 1xiCcuefjklqrstmnopgwvdbha
728 dl_tensor.data = NULL 1g
729 else:
730 dl_tensor.data = <void*><intptr_t>view.ptr 1xiCcuefjklqrstmnopwvdbha
731 dl_tensor.byte_offset = 0 1xiCcuefjklqrstmnopgwvdbha
732 return 0 1xiCcuefjklqrstmnopgwvdbha
735cdef inline int _smv_setup_dl_tensor(DLTensor* dl_tensor, StridedMemoryView view) except -1:
736 cdef _StridedLayout layout = view.get_layout() 1xzAyiQcuefjklqrstmnopgwvdbha
737 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1xzAyiQcuefjklqrstmnopgwvdbha
739 cdef int i
740 cdef int64_t* shape_strides = NULL 1xicuefjklqrstmnopgwvdbha
741 cdef int64_t* strides_src = NULL 1xicuefjklqrstmnopgwvdbha
742 cdef int ndim = dl_tensor.ndim 1xicuefjklqrstmnopgwvdbha
743 if ndim == 0: 1xicuefjklqrstmnopgwvdbha
744 dl_tensor.shape = NULL 1w
745 dl_tensor.strides = NULL 1w
746 else:
747 # DLPack v1.2+ requires non-NULL strides for ndim != 0.
748 shape_strides = <int64_t*>stdlib.malloc(sizeof(int64_t) * 2 * ndim) 1xicuefjklqrstmnopgvdbha
749 if shape_strides == NULL: 1xicuefjklqrstmnopgvdbha
750 raise MemoryError()
751 try: 1xicuefjklqrstmnopgvdbha
752 strides_src = get_strides_ptr(layout.base) 1xicuefjklqrstmnopgvdbha
753 for i in range(ndim): 1xicuefjklqrstmnopgvdbha
754 shape_strides[i] = layout.base.shape[i] 1xicuefjklqrstmnopgvdbha
755 shape_strides[i + ndim] = strides_src[i] 1xicuefjklqrstmnopgvdbha
756 except Exception:
757 stdlib.free(shape_strides)
758 raise
759 dl_tensor.shape = shape_strides 1xicuefjklqrstmnopgvdbha
760 dl_tensor.strides = shape_strides + ndim 1xicuefjklqrstmnopgvdbha
761 return 0 1xicuefjklqrstmnopgwvdbha
764cdef inline int _smv_setup_dltensor_borrowed(DLTensor* dl_tensor, StridedMemoryView view) except -1:
765 cdef _StridedLayout layout = view.get_layout() 1C
766 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1C
768 if dl_tensor.ndim == 0: 1C
769 dl_tensor.shape = NULL
770 dl_tensor.strides = NULL
771 else:
772 dl_tensor.shape = layout.base.shape 1C
773 # For temporary/non-owning exchange we provide explicit strides.
774 dl_tensor.strides = get_strides_ptr(layout.base) 1C
775 return 0 1C
778cdef inline int _smv_fill_managed_tensor_versioned(
779 DLManagedTensorVersioned* dlm_tensor_ver,
780 StridedMemoryView view,
781) except -1:
782 cpython.Py_INCREF(view) 1icuefjklqrstmnopgwdha
783 dlm_tensor_ver.manager_ctx = <void*>view 1icuefjklqrstmnopgwdha
784 dlm_tensor_ver.deleter = _smv_versioned_deleter 1icuefjklqrstmnopgwdha
785 dlm_tensor_ver.version.major = DLPACK_MAJOR_VERSION 1icuefjklqrstmnopgwdha
786 dlm_tensor_ver.version.minor = DLPACK_MINOR_VERSION 1icuefjklqrstmnopgwdha
787 dlm_tensor_ver.flags = DLPACK_FLAG_BITMASK_READ_ONLY if view.readonly else 0 1icuefjklqrstmnopgwdha
788 _smv_setup_dl_tensor(&dlm_tensor_ver.dl_tensor, view) 1icuefjklqrstmnopgwdha
789 return 0 1icuefjklqrstmnopgwdha
792cdef inline int _smv_fill_managed_tensor(
793 DLManagedTensor* dlm_tensor,
794 StridedMemoryView view,
795) except -1:
796 cpython.Py_INCREF(view) 1xzAyQvba
797 dlm_tensor.manager_ctx = <void*>view 1xzAyQvba
798 dlm_tensor.deleter = _smv_deleter 1xzAyQvba
799 _smv_setup_dl_tensor(&dlm_tensor.dl_tensor, view) 1xzAyQvba
800 return 0 1xvba
803cdef object _smv_make_py_capsule(StridedMemoryView view, bint versioned):
804 cdef DLManagedTensor* dlm_tensor = NULL 1xzAyiQuefjklqrstmnopgwvdbha
805 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1xzAyiQuefjklqrstmnopgwvdbha
806 cdef object capsule = None 1xzAyiQuefjklqrstmnopgwvdbha
807 cdef void* tensor_ptr = NULL 1xzAyiQuefjklqrstmnopgwvdbha
808 cdef const char* capsule_name
809 try: 1xzAyiQuefjklqrstmnopgwvdbha
810 if versioned: 1xzAyiQuefjklqrstmnopgwvdbha
811 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1iuefjklqrstmnopgwdha
812 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, view) 1iuefjklqrstmnopgwdha
813 tensor_ptr = <void*>dlm_tensor_ver 1iuefjklqrstmnopgwdha
814 capsule_name = DLPACK_VERSIONED_TENSOR_UNUSED_NAME 1iuefjklqrstmnopgwdha
815 else:
816 dlm_tensor = _smv_allocate_dlm_tensor() 1xzAyQvba
817 _smv_fill_managed_tensor(dlm_tensor, view) 1xzAyQvba
818 tensor_ptr = <void*>dlm_tensor 1xvba
819 capsule_name = DLPACK_TENSOR_UNUSED_NAME 1xvba
820 capsule = cpython.PyCapsule_New(tensor_ptr, capsule_name, _smv_pycapsule_deleter) 1xiuefjklqrstmnopgwvdbha
821 except Exception: 1zAyQ
822 if capsule is None: 1zAyQ
823 _smv_deleter(dlm_tensor) 1zAyQ
824 _smv_versioned_deleter(dlm_tensor_ver) 1zAyQ
825 raise 1zAyQ
826 return capsule 1xiuefjklqrstmnopgwvdbha
829cdef inline StridedMemoryView _smv_from_dlpack_capsule(object capsule, object exporting_obj):
830 cdef void* data = NULL 1c
831 cdef DLTensor* dl_tensor = NULL 1c
832 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1c
833 cdef DLManagedTensor* dlm_tensor = NULL 1c
834 cdef bint is_readonly = False 1c
835 cdef const char* used_name = NULL 1c
836 if cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1c
837 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 1c
838 dlm_tensor_ver = <DLManagedTensorVersioned*>data 1c
839 dl_tensor = &dlm_tensor_ver.dl_tensor 1c
840 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 1c
841 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 1c
842 elif cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME):
843 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME)
844 dlm_tensor = <DLManagedTensor*>data
845 dl_tensor = &dlm_tensor.dl_tensor
846 is_readonly = False
847 used_name = DLPACK_TENSOR_USED_NAME
848 else:
849 raise BufferError("Invalid DLPack capsule")
851 cpython.PyCapsule_SetName(capsule, used_name) 1c
853 cdef StridedMemoryView view = StridedMemoryView.__new__(StridedMemoryView) 1c
854 view.dl_tensor = dl_tensor 1c
855 view.metadata = capsule 1c
856 view.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset) 1c
857 view.readonly = is_readonly 1c
858 view.exporting_obj = exporting_obj 1c
859 if dl_tensor.device.device_type == _kDLCPU: 1c
860 view.device_id = -1 1c
861 view.is_device_accessible = False 1c
862 elif dl_tensor.device.device_type in (_kDLCUDA, _kDLCUDAHost, _kDLCUDAManaged):
863 view.device_id = dl_tensor.device.device_id
864 view.is_device_accessible = True
865 else:
866 raise BufferError("device not supported")
867 return view 1c
870cdef int _smv_managed_tensor_allocator(
871 DLTensor* prototype,
872 DLManagedTensorVersioned** out,
873 void* error_ctx,
874 void (*SetError)(void* error_ctx, const char* kind, const char* message) noexcept,
875) noexcept with gil:
876 if out != NULL: 2Xb
877 out[0] = NULL 2Xb
878 if SetError != NULL: 2Xb
879 SetError(error_ctx, b"NotImplementedError", b"managed_tensor_allocator is not supported by StridedMemoryView")
880 cpython.PyErr_SetString(NotImplementedError, b"managed_tensor_allocator is not supported by StridedMemoryView") 2Xb
881 return -1 2Xb
884cdef int _smv_managed_tensor_from_py_object_no_sync(
885 void* py_object,
886 DLManagedTensorVersioned** out,
887) noexcept with gil:
888 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1c
889 if out == NULL: 1c
890 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL")
891 return -1
892 out[0] = NULL 1c
893 cdef object obj = <object>py_object 1c
894 if not isinstance(obj, StridedMemoryView): 1c
895 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView")
896 return -1
897 try: 1c
898 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1c
899 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, <StridedMemoryView>obj) 1c
900 except Exception:
901 _smv_versioned_deleter(dlm_tensor_ver)
902 return -1
903 out[0] = dlm_tensor_ver 1c
904 return 0 1c
907cdef int _smv_managed_tensor_to_py_object_no_sync(
908 DLManagedTensorVersioned* tensor,
909 void** out_py_object,
910) noexcept with gil:
911 cdef object capsule
912 cdef object py_view
913 if out_py_object == NULL: 2c Yb
914 cpython.PyErr_SetString(RuntimeError, b"out_py_object cannot be NULL")
915 return -1
916 out_py_object[0] = NULL 2c Yb
917 if tensor == NULL: 2c Yb
918 cpython.PyErr_SetString(RuntimeError, b"tensor cannot be NULL") 2Yb
919 return -1 2Yb
920 try: 1c
921 capsule = cpython.PyCapsule_New( 1c
922 <void*>tensor,
923 DLPACK_VERSIONED_TENSOR_UNUSED_NAME,
924 _smv_pycapsule_deleter,
925 )
926 py_view = _smv_from_dlpack_capsule(capsule, capsule) 1c
927 cpython.Py_INCREF(py_view) 1c
928 out_py_object[0] = <void*>py_view 1c
929 except Exception:
930 return -1
931 return 0 1c
934cdef int _smv_dltensor_from_py_object_no_sync(
935 void* py_object,
936 DLTensor* out,
937) noexcept with gil:
938 if out == NULL: 2ZbC
939 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL")
940 return -1
941 cdef object obj = <object>py_object 2ZbC
942 if not isinstance(obj, StridedMemoryView): 2ZbC
943 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView") 2Zb
944 return -1 2Zb
945 try: 1C
946 _smv_setup_dltensor_borrowed(out, <StridedMemoryView>obj) 1C
947 except Exception:
948 return -1
949 return 0 1C
952cdef int _smv_current_work_stream(
953 _DLDeviceType device_type,
954 int32_t device_id,
955 void** out_current_stream,
956) noexcept with gil:
957 if out_current_stream == NULL: 24b
958 cpython.PyErr_SetString(RuntimeError, b"out_current_stream cannot be NULL")
959 return -1
960 # cuda.core has no global/current stream state today.
961 out_current_stream[0] = NULL 24b
962 return 0 24b
965cdef void _init_smv_dlpack_exchange_api():
966 global _SMV_DLPACK_EXCHANGE_API_INITED
967 if _SMV_DLPACK_EXCHANGE_API_INITED:
968 return
969 _SMV_DLPACK_EXCHANGE_API.header.version.major = DLPACK_MAJOR_VERSION
970 _SMV_DLPACK_EXCHANGE_API.header.version.minor = DLPACK_MINOR_VERSION
971 _SMV_DLPACK_EXCHANGE_API.header.prev_api = NULL
972 _SMV_DLPACK_EXCHANGE_API.managed_tensor_allocator = _smv_managed_tensor_allocator
973 _SMV_DLPACK_EXCHANGE_API.managed_tensor_from_py_object_no_sync = _smv_managed_tensor_from_py_object_no_sync
974 _SMV_DLPACK_EXCHANGE_API.managed_tensor_to_py_object_no_sync = _smv_managed_tensor_to_py_object_no_sync
975 _SMV_DLPACK_EXCHANGE_API.dltensor_from_py_object_no_sync = _smv_dltensor_from_py_object_no_sync
976 _SMV_DLPACK_EXCHANGE_API.current_work_stream = _smv_current_work_stream
977 _SMV_DLPACK_EXCHANGE_API_INITED = True
980_init_smv_dlpack_exchange_api()
981# cdef classes are immutable types in Cython 3, so inject these attributes
982# directly into the type dict.
983(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__dlpack_c_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE
984(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__c_dlpack_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE
985PyType_Modified(<PyTypeObject*>StridedMemoryView)
988cdef str get_simple_repr(obj):
989 # TODO: better handling in np.dtype objects
990 cdef object obj_class
991 cdef str obj_repr
992 if isinstance(obj, type): 1#K
993 obj_class = obj
994 else:
995 obj_class = obj.__class__ 1#K
996 if obj_class.__module__ in (None, "builtins"): 1#K
997 obj_repr = obj_class.__name__ 1#
998 else:
999 obj_repr = f"{obj_class.__module__}.{obj_class.__name__}" 1#K
1000 return obj_repr 1#K
1004cdef bint check_has_dlpack(obj) except*:
1005 cdef bint has_dlpack
1006 if hasattr(obj, "__dlpack__") and hasattr(obj, "__dlpack_device__"): 24 5 Y ! X R L M N O P S T U V W Z 0 1 2 3 1bz A y 8 9 i bbVbK 6 J C c u e f j k l q r s t m n o p g w v d b h a
1007 has_dlpack = True 145Y!XRLMNOPSTUVWZ0123zAy89iK6JCcuefjklqrstmnopgwvdbha
1008 elif hasattr(obj, "__cuda_array_interface__"): 21bbbVb
1009 has_dlpack = False 2bbVb
1010 else:
1011 raise BufferError( 21b
1012 "the input object does not support any data exchange protocol")
1013 return has_dlpack 24 5 Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i bbVbK 6 J C c u e f j k l q r s t m n o p g w v d b h a
1016cdef class _StridedMemoryViewProxy:
1017 cdef readonly:
1018 object obj
1019 bint has_dlpack
1021 def __init__(self, obj: object) -> None:
1022 self.obj = obj 2L M N O P Vb
1023 self.has_dlpack = check_has_dlpack(obj) 2L M N O P Vb
1025 cpdef StridedMemoryView view(self, stream_ptr=None):
1026 if self.has_dlpack: 1LMNOP
1027 return StridedMemoryView.from_dlpack(self.obj, stream_ptr) 1LMNOP
1028 else:
1029 return StridedMemoryView.from_cuda_array_interface(self.obj, stream_ptr)
1032cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
1033 cdef int dldevice, device_id
1034 cdef bint is_device_accessible, is_readonly
1035 is_device_accessible = False 24 5 Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
1036 dldevice, device_id = obj.__dlpack_device__() 24 5 Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
1037 if dldevice == _kDLCPU: 24 5 Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a Ob
1038 assert device_id == 0 2Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a
1039 device_id = -1 2Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a
1040 if stream_ptr is None: 2Y ! X R L M N O P S T U V W Z 0 1 2 3 z A y 8 9 i K 6 J D E F G H I C c u e f j k l q r s t m n o p g w v d Nbb h a
1041 raise BufferError("stream=None is ambiguous with view()") 2Nb
1042 elif stream_ptr == -1: 1Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1043 stream_ptr = None 1Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1044 elif dldevice == _kDLCUDA:
1045 assert device_id >= 0
1046 is_device_accessible = True
1047 # no need to check other stream values, it's a pass-through
1048 if stream_ptr is None:
1049 raise BufferError("stream=None is ambiguous with view()")
1050 elif dldevice in (_kDLCUDAHost, _kDLCUDAManaged):
1051 is_device_accessible = True 145
1052 # just do a pass-through without any checks, as pinned/managed memory can be
1053 # accessed on both host and device
1054 else:
1055 raise BufferError("device not supported") 2Ob
1057 cdef object capsule
1058 try: 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1059 capsule = obj.__dlpack__( 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1060 stream=int(stream_ptr) if stream_ptr else None, 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1061 max_version=(DLPACK_MAJOR_VERSION, DLPACK_MINOR_VERSION)) 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1062 except TypeError: 1ba
1063 capsule = obj.__dlpack__( 1ba
1064 stream=int(stream_ptr) if stream_ptr else None) 1ba
1066 cdef void* data = NULL 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1067 cdef DLTensor* dl_tensor
1068 cdef DLManagedTensorVersioned* dlm_tensor_ver
1069 cdef DLManagedTensor* dlm_tensor
1070 cdef const char *used_name
1071 if cpython.PyCapsule_IsValid( 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1072 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME):
1073 data = cpython.PyCapsule_GetPointer( 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1074 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME)
1075 dlm_tensor_ver = <DLManagedTensorVersioned*>data 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1076 dl_tensor = &dlm_tensor_ver.dl_tensor 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1077 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1078 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1079 elif cpython.PyCapsule_IsValid( 1ba
1080 capsule, DLPACK_TENSOR_UNUSED_NAME):
1081 data = cpython.PyCapsule_GetPointer( 1ba
1082 capsule, DLPACK_TENSOR_UNUSED_NAME)
1083 dlm_tensor = <DLManagedTensor*>data 1ba
1084 dl_tensor = &dlm_tensor.dl_tensor 1ba
1085 is_readonly = False 1ba
1086 used_name = DLPACK_TENSOR_USED_NAME 1ba
1087 else:
1088 assert False
1090 cpython.PyCapsule_SetName(capsule, used_name) 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1092 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1093 buf.dl_tensor = dl_tensor 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1094 buf.metadata = capsule 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1095 buf.ptr = <intptr_t>(dl_tensor.data) 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1096 buf.device_id = device_id 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1097 buf.is_device_accessible = is_device_accessible 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1098 buf.readonly = is_readonly 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1099 buf.exporting_obj = obj 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1101 return buf 145Y!XRLMNOPSTUVWZ0123zAy89iK6JDEFGHICcuefjklqrstmnopgwvdbha
1104@functools.lru_cache
1105def _typestr2dtype(str typestr) -> numpy.dtype:
1106 return numpy.dtype(typestr) 1$%'()*+,-./
1109@functools.lru_cache
1110def _typestr2itemsize(str typestr) -> int:
1111 return _typestr2dtype(typestr).itemsize 1$%'()*+,-./
1114cdef object dtype_dlpack_to_numpy(DLDataType* dtype):
1115 cdef int bits = dtype.bits 1iKDEFGHICcuefjklqrstmnopgwvdbha
1116 if dtype.lanes != 1: 1iKDEFGHICcuefjklqrstmnopgwvdbha
1117 # TODO: return a NumPy structured dtype?
1118 raise NotImplementedError(
1119 f'vector dtypes (lanes={dtype.lanes}) is not supported')
1120 if dtype.code == kDLUInt: 1iKDEFGHICcuefjklqrstmnopgwvdbha
1121 if bits == 8: 1mnop
1122 np_dtype = numpy.uint8 1m
1123 elif bits == 16:
1124 np_dtype = numpy.uint16 1n
1125 elif bits == 32:
1126 np_dtype = numpy.uint32 1o
1127 elif bits == 64:
1128 np_dtype = numpy.uint64 1p
1129 else:
1130 raise TypeError('uint{} is not supported.'.format(bits))
1131 elif dtype.code == kDLInt:
1132 if bits == 8: 1iKDEFGHICqrstvdbha
1133 np_dtype = numpy.int8 1q
1134 elif bits == 16:
1135 np_dtype = numpy.int16 1r
1136 elif bits == 32:
1137 np_dtype = numpy.int32 1iKDEFGHICsvdbha
1138 elif bits == 64:
1139 np_dtype = numpy.int64 1t
1140 else:
1141 raise TypeError('int{} is not supported.'.format(bits))
1142 elif dtype.code == kDLFloat:
1143 if bits == 16: 1cjkl
1144 np_dtype = numpy.float16 1j
1145 elif bits == 32:
1146 np_dtype = numpy.float32 1k
1147 elif bits == 64:
1148 np_dtype = numpy.float64 1cl
1149 else:
1150 raise TypeError('float{} is not supported.'.format(bits))
1151 elif dtype.code == kDLComplex:
1152 # TODO(leofang): support complex32
1153 if bits == 64: 1efgw
1154 np_dtype = numpy.complex64 1e
1155 elif bits == 128:
1156 np_dtype = numpy.complex128 1fgw
1157 else:
1158 raise TypeError('complex{} is not supported.'.format(bits))
1159 elif dtype.code == kDLBool:
1160 if bits == 8: 1u
1161 np_dtype = numpy.bool_ 1u
1162 else:
1163 raise TypeError(f'{bits}-bit bool is not supported')
1164 elif dtype.code == kDLBfloat:
1165 if bfloat16 is not None:
1166 np_dtype = numpy.dtype("bfloat16")
1167 else:
1168 raise NotImplementedError(
1169 'Support for bfloat16 within cuda-core requires `ml_dtypes`'
1170 'to be installed.'
1171 )
1172 else:
1173 raise TypeError('Unsupported dtype. dtype code: {}'.format(dtype.code))
1175 # We want the dtype object not just the type object
1176 return numpy.dtype(np_dtype) 1iKDEFGHICcuefjklqrstmnopgwvdbha
1179cpdef StridedMemoryView view_as_cai(obj, stream_ptr, view=None):
1180 cdef dict cai_data = obj.__cuda_array_interface__ 22b0b5b6bx / Kbabbb7
1181 if cai_data.get("version", 0) < 3: 22b0b5b6bx / Kbabbb7
1182 raise BufferError("only CUDA Array Interface v3 or above is supported") 25b6b
1183 if cai_data.get("mask") is not None: 22b0bx / Kbabbb7
1184 raise BufferError("mask is not supported") 22b
1185 if stream_ptr is None: 20bx / Kbabbb7
1186 raise BufferError("stream=None is ambiguous with view()") 20b
1188 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2x / Kbabbb7
1189 buf.exporting_obj = obj 2x / Kbabbb7
1190 buf.metadata = cai_data 2x / Kbabbb7
1191 buf.dl_tensor = NULL 2x / Kbabbb7
1192 # Validate shape/strides/typestr eagerly so constructor paths fail fast.
1193 buf.get_layout() 2x / Kbabbb7
1194 buf.ptr, buf.readonly = cai_data["data"] 2x / abbb7
1195 buf.is_device_accessible = True 2x / abbb7
1196 if buf.ptr != 0: 2x / abbb7
1197 buf.device_id = handle_return( 1x7
1198 driver.cuPointerGetAttribute( 1x7
1199 driver.CUpointer_attribute.CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL, 1x7
1200 buf.ptr)) 1x7
1201 else:
1202 buf.device_id = handle_return(driver.cuCtxGetDevice()) 2/ abbb
1204 cdef intptr_t producer_s, consumer_s
1205 cdef EventHandle h_event
1206 stream_ptr = int(stream_ptr) 2x / abbb7
1207 if stream_ptr != -1: 2x / abbb7
1208 stream = cai_data.get("stream") 17
1209 if stream is not None: 17
1210 producer_s = <intptr_t>(stream) 17
1211 consumer_s = <intptr_t>(stream_ptr) 17
1212 assert producer_s > 0 17
1213 # establish stream order
1214 if producer_s != consumer_s: 17
1215 with nogil: 17
1216 h_event = create_event_handle_noctx(cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING) 17
1217 HANDLE_RETURN(cydriver.cuEventRecord( 17
1218 as_cu(h_event), <cydriver.CUstream>producer_s))
1219 HANDLE_RETURN(cydriver.cuStreamWaitEvent( 17
1220 <cydriver.CUstream>consumer_s, as_cu(h_event), 0))
1221 elif _is_torch_tensor(obj):
1222 # PyTorch's __cuda_array_interface__ reports version 2 and
1223 # omits the "stream" field, so the standard CAI sync path
1224 # above is a no-op for torch tensors. This is unsafe: the
1225 # consumer has no guarantee that the producer's work is
1226 # visible. We fix this by querying PyTorch's current CUDA
1227 # stream via the AOTI stable C ABI and performing the same
1228 # event-based stream ordering.
1229 _get_tensor_bridge().sync_torch_stream(
1230 buf.device_id, <intptr_t>(stream_ptr))
1232 return buf 2x / abbb7
1235cpdef StridedMemoryView view_as_array_interface(obj, view=None):
1236 cdef dict data = obj.__array_interface__ 23b7b8b$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1237 if data.get("version", 0) < 3: 23b7b8b$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1238 raise BufferError("only NumPy Array Interface v3 or above is supported") 27b8b
1239 if data.get("mask") is not None: 23b$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1240 raise BufferError("mask is not supported") 23b
1242 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1243 buf.exporting_obj = obj 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1244 buf.metadata = data 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1245 buf.dl_tensor = NULL 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1246 # Validate shape/strides/typestr eagerly so constructor paths fail fast.
1247 buf.get_layout() 2$ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb
1248 buf.ptr, buf.readonly = data["data"] 1$%:';(=)*+,?@[]^_`-{|}~.
1249 buf.is_device_accessible = False 1$%:';(=)*+,?@[]^_`-{|}~.
1250 buf.device_id = handle_return(driver.cuCtxGetDevice()) 1$%:';(=)*+,?@[]^_`-{|}~.
1251 return buf 1$%:';(=)*+,?@[]^_`-{|}~.
1254def args_viewable_as_strided_memory(arg_indices: tuple[int, ...]) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
1255 """
1256 Decorator to create proxy objects to :obj:`StridedMemoryView` for the
1257 specified positional arguments.
1259 This allows array/tensor attributes to be accessed inside the function
1260 implementation, while keeping the function body array-library-agnostic (if
1261 desired).
1263 Inside the decorated function, the specified arguments become instances
1264 of an (undocumented) proxy type, regardless of its original source. A
1265 :obj:`StridedMemoryView` instance can be obtained by passing the (consumer)
1266 stream pointer (as a Python `int`) to the proxies's ``view()`` method. For
1267 example:
1269 .. code-block:: python
1271 @args_viewable_as_strided_memory((1,))
1272 def my_func(arg0, arg1, arg2, stream: Stream):
1273 # arg1 can be any object supporting DLPack or CUDA Array Interface
1274 view = arg1.view(stream.handle)
1275 assert isinstance(view, StridedMemoryView)
1276 ...
1278 Parameters
1279 ----------
1280 arg_indices : tuple
1281 The indices of the target positional arguments.
1282 """
1283 def wrapped_func_with_indices(func: "Callable") -> "Callable": 1LMNOP
1284 @functools.wraps(func) 1LMNOP
1285 def wrapped_func(*args, **kwargs) -> object:
1286 args = list(args) 1LMNOP
1287 cdef int idx
1288 for idx in arg_indices: 1LMNOP
1289 args[idx] = _StridedMemoryViewProxy(args[idx]) 1LMNOP
1290 return func(*args, **kwargs) 1LMNOP
1291 return wrapped_func 1LMNOP
1292 return wrapped_func_with_indices 1LMNOP
1295cdef inline _StridedLayout layout_from_dlpack(DLTensor* dl_tensor):
1296 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
1297 cdef int nbits = dl_tensor.dtype.bits * dl_tensor.dtype.lanes 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
1298 cdef int itemsize = nbits >> 3 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
1299 if (itemsize << 3) != nbits: 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
1300 raise ValueError("dl_tensor.dtype.bits must be a multiple of 8")
1301 layout.init_from_ptr(dl_tensor.ndim, dl_tensor.shape, dl_tensor.strides, itemsize) 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
1302 return layout 1XLMNOPSTUVWZ0123zAyiKJDEFGHICcuefjklqrstmnopgwvdbha
1305cdef _StridedLayout layout_from_cai(object metadata):
1306 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabbb7
1307 cdef object shape = metadata["shape"] 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabbb7
1308 cdef object strides = metadata.get("strides") 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabbb7
1309 cdef int itemsize = _typestr2itemsize(metadata["typestr"]) 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabbb7
1310 layout.init_from_tuple(shape, strides, itemsize, True) 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . Mb/ Kbabbb7
1311 return layout 2x $ % : ' ; ( = ) * + , ? @ [ ] ^ _ ` - { | } ~ . / abbb7
1314cdef inline intptr_t get_data_ptr(object buffer, _StridedLayout layout) except? 0:
1315 return <intptr_t>(int(buffer.handle)) + layout.get_slice_offset_in_bytes() 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1318cdef inline int view_buffer_strided(
1319 StridedMemoryView view,
1320 object buffer,
1321 _StridedLayout layout,
1322 object dtype,
1323 bint is_readonly,
1324) except -1:
1325 if dtype is not None: 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbQ # J D E F G H I
1326 dtype = numpy.dtype(dtype) 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbJ D E F G H I
1327 if dtype.itemsize != layout.itemsize: 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbJ D E F G H I
1328 raise ValueError(
1329 f"The dtype's itemsize ({dtype.itemsize}) does not match the layout's "
1330 f"itemsize ({layout.itemsize})."
1331 )
1332 # Check the layout's offset range [min_offset, max_offset] fits
1333 # within the [0, buffer.size - 1] range.
1334 # The required_size_in_bytes fails if min_offset < 0.
1335 # NB. For external memory, both positive and negative offsets can be valid,
1336 # but for a proper check we'd need to know both size and data offset,
1337 # while neither is reported by the packages.
1338 cdef bint is_allocated = buffer.memory_resource is not None 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbQ # J D E F G H I
1339 if is_allocated and buffer.size < layout.get_required_size_in_bytes(): 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbRbcbdbPbLbQ # J D E F G H I
1340 raise ValueError( 2Pb
1341 f"Buffer size is too small for the layout. " 2Pb
1342 f"Expected at least {layout.get_required_size_in_bytes()} bytes, " 2Pb
1343 f"got {buffer.size} bytes." 2Pb
1344 )
1345 # set the public attributes
1346 view.ptr = get_data_ptr(buffer, layout) 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1347 view.device_id = buffer.device_id 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1348 view.is_device_accessible = buffer.is_device_accessible 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1349 view.readonly = is_readonly 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1350 view.exporting_obj = view._buffer = buffer 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1351 # no dlpack/cai metadata
1352 view.dl_tensor = NULL 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1353 view.metadata = None 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1354 # we get the layout from the caller
1355 view._layout = layout 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1356 view._dtype = dtype 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I
1357 return 0 2z A y ebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbcbdbLbQ # J D E F G H I