Coverage for cuda/core/_memoryview.pyx: 84.93%
710 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +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) 21 2 3 8 V P J K L M N Q R S T U v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} 6 7 g I 5 H 4 B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
78 cdef object cached = _torch_type_cache.get(tp) 21 2 3 8 V P J K L M N Q R S T U v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} 6 7 g I 5 H 4 B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
79 if cached is not None: 21 2 3 8 V P J K L M N Q R S T U v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} 6 7 g I 5 H 4 B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
80 return <bint>cached 23 8 V J K L M N Q R S T U x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba
81 cdef str mod = tp.__module__ or "" 21 2 P v | Ib} 4 a Mb
82 cdef bint result = mod.startswith("torch") and hasattr(obj, "data_ptr") \ 21 2 P v | Ib} 4 a Mb
83 and _torch_version_check()
84 _torch_type_cache[tp] = result # setdefault not needed for bools 21 2 P v | Ib} 4 a Mb
85 return result 21 2 P v | Ib} 4 a Mb
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__ 2W X Y Z 0 Ob~
185 if obj is not None: 2z W X Y Z 0 Ob~
186 # populate self's attributes
187 if check_has_dlpack(obj): 1WXYZ0~
188 warnings.warn( 1WXYZ0
189 f"Constructing a {clsname} directly from a DLPack-supporting object is deprecated; " 1WXYZ0
190 "Use `StridedMemoryView.from_dlpack` or `StridedMemoryView.from_any_interface` instead.",
191 DeprecationWarning, 1WXYZ0
192 stacklevel=2,
193 )
194 view_as_dlpack(obj, stream_ptr, self) 1zWXYZ0
195 else:
196 warnings.warn( 1~
197 f"Constructing a {clsname} directly from a CUDA-array-interface-supporting object is deprecated; " 1~
198 "Use `StridedMemoryView.from_cuda_array_interface` or `StridedMemoryView.from_any_interface` instead.",
199 DeprecationWarning, 1~
200 stacklevel=2,
201 )
202 view_as_cai(obj, stream_ptr, self) 1~
203 else:
204 warnings.warn( 2Ob
205 f"Constructing an empty {clsname} is deprecated; " 2Ob
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, 2Ob
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) 21 2 3 8 V P J K L M N Q R S T U x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
225 if _is_torch_tensor(obj): 21 2 3 8 V P J K L M N Q R S T U x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
226 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf)
227 return buf
228 view_as_dlpack(obj, stream_ptr, buf) 21 2 3 8 V P J K L M N Q R S T U x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
229 return buf 11238VPJKLMNQRSTUxyw67gI5HBCDEFGAbsdehijopqrklmnfutca
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) 2v | Ib} 4
243 if _is_torch_tensor(obj): 2v | Ib} 4
244 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf)
245 return buf
246 view_as_cai(obj, stream_ptr, buf) 2z v | Ib} 4
247 return buf 1v|}4
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) 2z ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
259 if _is_torch_tensor(obj): 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
260 _get_tensor_bridge().view_as_torch_tensor(obj, None, buf)
261 return buf
262 view_as_array_interface(obj, buf) 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
263 return buf 1z!#-$.%'()*/:;=?@[]^+_`{,
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): 21 2 3 8 V P Q R S T U Zbx y w 6 7 g I 5 H A b s d e h i j o p q r k l m n f u t c a
283 return cls.from_dlpack(obj, stream_ptr) 11238VPQRSTUxyw67gI5HAbsdehijopqrklmnfutca
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) 2RbSbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbUbQbabbbNbJbO 9
324 if itemsize is None and dtype is None: 2RbSbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbUbQbabbbNbJbO 9
325 raise ValueError("Either itemsize or dtype must be specified") 2Ub
326 if itemsize is not None and dtype is not None and itemsize != dtype.itemsize: 2RbSbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbQbabbbNbJbO 9
327 raise ValueError( 2Qb
328 f"itemsize ({itemsize}) does not match dtype.itemsize ({dtype.itemsize})" 2Qb
329 )
330 # (itemsize is None XOR dtype is None) OR they are equal
331 view_buffer_strided( 2cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbO 9
332 view,
333 buffer,
334 _StridedLayout(shape=shape, strides=strides, itemsize=getattr(dtype, "itemsize", itemsize)), 2RbSbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbO 9
335 dtype,
336 is_readonly,
337 )
338 return view 2z cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9
340 def __dealloc__(self) -> None:
341 if self.dl_tensor == NULL: 2RbSb1 2 3 8 V P J K L M N Q R S T U W X Y Z 0 v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , KbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbUbQbabbbNbJb| Ib} 6 7 g O Ob~ 9 I 5 H 4 B C D E F G A s d e h i j o p q r k l m n f u t c Lba Mb
342 return 2RbSbv x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , KbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbUbQbabbbNbJb| Ib} O Ob~ 9 5 H 4 B C D E F G LbMb
344 if cpython.PyCapsule_IsValid( 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAsdehijopqrklmnfutca
345 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME): 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAsdehijopqrklmnfutca
346 data = cpython.PyCapsule_GetPointer( 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAsdehijopqrklmnfutca
347 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME) 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAsdehijopqrklmnfutca
348 dlm_tensor_ver = <DLManagedTensorVersioned*>data 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAsdehijopqrklmnfutca
349 dlm_tensor_ver.deleter(dlm_tensor_ver) 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAsdehijopqrklmnfutca
350 elif cpython.PyCapsule_IsValid( 1a
351 self.metadata, DLPACK_TENSOR_USED_NAME): 1za
352 data = cpython.PyCapsule_GetPointer( 1a
353 self.metadata, DLPACK_TENSOR_USED_NAME) 1a
354 dlm_tensor = <DLManagedTensor*>data 1a
355 dlm_tensor.deleter(dlm_tensor) 1a
357 def view(
358 self, layout : _StridedLayout | None = None, dtype : numpy.dtype | None = None
359 ) -> StridedMemoryView:
360 """
361 Creates a new view with adjusted layout and dtype.
362 Same as calling :meth:`from_buffer` with the current buffer.
363 """
364 cdef StridedMemoryView view = StridedMemoryView.__new__(self.__class__) 2x y w abbb5 H B C D E F G
365 if layout is None and dtype is None: 2z x y w abbb5 H B C D E F G
366 return self 15
367 if layout is None: 2x y w abbbH B C D E F G
368 layout = self.get_layout() 1xywH
369 if dtype is None: 2x y w abbbH B C D E F G
370 dtype = self.get_dtype() 2z abbbB C D E F G
371 view_buffer_strided(view, self.get_buffer(), layout, dtype, self.readonly) 2x y w abbbH B C D E F G
372 return view 2x y w abbbH B C D E F G
374 def as_tensor_map(
375 self,
376 box_dim: tuple[int, ...] | None = None,
377 *,
378 options: object = None,
379 element_strides: tuple[int, ...] | None = None,
380 data_type: object = None,
381 interleave: object = None,
382 swizzle: object = None,
383 l2_promotion: object = None,
384 oob_fill: object = None,
385 ) -> object:
386 """Create a tiled :obj:`TensorMapDescriptor` from this view.
388 This is the public entry point for creating tiled tensor map
389 descriptors in ``cuda.core``. Pass either ``box_dim`` and the
390 individual keyword arguments directly, or provide bundled tiled
391 options via ``options=``.
392 """
393 from cuda.core._tensor_map import TensorMapDescriptor 1zP
395 kwargs = {} 1P
396 if options is not None: 1P
397 kwargs["options"] = options
398 if element_strides is not None: 1P
399 kwargs["element_strides"] = element_strides 1P
400 if data_type is not None: 1P
401 kwargs["data_type"] = data_type 1zP
402 if interleave is not None: 1P
403 kwargs["interleave"] = interleave
404 if swizzle is not None: 1P
405 kwargs["swizzle"] = swizzle 1P
406 if l2_promotion is not None: 1P
407 kwargs["l2_promotion"] = l2_promotion 1P
408 if oob_fill is not None: 1P
409 kwargs["oob_fill"] = oob_fill 1P
410 return TensorMapDescriptor._from_tiled(self, box_dim, **kwargs) 1P
412 def copy_from(
413 self,
414 other: StridedMemoryView,
415 stream: Stream,
416 allocator: object = None,
417 blocking: bool | None = None,
418 ) -> None:
419 """
420 Copies the data from the other view into this view.
422 The copy can be performed between following memory spaces:
423 host-to-device, device-to-host, device-to-device (on the same device).
425 Parameters
426 ----------
427 other : StridedMemoryView
428 The view to copy data from.
429 stream : Stream | None, optional
430 The stream to schedule the copy on.
431 allocator : MemoryResource | None, optional
432 If temporary buffers are needed, the specified memory resources
433 will be used to allocate the memory. If not specified, default
434 resources will be used.
435 blocking : bool | None, optional
436 Whether the call should block until the copy is complete.
437 * ``True``: the ``stream`` is synchronized with the host at the end of the call,
438 blocking until the copy is complete.
439 * ``False``: if possible, the call returns immediately once the copy is scheduled.
440 However, in some cases of host-to-device or device-to-host copies, the call may
441 still synchronize with the host if necessary.
442 * ``None`` (default):
443 * for device-to-device, it defaults to ``False`` (non-blocking),
444 * for host-to-device or device-to-host, it defaults to ``True`` (blocking).
445 """
446 raise NotImplementedError("Sorry, not supported: copy_from") 16
448 def copy_to(
449 self,
450 other: StridedMemoryView,
451 stream: Stream | None = None,
452 allocator: object = None,
453 blocking: bool | None = None,
454 ) -> None:
455 """
456 Copies the data from this view into the ``other`` view.
458 For details, see :meth:`copy_from`.
459 """
460 raise NotImplementedError("Sorry, not supported: copy_to") 17
462 def __dlpack__(
463 self,
464 *,
465 stream: int | None = None,
466 max_version: tuple[int, int] | None = None,
467 dl_device: tuple[int, int] | None = None,
468 copy: bool | None = None,
469 ) -> object:
470 # Similar to Buffer.__dlpack__: no implicit synchronization is performed.
471 if dl_device is not None: 13vxywgOsdehijopqrklmnfutca
472 raise BufferError("Sorry, not supported: dl_device other than None") 13
473 if copy is True: 13vxywgOsdehijopqrklmnfutca
474 raise BufferError("Sorry, not supported: copy=True") 13
476 cdef bint versioned
477 if max_version is None: 13vxywgOsdehijopqrklmnfutca
478 versioned = False 1vxywOta
479 else:
480 if not isinstance(max_version, tuple) or len(max_version) != 2: 13gsdehijopqrklmnfuca
481 raise BufferError(f"Expected max_version tuple[int, int], got {max_version}") 1z3
482 versioned = max_version >= (1, 0) 1gsdehijopqrklmnfuca
484 # NOTE: stream is accepted for protocol compatibility but not used.
485 cdef object capsule = _smv_make_py_capsule(self, versioned) 1vxywgOsdehijopqrklmnfutca
486 return capsule 1vgsdehijopqrklmnfutca
488 def __dlpack_device__(self) -> tuple[int, int]:
489 cdef _DLDeviceType device_type
490 cdef int32_t device_id
491 _smv_get_dl_device(self, &device_type, &device_id) 112vga
492 return (<int>device_type, int(device_id)) 112vga
494 @property
495 def _layout(self) -> _StridedLayout:
496 """
497 The layout of the tensor. For StridedMemoryView created from DLPack or CAI,
498 the layout is inferred from the tensor object's metadata.
499 """
500 return self.get_layout() 2cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbObH B C D E F G
502 @property
503 def size(self) -> int:
504 return self.get_layout().get_volume() 1JKLMNQRSTUWXYZ0!#-$.%'()*/:;=?@[]^+_`{,|
506 @property
507 def shape(self) -> tuple[int, ...]:
508 """
509 Shape of the tensor.
510 """
511 return self.get_layout().get_shape_tuple() 2V J K L M N Q R S T U W X Y Z 0 ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbb| } ~ 9 I 4 B C D E F G b
513 @property
514 def strides(self) -> tuple[int, ...] | None:
515 """
516 Strides of the tensor (in **counts**, not bytes).
517 """
518 return self.get_layout().get_strides_tuple() 2V J K L M N Q R S T U W X Y Z 0 ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHb| } 9 I
520 @property
521 def dtype(self) -> numpy.dtype | None:
522 """
523 Data type of the tensor.
525 Supports standard NumPy dtypes as well as narrow data types (e.g., ``bfloat16``)
526 when the optional `ml_dtypes <https://github.com/jax-ml/ml_dtypes>`_ package is
527 installed. If ``ml_dtypes`` is not available and such a tensor is encountered,
528 a :obj:`NotImplementedError` will be raised.
529 """
530 return self.get_dtype() 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbJb9 I H B C D E F G
532 def __repr__(self) -> str:
533 return (f"StridedMemoryView(ptr={self.ptr},\n" 19I
534 + f" shape={self.shape},\n" 19I
535 + f" strides={self.strides},\n" 19I
536 + f" itemsize={self._layout.itemsize},\n" 19I
537 + f" dtype={get_simple_repr(self.dtype)},\n" 19I
538 + f" device_id={self.device_id},\n" 19I
539 + f" is_device_accessible={self.is_device_accessible},\n" 19I
540 + f" readonly={self.readonly},\n" 19I
541 + f" exporting_obj={get_simple_repr(self.exporting_obj)})") 1z9I
543 @cython.critical_section
544 cdef inline _StridedLayout get_layout(self):
545 if self._layout is None: 2V J K L M N Q R S T U W X Y Z 0 v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , KbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbb| Ib} g O Ob~ 9 I H 4 B C D E F G A b s d e h i j o p q r k l m n f u t c a
546 if self.dl_tensor: 2V J K L M N Q R S T U W X Y Z 0 v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} g Ob~ I H 4 B C D E F G A b s d e h i j o p q r k l m n f u t c a
547 self._layout = layout_from_dlpack(self.dl_tensor) 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
548 elif self.metadata is not None: 2z v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} Ob~ 4
549 self._layout = layout_from_cai(self.metadata) 2v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} ~ 4
550 else:
551 raise ValueError("Cannot infer layout from the exporting object") 2Ob
552 return self._layout 2V J K L M N Q R S T U W X Y Z 0 v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbb| } g O ~ 9 I H 4 B C D E F G A b s d e h i j o p q r k l m n f u t c a
554 @cython.critical_section
555 cdef inline object get_buffer(self):
556 """
557 Returns Buffer instance with the underlying data.
558 If the SMV was created from a Buffer, it will return the same Buffer instance.
559 Otherwise, it will create a new instance with owner set to the exporting object.
560 """
561 if self._buffer is None: 2v x y w abbbH B C D E F G
562 if isinstance(self.exporting_obj, Buffer): 1vxywHBCDEFG
563 self._buffer = self.exporting_obj
564 else:
565 self._buffer = Buffer.from_handle(self.ptr, 0, owner=self.exporting_obj) 1zvxywHBCDEFG
566 return self._buffer 2v x y w abbbH B C D E F G
568 @cython.critical_section
569 cdef inline object get_dtype(self):
570 if self._dtype is None: 2v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbg O 9 I H B C D E F G A b s d e h i j o p q r k l m n f u t c a
571 if self.dl_tensor != NULL: 1v!#-$.%'()*/:;=?@[]^+_`{,gO9IBCDEFGAbsdehijopqrklmnfutca
572 self._dtype = dtype_dlpack_to_numpy(&self.dl_tensor.dtype) 1gIBCDEFGAbsdehijopqrklmnfutca
573 elif isinstance(self.metadata, int): 1v!#-$.%'()*/:;=?@[]^+_`{,O9
574 # AOTI dtype code stored by the torch tensor bridge
575 self._dtype = _get_tensor_bridge().resolve_aoti_dtype(
576 self.metadata)
577 elif self.metadata is not None: 1v!#-$.%'()*/:;=?@[]^+_`{,O9
578 self._dtype = _typestr2dtype(self.metadata["typestr"]) 1v!#-$.%'()*/:;=?@[]^+_`{,
579 return self._dtype 2v x y w ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbg O 9 I H B C D E F G A b s d e h i j o p q r k l m n f u t c a
582cdef void _smv_pycapsule_deleter(object capsule) noexcept:
583 cdef DLManagedTensor* dlm_tensor
584 cdef DLManagedTensorVersioned* dlm_tensor_ver
585 # Do not invoke the deleter on a used capsule.
586 if cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME): 1vgsdehijopqrklmnfutca
587 dlm_tensor = <DLManagedTensor*>( 1zvt
588 cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME) 1vt
589 )
590 if dlm_tensor.deleter: 1vt
591 dlm_tensor.deleter(dlm_tensor) 1zvt
592 elif cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1gsdehijopqrklmnfuca
593 dlm_tensor_ver = <DLManagedTensorVersioned*>( 1c
594 cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 1c
595 )
596 if dlm_tensor_ver.deleter: 1c
597 dlm_tensor_ver.deleter(dlm_tensor_ver) 1c
600cdef inline void _smv_release_export_resources(void* manager_ctx, int64_t* shape_ptr) noexcept with gil:
601 if shape_ptr: 1vxywgOsdehijopqrklmnfutca
602 stdlib.free(shape_ptr) 1vgsdehijopqrklmnftca
603 if manager_ctx: 1vxywgOsdehijopqrklmnfutca
604 cpython.Py_DECREF(<object>manager_ctx) 1vxywgOsdehijopqrklmnfutca
607cdef void _smv_deleter(DLManagedTensor* tensor) noexcept with gil:
608 if tensor: 1vxywOta
609 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1zvxywOta
610 tensor.manager_ctx = NULL 1vxywOta
611 stdlib.free(tensor) 1vxywOta
614cdef void _smv_versioned_deleter(DLManagedTensorVersioned* tensor) noexcept with gil:
615 if tensor: 1xywgOsdehijopqrklmnfuca
616 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1gsdehijopqrklmnfuca
617 tensor.manager_ctx = NULL 1gsdehijopqrklmnfuca
618 stdlib.free(tensor) 1gsdehijopqrklmnfuca
621cdef inline DLManagedTensorVersioned* _smv_allocate_dlm_tensor_versioned() except? NULL:
622 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1gbsdehijopqrklmnfuca
623 dlm_tensor_ver = <DLManagedTensorVersioned*>stdlib.malloc(sizeof(DLManagedTensorVersioned)) 1gbsdehijopqrklmnfuca
624 if dlm_tensor_ver == NULL: 1gbsdehijopqrklmnfuca
625 raise MemoryError()
626 dlm_tensor_ver.dl_tensor.shape = NULL 1gbsdehijopqrklmnfuca
627 dlm_tensor_ver.manager_ctx = NULL 1gbsdehijopqrklmnfuca
628 return dlm_tensor_ver 1gbsdehijopqrklmnfuca
631cdef inline DLManagedTensor* _smv_allocate_dlm_tensor() except? NULL:
632 cdef DLManagedTensor* dlm_tensor = NULL 1zvxywOta
633 dlm_tensor = <DLManagedTensor*>stdlib.malloc(sizeof(DLManagedTensor)) 1vxywOta
634 if dlm_tensor == NULL: 1vxywOta
635 raise MemoryError()
636 dlm_tensor.dl_tensor.shape = NULL 1vxywOta
637 dlm_tensor.manager_ctx = NULL 1vxywOta
638 return dlm_tensor 1vxywOta
641cdef inline int _smv_dtype_numpy_to_dlpack(object dtype_obj, DLDataType* out_dtype) except -1:
642 cdef object np_dtype = numpy.dtype(dtype_obj) 1vxywgAbsdehijopqrklmnfutca
643 if np_dtype.fields is not None: 1vxywgAbsdehijopqrklmnfutca
644 raise BufferError("Structured dtypes are not supported for DLPack export") 1y
645 if not np_dtype.isnative and np_dtype.byteorder not in ("=", "|"): 1vxwgAbsdehijopqrklmnfutca
646 raise BufferError("Non-native-endian dtypes are not supported for DLPack export") 1x
648 cdef str kind = np_dtype.kind 1zvwgAbsdehijopqrklmnfutca
649 cdef int bits = np_dtype.itemsize * 8 1vwgAbsdehijopqrklmnfutca
650 cdef uint8_t code
651 if kind == "b": 1vwgAbsdehijopqrklmnfutca
652 if bits != 8: 1s
653 raise BufferError(f"Unsupported bool dtype itemsize: {np_dtype.itemsize}")
654 code = <uint8_t>kDLBool 1s
655 elif kind == "i": 1vwgAbdehijopqrklmnfutca
656 if bits not in (8, 16, 32, 64): 1gAopqrtca
657 raise BufferError(f"Unsupported signed integer dtype: {np_dtype}")
658 code = <uint8_t>kDLInt 1gAopqrtca
659 elif kind == "u": 1vwbdehijklmnfu
660 if bits not in (8, 16, 32, 64): 1klmn
661 raise BufferError(f"Unsupported unsigned integer dtype: {np_dtype}")
662 code = <uint8_t>kDLUInt 1klmn
663 elif kind == "f": 1vwbdehijfu
664 if bits not in (16, 32, 64): 1vbhij
665 raise BufferError(f"Unsupported floating dtype: {np_dtype}")
666 code = <uint8_t>kDLFloat 1vbhij
667 elif kind == "c": 1wdefu
668 if bits not in (64, 128): 1zdefu
669 raise BufferError(f"Unsupported complex dtype: {np_dtype}")
670 code = <uint8_t>kDLComplex 1defu
671 else:
672 raise BufferError(f"Unsupported dtype for DLPack export: {np_dtype}") 1w
674 out_dtype.code = code 1vgAbsdehijopqrklmnfutca
675 out_dtype.bits = <uint8_t>bits 1vgAbsdehijopqrklmnfutca
676 out_dtype.lanes = <uint16_t>1 1vgAbsdehijopqrklmnfutca
677 return 0 1vgAbsdehijopqrklmnfutca
680cdef inline int _smv_get_dl_device(
681 StridedMemoryView view,
682 _DLDeviceType* out_device_type,
683 int32_t* out_device_id,
684) except -1:
685 cdef _DLDeviceType device_type
686 cdef int32_t device_id
687 cdef object buf
688 if view.dl_tensor != NULL: 112vgAbsdehijopqrklmnfutca
689 device_type = view.dl_tensor.device.device_type 112gAbsdehijopqrklmnfutca
690 if device_type == _kDLCUDA: 112gAbsdehijopqrklmnfutca
691 device_id = view.dl_tensor.device.device_id
692 else:
693 # CPU, CUDAHost, and CUDAManaged use device_id=0 in DLPack.
694 device_id = 0 112gAbsdehijopqrklmnfutca
695 elif view.is_device_accessible: 1v
696 buf = view.get_buffer() 1v
697 dev_type, dev_id = classify_dl_device(buf) 1v
698 device_type = <_DLDeviceType>dev_type 1v
699 device_id = <int32_t>dev_id 1v
700 else:
701 device_type = _kDLCPU
702 device_id = 0
704 out_device_type[0] = device_type 112vgAbsdehijopqrklmnfutca
705 out_device_id[0] = device_id 112vgAbsdehijopqrklmnfutca
706 return 0 112vgAbsdehijopqrklmnfutca
709cdef inline int _smv_setup_dl_tensor_common(
710 DLTensor* dl_tensor,
711 StridedMemoryView view,
712 _StridedLayout layout,
713) except -1:
714 cdef object dtype_obj = view.get_dtype() 1vxywgOAbsdehijopqrklmnfutca
715 if dtype_obj is None: 1vxywgOAbsdehijopqrklmnfutca
716 raise BufferError( 1O
717 "Cannot export StridedMemoryView via DLPack without dtype information; "
718 "create the view with dtype specified."
719 )
720 _smv_dtype_numpy_to_dlpack(dtype_obj, &dl_tensor.dtype) 1vxywgAbsdehijopqrklmnfutca
721 _smv_get_dl_device(view, &dl_tensor.device.device_type, &dl_tensor.device.device_id) 1vgAbsdehijopqrklmnfutca
723 cdef int ndim = layout.base.ndim 1vgAbsdehijopqrklmnfutca
724 dl_tensor.ndim = ndim 1vgAbsdehijopqrklmnfutca
725 if layout.get_volume() == 0: 1vgAbsdehijopqrklmnfutca
726 dl_tensor.data = NULL 1f
727 else:
728 dl_tensor.data = <void*><intptr_t>view.ptr 1vgAbsdehijopqrklmnutca
729 dl_tensor.byte_offset = 0 1vgAbsdehijopqrklmnfutca
730 return 0 1vgAbsdehijopqrklmnfutca
733cdef inline int _smv_setup_dl_tensor(DLTensor* dl_tensor, StridedMemoryView view) except -1:
734 cdef _StridedLayout layout = view.get_layout() 1vxywgObsdehijopqrklmnfutca
735 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1vxywgObsdehijopqrklmnfutca
737 cdef int i
738 cdef int64_t* shape_strides = NULL 1vgbsdehijopqrklmnfutca
739 cdef int64_t* strides_src = NULL 1vgbsdehijopqrklmnfutca
740 cdef int ndim = dl_tensor.ndim 1vgbsdehijopqrklmnfutca
741 if ndim == 0: 1vgbsdehijopqrklmnfutca
742 dl_tensor.shape = NULL 1u
743 dl_tensor.strides = NULL 1u
744 else:
745 # DLPack v1.2+ requires non-NULL strides for ndim != 0.
746 shape_strides = <int64_t*>stdlib.malloc(sizeof(int64_t) * 2 * ndim) 1vgbsdehijopqrklmnftca
747 if shape_strides == NULL: 1vgbsdehijopqrklmnftca
748 raise MemoryError()
749 try: 1vgbsdehijopqrklmnftca
750 strides_src = get_strides_ptr(layout.base) 1vgbsdehijopqrklmnftca
751 for i in range(ndim): 1vgbsdehijopqrklmnftca
752 shape_strides[i] = layout.base.shape[i] 1vgbsdehijopqrklmnftca
753 shape_strides[i + ndim] = strides_src[i] 1vgbsdehijopqrklmnftca
754 except Exception:
755 stdlib.free(shape_strides)
756 raise
757 dl_tensor.shape = shape_strides 1vgbsdehijopqrklmnftca
758 dl_tensor.strides = shape_strides + ndim 1vgbsdehijopqrklmnftca
759 return 0 1vgbsdehijopqrklmnfutca
762cdef inline int _smv_setup_dltensor_borrowed(DLTensor* dl_tensor, StridedMemoryView view) except -1:
763 cdef _StridedLayout layout = view.get_layout() 1A
764 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1A
766 if dl_tensor.ndim == 0: 1A
767 dl_tensor.shape = NULL
768 dl_tensor.strides = NULL
769 else:
770 dl_tensor.shape = layout.base.shape 1A
771 # For temporary/non-owning exchange we provide explicit strides.
772 dl_tensor.strides = get_strides_ptr(layout.base) 1A
773 return 0 1A
776cdef inline int _smv_fill_managed_tensor_versioned(
777 DLManagedTensorVersioned* dlm_tensor_ver,
778 StridedMemoryView view,
779) except -1:
780 cpython.Py_INCREF(view) 1gbsdehijopqrklmnfuca
781 dlm_tensor_ver.manager_ctx = <void*>view 1gbsdehijopqrklmnfuca
782 dlm_tensor_ver.deleter = _smv_versioned_deleter 1gbsdehijopqrklmnfuca
783 dlm_tensor_ver.version.major = DLPACK_MAJOR_VERSION 1gbsdehijopqrklmnfuca
784 dlm_tensor_ver.version.minor = DLPACK_MINOR_VERSION 1gbsdehijopqrklmnfuca
785 dlm_tensor_ver.flags = DLPACK_FLAG_BITMASK_READ_ONLY if view.readonly else 0 1gbsdehijopqrklmnfuca
786 _smv_setup_dl_tensor(&dlm_tensor_ver.dl_tensor, view) 1gbsdehijopqrklmnfuca
787 return 0 1gbsdehijopqrklmnfuca
790cdef inline int _smv_fill_managed_tensor(
791 DLManagedTensor* dlm_tensor,
792 StridedMemoryView view,
793) except -1:
794 cpython.Py_INCREF(view) 1vxywOta
795 dlm_tensor.manager_ctx = <void*>view 1vxywOta
796 dlm_tensor.deleter = _smv_deleter 1vxywOta
797 _smv_setup_dl_tensor(&dlm_tensor.dl_tensor, view) 1vxywOta
798 return 0 1vta
801cdef object _smv_make_py_capsule(StridedMemoryView view, bint versioned):
802 cdef DLManagedTensor* dlm_tensor = NULL 1vxywgOsdehijopqrklmnfutca
803 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1vxywgOsdehijopqrklmnfutca
804 cdef object capsule = None 1vxywgOsdehijopqrklmnfutca
805 cdef void* tensor_ptr = NULL 1vxywgOsdehijopqrklmnfutca
806 cdef const char* capsule_name
807 try: 1vxywgOsdehijopqrklmnfutca
808 if versioned: 1vxywgOsdehijopqrklmnfutca
809 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1gsdehijopqrklmnfuca
810 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, view) 1gsdehijopqrklmnfuca
811 tensor_ptr = <void*>dlm_tensor_ver 1gsdehijopqrklmnfuca
812 capsule_name = DLPACK_VERSIONED_TENSOR_UNUSED_NAME 1gsdehijopqrklmnfuca
813 else:
814 dlm_tensor = _smv_allocate_dlm_tensor() 1vxywOta
815 _smv_fill_managed_tensor(dlm_tensor, view) 1vxywOta
816 tensor_ptr = <void*>dlm_tensor 1vta
817 capsule_name = DLPACK_TENSOR_UNUSED_NAME 1vta
818 capsule = cpython.PyCapsule_New(tensor_ptr, capsule_name, _smv_pycapsule_deleter) 1vgsdehijopqrklmnfutca
819 except Exception: 1xywO
820 if capsule is None: 1xywO
821 _smv_deleter(dlm_tensor) 1xywO
822 _smv_versioned_deleter(dlm_tensor_ver) 1xywO
823 raise 1xywO
824 return capsule 1vgsdehijopqrklmnfutca
827cdef inline StridedMemoryView _smv_from_dlpack_capsule(object capsule, object exporting_obj):
828 cdef void* data = NULL 1b
829 cdef DLTensor* dl_tensor = NULL 1b
830 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1b
831 cdef DLManagedTensor* dlm_tensor = NULL 1b
832 cdef bint is_readonly = False 1b
833 cdef const char* used_name = NULL 1b
834 if cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1b
835 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 1b
836 dlm_tensor_ver = <DLManagedTensorVersioned*>data 1b
837 dl_tensor = &dlm_tensor_ver.dl_tensor 1b
838 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 1b
839 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 1b
840 elif cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME):
841 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME)
842 dlm_tensor = <DLManagedTensor*>data
843 dl_tensor = &dlm_tensor.dl_tensor
844 is_readonly = False
845 used_name = DLPACK_TENSOR_USED_NAME
846 else:
847 raise BufferError("Invalid DLPack capsule")
849 cpython.PyCapsule_SetName(capsule, used_name) 1b
851 cdef StridedMemoryView view = StridedMemoryView.__new__(StridedMemoryView) 1b
852 view.dl_tensor = dl_tensor 1b
853 view.metadata = capsule 1b
854 view.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset) 1b
855 view.readonly = is_readonly 1b
856 view.exporting_obj = exporting_obj 1b
857 if dl_tensor.device.device_type == _kDLCPU: 1b
858 view.device_id = -1 1b
859 view.is_device_accessible = False 1b
860 elif dl_tensor.device.device_type in (_kDLCUDA, _kDLCUDAHost, _kDLCUDAManaged):
861 view.device_id = dl_tensor.device.device_id
862 view.is_device_accessible = True
863 else:
864 raise BufferError("device not supported")
865 return view 1b
868cdef int _smv_managed_tensor_allocator(
869 DLTensor* prototype,
870 DLManagedTensorVersioned** out,
871 void* error_ctx,
872 void (*SetError)(void* error_ctx, const char* kind, const char* message) noexcept,
873) noexcept with gil:
874 if out != NULL: 2Vb
875 out[0] = NULL 2Vb
876 if SetError != NULL: 2Vb
877 SetError(error_ctx, b"NotImplementedError", b"managed_tensor_allocator is not supported by StridedMemoryView")
878 cpython.PyErr_SetString(NotImplementedError, b"managed_tensor_allocator is not supported by StridedMemoryView") 2Vb
879 return -1 2Vb
882cdef int _smv_managed_tensor_from_py_object_no_sync(
883 void* py_object,
884 DLManagedTensorVersioned** out,
885) noexcept with gil:
886 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1b
887 if out == NULL: 1b
888 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL")
889 return -1
890 out[0] = NULL 1b
891 cdef object obj = <object>py_object 1b
892 if not isinstance(obj, StridedMemoryView): 1b
893 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView")
894 return -1
895 try: 1b
896 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1b
897 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, <StridedMemoryView>obj) 1b
898 except Exception:
899 _smv_versioned_deleter(dlm_tensor_ver)
900 return -1
901 out[0] = dlm_tensor_ver 1b
902 return 0 1b
905cdef int _smv_managed_tensor_to_py_object_no_sync(
906 DLManagedTensorVersioned* tensor,
907 void** out_py_object,
908) noexcept with gil:
909 cdef object capsule
910 cdef object py_view
911 if out_py_object == NULL: 2b Wb
912 cpython.PyErr_SetString(RuntimeError, b"out_py_object cannot be NULL")
913 return -1
914 out_py_object[0] = NULL 2b Wb
915 if tensor == NULL: 2b Wb
916 cpython.PyErr_SetString(RuntimeError, b"tensor cannot be NULL") 2Wb
917 return -1 2Wb
918 try: 1b
919 capsule = cpython.PyCapsule_New( 1b
920 <void*>tensor,
921 DLPACK_VERSIONED_TENSOR_UNUSED_NAME,
922 _smv_pycapsule_deleter,
923 )
924 py_view = _smv_from_dlpack_capsule(capsule, capsule) 1b
925 cpython.Py_INCREF(py_view) 1b
926 out_py_object[0] = <void*>py_view 1b
927 except Exception:
928 return -1
929 return 0 1b
932cdef int _smv_dltensor_from_py_object_no_sync(
933 void* py_object,
934 DLTensor* out,
935) noexcept with gil:
936 if out == NULL: 2XbA
937 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL")
938 return -1
939 cdef object obj = <object>py_object 2XbA
940 if not isinstance(obj, StridedMemoryView): 2XbA
941 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView") 2Xb
942 return -1 2Xb
943 try: 1A
944 _smv_setup_dltensor_borrowed(out, <StridedMemoryView>obj) 1A
945 except Exception:
946 return -1
947 return 0 1A
950cdef int _smv_current_work_stream(
951 _DLDeviceType device_type,
952 int32_t device_id,
953 void** out_current_stream,
954) noexcept with gil:
955 if out_current_stream == NULL: 22b
956 cpython.PyErr_SetString(RuntimeError, b"out_current_stream cannot be NULL")
957 return -1
958 # cuda.core has no global/current stream state today.
959 out_current_stream[0] = NULL 22b
960 return 0 22b
963cdef void _init_smv_dlpack_exchange_api():
964 global _SMV_DLPACK_EXCHANGE_API_INITED
965 if _SMV_DLPACK_EXCHANGE_API_INITED:
966 return
967 _SMV_DLPACK_EXCHANGE_API.header.version.major = DLPACK_MAJOR_VERSION
968 _SMV_DLPACK_EXCHANGE_API.header.version.minor = DLPACK_MINOR_VERSION
969 _SMV_DLPACK_EXCHANGE_API.header.prev_api = NULL
970 _SMV_DLPACK_EXCHANGE_API.managed_tensor_allocator = _smv_managed_tensor_allocator
971 _SMV_DLPACK_EXCHANGE_API.managed_tensor_from_py_object_no_sync = _smv_managed_tensor_from_py_object_no_sync
972 _SMV_DLPACK_EXCHANGE_API.managed_tensor_to_py_object_no_sync = _smv_managed_tensor_to_py_object_no_sync
973 _SMV_DLPACK_EXCHANGE_API.dltensor_from_py_object_no_sync = _smv_dltensor_from_py_object_no_sync
974 _SMV_DLPACK_EXCHANGE_API.current_work_stream = _smv_current_work_stream
975 _SMV_DLPACK_EXCHANGE_API_INITED = True
978_init_smv_dlpack_exchange_api()
979# cdef classes are immutable types in Cython 3, so inject these attributes
980# directly into the type dict.
981(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__dlpack_c_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE
982(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__c_dlpack_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE
983PyType_Modified(<PyTypeObject*>StridedMemoryView)
986cdef str get_simple_repr(obj):
987 # TODO: better handling in np.dtype objects
988 cdef object obj_class
989 cdef str obj_repr
990 if isinstance(obj, type): 19I
991 obj_class = obj
992 else:
993 obj_class = obj.__class__ 19I
994 if obj_class.__module__ in (None, "builtins"): 19I
995 obj_repr = obj_class.__name__ 19
996 else:
997 obj_repr = f"{obj_class.__module__}.{obj_class.__name__}" 19I
998 return obj_repr 19I
1002cdef bint check_has_dlpack(obj) except*:
1003 cdef bint has_dlpack
1004 if hasattr(obj, "__dlpack__") and hasattr(obj, "__dlpack_device__"): 21 2 3 8 V P J K L M N Q R S T U W X Y Z 0 Zbx y w 6 7 g ~ TbI 5 H A b s d e h i j o p q r k l m n f u t c a
1005 has_dlpack = True 11238VPJKLMNQRSTUWXYZ0xyw67gI5HAbsdehijopqrklmnfutca
1006 elif hasattr(obj, "__cuda_array_interface__"): 2Zb~ Tb
1007 has_dlpack = False 2~ Tb
1008 else:
1009 raise BufferError( 2Zb
1010 "the input object does not support any data exchange protocol")
1011 return has_dlpack 21 2 3 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g ~ TbI 5 H A b s d e h i j o p q r k l m n f u t c a
1014cdef class _StridedMemoryViewProxy:
1015 cdef readonly:
1016 object obj
1017 bint has_dlpack
1019 def __init__(self, obj: object) -> None:
1020 self.obj = obj 2J K L M N Tb
1021 self.has_dlpack = check_has_dlpack(obj) 2J K L M N Tb
1023 cpdef StridedMemoryView view(self, stream_ptr=None):
1024 if self.has_dlpack: 1JKLMN
1025 return StridedMemoryView.from_dlpack(self.obj, stream_ptr) 1JKLMN
1026 else:
1027 return StridedMemoryView.from_cuda_array_interface(self.obj, stream_ptr)
1030cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
1031 cdef int dldevice, device_id
1032 cdef bint is_device_accessible, is_readonly
1033 is_device_accessible = False 21 2 3 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
1034 dldevice, device_id = obj.__dlpack_device__() 21 2 3 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
1035 if dldevice == _kDLCPU: 21 2 3 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba Mb
1036 assert device_id == 0 23 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba
1037 device_id = -1 23 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba
1038 if stream_ptr is None: 23 8 V P J K L M N Q R S T U W X Y Z 0 x y w 6 7 g I 5 H B C D E F G A b s d e h i j o p q r k l m n f u t c Lba
1039 raise BufferError("stream=None is ambiguous with view()") 2Lb
1040 elif stream_ptr == -1: 138VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1041 stream_ptr = None 138VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1042 elif dldevice == _kDLCUDA:
1043 assert device_id >= 0
1044 is_device_accessible = True
1045 # no need to check other stream values, it's a pass-through
1046 if stream_ptr is None:
1047 raise BufferError("stream=None is ambiguous with view()")
1048 elif dldevice in (_kDLCUDAHost, _kDLCUDAManaged):
1049 is_device_accessible = True 112
1050 # just do a pass-through without any checks, as pinned/managed memory can be
1051 # accessed on both host and device
1052 else:
1053 raise BufferError("device not supported") 2Mb
1055 cdef object capsule
1056 try: 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1057 capsule = obj.__dlpack__( 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1058 stream=int(stream_ptr) if stream_ptr else None, 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1059 max_version=(DLPACK_MAJOR_VERSION, DLPACK_MINOR_VERSION)) 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1060 except TypeError: 1a
1061 capsule = obj.__dlpack__( 1a
1062 stream=int(stream_ptr) if stream_ptr else None) 1a
1064 cdef void* data = NULL 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1065 cdef DLTensor* dl_tensor
1066 cdef DLManagedTensorVersioned* dlm_tensor_ver
1067 cdef DLManagedTensor* dlm_tensor
1068 cdef const char *used_name
1069 if cpython.PyCapsule_IsValid( 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1070 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME):
1071 data = cpython.PyCapsule_GetPointer( 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1072 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME)
1073 dlm_tensor_ver = <DLManagedTensorVersioned*>data 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1074 dl_tensor = &dlm_tensor_ver.dl_tensor 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1075 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1076 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1077 elif cpython.PyCapsule_IsValid( 1a
1078 capsule, DLPACK_TENSOR_UNUSED_NAME):
1079 data = cpython.PyCapsule_GetPointer( 1a
1080 capsule, DLPACK_TENSOR_UNUSED_NAME)
1081 dlm_tensor = <DLManagedTensor*>data 1a
1082 dl_tensor = &dlm_tensor.dl_tensor 1a
1083 is_readonly = False 1a
1084 used_name = DLPACK_TENSOR_USED_NAME 1a
1085 else:
1086 assert False
1088 cpython.PyCapsule_SetName(capsule, used_name) 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1090 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1091 buf.dl_tensor = dl_tensor 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1092 buf.metadata = capsule 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1093 buf.ptr = <intptr_t>(dl_tensor.data) 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1094 buf.device_id = device_id 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1095 buf.is_device_accessible = is_device_accessible 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1096 buf.readonly = is_readonly 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1097 buf.exporting_obj = obj 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1099 return buf 11238VPJKLMNQRSTUWXYZ0xyw67gI5HBCDEFGAbsdehijopqrklmnfutca
1102@functools.lru_cache
1103def _typestr2dtype(str typestr) -> numpy.dtype:
1104 return numpy.dtype(typestr) 1!#$%'()*+,4
1107@functools.lru_cache
1108def _typestr2itemsize(str typestr) -> int:
1109 return _typestr2dtype(typestr).itemsize 1!#$%'()*+,4
1112cdef object dtype_dlpack_to_numpy(DLDataType* dtype):
1113 cdef int bits = dtype.bits 1gIBCDEFGAbsdehijopqrklmnfutca
1114 if dtype.lanes != 1: 1gIBCDEFGAbsdehijopqrklmnfutca
1115 # TODO: return a NumPy structured dtype?
1116 raise NotImplementedError(
1117 f'vector dtypes (lanes={dtype.lanes}) is not supported')
1118 if dtype.code == kDLUInt: 1gIBCDEFGAbsdehijopqrklmnfutca
1119 if bits == 8: 1klmn
1120 np_dtype = numpy.uint8 1k
1121 elif bits == 16:
1122 np_dtype = numpy.uint16 1l
1123 elif bits == 32:
1124 np_dtype = numpy.uint32 1m
1125 elif bits == 64:
1126 np_dtype = numpy.uint64 1n
1127 else:
1128 raise TypeError('uint{} is not supported.'.format(bits))
1129 elif dtype.code == kDLInt:
1130 if bits == 8: 1gIBCDEFGAopqrtca
1131 np_dtype = numpy.int8 1o
1132 elif bits == 16:
1133 np_dtype = numpy.int16 1p
1134 elif bits == 32:
1135 np_dtype = numpy.int32 1gIBCDEFGAqtca
1136 elif bits == 64:
1137 np_dtype = numpy.int64 1r
1138 else:
1139 raise TypeError('int{} is not supported.'.format(bits))
1140 elif dtype.code == kDLFloat:
1141 if bits == 16: 1bhij
1142 np_dtype = numpy.float16 1h
1143 elif bits == 32:
1144 np_dtype = numpy.float32 1i
1145 elif bits == 64:
1146 np_dtype = numpy.float64 1bj
1147 else:
1148 raise TypeError('float{} is not supported.'.format(bits))
1149 elif dtype.code == kDLComplex:
1150 # TODO(leofang): support complex32
1151 if bits == 64: 1defu
1152 np_dtype = numpy.complex64 1d
1153 elif bits == 128:
1154 np_dtype = numpy.complex128 1efu
1155 else:
1156 raise TypeError('complex{} is not supported.'.format(bits))
1157 elif dtype.code == kDLBool:
1158 if bits == 8: 1s
1159 np_dtype = numpy.bool_ 1s
1160 else:
1161 raise TypeError(f'{bits}-bit bool is not supported')
1162 elif dtype.code == kDLBfloat:
1163 if bfloat16 is not None:
1164 np_dtype = numpy.dtype("bfloat16")
1165 else:
1166 raise NotImplementedError(
1167 'Support for bfloat16 within cuda-core requires `ml_dtypes`'
1168 'to be installed.'
1169 )
1170 else:
1171 raise TypeError('Unsupported dtype. dtype code: {}'.format(dtype.code))
1173 # We want the dtype object not just the type object
1174 return numpy.dtype(np_dtype) 1gIBCDEFGAbsdehijopqrklmnfutca
1177cpdef StridedMemoryView view_as_cai(obj, stream_ptr, view=None):
1178 cdef dict cai_data = obj.__cuda_array_interface__ 20bYb3bv | Ib} ~ 4
1179 if cai_data["version"] < 3: 20bYb3bv | Ib} ~ 4
1180 raise BufferError("only CUDA Array Interface v3 or above is supported") 23b
1181 if cai_data.get("mask") is not None: 20bYbv | Ib} ~ 4
1182 raise BufferError("mask is not supported") 20b
1183 if stream_ptr is None: 2Ybv | Ib} ~ 4
1184 raise BufferError("stream=None is ambiguous with view()") 2Yb
1186 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2v | Ib} ~ 4
1187 buf.exporting_obj = obj 2v | Ib} ~ 4
1188 buf.metadata = cai_data 2v | Ib} ~ 4
1189 buf.dl_tensor = NULL 2v | Ib} ~ 4
1190 # Validate shape/strides/typestr eagerly so constructor paths fail fast.
1191 buf.get_layout() 2v | Ib} ~ 4
1192 buf.ptr, buf.readonly = cai_data["data"] 1v|}~4
1193 buf.is_device_accessible = True 1v|}~4
1194 if buf.ptr != 0: 1v|}~4
1195 buf.device_id = handle_return( 1v4
1196 driver.cuPointerGetAttribute( 1v4
1197 driver.CUpointer_attribute.CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL, 1v4
1198 buf.ptr)) 1v4
1199 else:
1200 buf.device_id = handle_return(driver.cuCtxGetDevice()) 1|}~
1202 cdef intptr_t producer_s, consumer_s
1203 cdef EventHandle h_event
1204 stream_ptr = int(stream_ptr) 1v|}~4
1205 if stream_ptr != -1: 1v|}~4
1206 stream = cai_data.get("stream") 14
1207 if stream is not None: 14
1208 producer_s = <intptr_t>(stream) 14
1209 consumer_s = <intptr_t>(stream_ptr) 14
1210 assert producer_s > 0 14
1211 # establish stream order
1212 if producer_s != consumer_s: 14
1213 with nogil: 14
1214 h_event = create_event_handle_noctx(cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING) 14
1215 HANDLE_RETURN(cydriver.cuEventRecord( 14
1216 as_cu(h_event), <cydriver.CUstream>producer_s))
1217 HANDLE_RETURN(cydriver.cuStreamWaitEvent( 14
1218 <cydriver.CUstream>consumer_s, as_cu(h_event), 0))
1219 elif _is_torch_tensor(obj):
1220 # PyTorch's __cuda_array_interface__ reports version 2 and
1221 # omits the "stream" field, so the standard CAI sync path
1222 # above is a no-op for torch tensors. This is unsafe: the
1223 # consumer has no guarantee that the producer's work is
1224 # visible. We fix this by querying PyTorch's current CUDA
1225 # stream via the AOTI stable C ABI and performing the same
1226 # event-based stream ordering.
1227 _get_tensor_bridge().sync_torch_stream(
1228 buf.device_id, <intptr_t>(stream_ptr))
1230 return buf 1v|}~4
1233cpdef StridedMemoryView view_as_array_interface(obj, view=None):
1234 cdef dict data = obj.__array_interface__ 21b4b! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1235 if data["version"] < 3: 21b4b! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1236 raise BufferError("only NumPy Array Interface v3 or above is supported") 24b
1237 if data.get("mask") is not None: 21b! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1238 raise BufferError("mask is not supported") 21b
1240 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1241 buf.exporting_obj = obj 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1242 buf.metadata = data 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1243 buf.dl_tensor = NULL 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1244 # Validate shape/strides/typestr eagerly so constructor paths fail fast.
1245 buf.get_layout() 2! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb
1246 buf.ptr, buf.readonly = data["data"] 1!#-$.%'()*/:;=?@[]^+_`{,
1247 buf.is_device_accessible = False 1!#-$.%'()*/:;=?@[]^+_`{,
1248 buf.device_id = handle_return(driver.cuCtxGetDevice()) 1!#-$.%'()*/:;=?@[]^+_`{,
1249 return buf 1!#-$.%'()*/:;=?@[]^+_`{,
1252def args_viewable_as_strided_memory(arg_indices: tuple[int, ...]) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
1253 """
1254 Decorator to create proxy objects to :obj:`StridedMemoryView` for the
1255 specified positional arguments.
1257 This allows array/tensor attributes to be accessed inside the function
1258 implementation, while keeping the function body array-library-agnostic (if
1259 desired).
1261 Inside the decorated function, the specified arguments become instances
1262 of an (undocumented) proxy type, regardless of its original source. A
1263 :obj:`StridedMemoryView` instance can be obtained by passing the (consumer)
1264 stream pointer (as a Python `int`) to the proxies's ``view()`` method. For
1265 example:
1267 .. code-block:: python
1269 @args_viewable_as_strided_memory((1,))
1270 def my_func(arg0, arg1, arg2, stream: Stream):
1271 # arg1 can be any object supporting DLPack or CUDA Array Interface
1272 view = arg1.view(stream.handle)
1273 assert isinstance(view, StridedMemoryView)
1274 ...
1276 Parameters
1277 ----------
1278 arg_indices : tuple
1279 The indices of the target positional arguments.
1280 """
1281 def wrapped_func_with_indices(func: "Callable") -> "Callable": 1JKLMN
1282 @functools.wraps(func) 1JKLMN
1283 def wrapped_func(*args, **kwargs) -> object:
1284 args = list(args) 1JKLMN
1285 cdef int idx
1286 for idx in arg_indices: 1JKLMN
1287 args[idx] = _StridedMemoryViewProxy(args[idx]) 1JKLMN
1288 return func(*args, **kwargs) 1JKLMN
1289 return wrapped_func 1JKLMN
1290 return wrapped_func_with_indices 1JKLMN
1293cdef inline _StridedLayout layout_from_dlpack(DLTensor* dl_tensor):
1294 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
1295 cdef int nbits = dl_tensor.dtype.bits * dl_tensor.dtype.lanes 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
1296 cdef int itemsize = nbits >> 3 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
1297 if (itemsize << 3) != nbits: 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
1298 raise ValueError("dl_tensor.dtype.bits must be a multiple of 8")
1299 layout.init_from_ptr(dl_tensor.ndim, dl_tensor.shape, dl_tensor.strides, itemsize) 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
1300 return layout 1VJKLMNQRSTUWXYZ0xywgIHBCDEFGAbsdehijopqrklmnfutca
1303cdef _StridedLayout layout_from_cai(object metadata):
1304 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 2v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} ~ 4
1305 cdef object shape = metadata["shape"] 2v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} ~ 4
1306 cdef object strides = metadata.get("strides") 2v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} ~ 4
1307 cdef int itemsize = _typestr2itemsize(metadata["typestr"]) 2v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} ~ 4
1308 layout.init_from_tuple(shape, strides, itemsize, True) 2v ! # - $ . % ' ( ) * / : ; = ? @ [ ] ^ + _ ` { , Kb| Ib} ~ 4
1309 return layout 1v!#-$.%'()*/:;=?@[]^+_`{,|}~4
1312cdef inline intptr_t get_data_ptr(object buffer, _StridedLayout layout) except? 0:
1313 return <intptr_t>(int(buffer.handle)) + layout.get_slice_offset_in_bytes() 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1316cdef inline int view_buffer_strided(
1317 StridedMemoryView view,
1318 object buffer,
1319 _StridedLayout layout,
1320 object dtype,
1321 bint is_readonly,
1322) except -1:
1323 if dtype is not None: 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbO 9 H B C D E F G
1324 dtype = numpy.dtype(dtype) 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbH B C D E F G
1325 if dtype.itemsize != layout.itemsize: 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbH B C D E F G
1326 raise ValueError(
1327 f"The dtype's itemsize ({dtype.itemsize}) does not match the layout's "
1328 f"itemsize ({layout.itemsize})."
1329 )
1330 # Check the layout's offset range [min_offset, max_offset] fits
1331 # within the [0, buffer.size - 1] range.
1332 # The required_size_in_bytes fails if min_offset < 0.
1333 # NB. For external memory, both positive and negative offsets can be valid,
1334 # but for a proper check we'd need to know both size and data offset,
1335 # while neither is reported by the packages.
1336 cdef bint is_allocated = buffer.memory_resource is not None 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbO 9 H B C D E F G
1337 if is_allocated and buffer.size < layout.get_required_size_in_bytes(): 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbPbabbbNbJbO 9 H B C D E F G
1338 raise ValueError( 2Nb
1339 f"Buffer size is too small for the layout. " 2Nb
1340 f"Expected at least {layout.get_required_size_in_bytes()} bytes, " 2Nb
1341 f"got {buffer.size} bytes." 2Nb
1342 )
1343 # set the public attributes
1344 view.ptr = get_data_ptr(buffer, layout) 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1345 view.device_id = buffer.device_id 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1346 view.is_device_accessible = buffer.is_device_accessible 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1347 view.readonly = is_readonly 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1348 view.exporting_obj = view._buffer = buffer 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1349 # no dlpack/cai metadata
1350 view.dl_tensor = NULL 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1351 view.metadata = None 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1352 # we get the layout from the caller
1353 view._layout = layout 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1354 view._dtype = dtype 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G
1355 return 0 2x y w cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbabbbJbO 9 H B C D E F G