Coverage for cuda/core/_memoryview.pyx: 85.18%
722 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-03 02:41 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-03 02:41 +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 TYPE_CHECKING, Any # no-cython-lint # used in string annotations below
21if TYPE_CHECKING:
22 from cuda.core._tensor_map import TensorMapDescriptorOptions
24import numpy
26from cuda.bindings cimport cydriver
27from cuda.core._resource_handles cimport (
28 EventHandle,
29 create_event_handle_noctx,
30 as_cu,
31)
33from cuda.core._utils.cuda_utils import handle_return, driver
34from cuda.core._utils.cuda_utils cimport HANDLE_RETURN
37from cuda.core._memory import Buffer
38from cuda.core._memory._buffer cimport Buffer as cyBuffer, Buffer_check_open
41# ---------------------------------------------------------------------------
42# Lazy tensor bridge (avoids loading _tensor_bridge.so until torch is used)
43# ---------------------------------------------------------------------------
45cdef object _tensor_bridge = None
46# Cache: type(obj) -> True/False for the torch tensor check.
47# Once a type is seen, we never re-check.
48cdef dict _torch_type_cache = {}
49# Tri-state: None = not checked, True/False = result of version check
50cdef object _torch_version_ok = None
52cdef inline bint _torch_version_check():
53 """Return True if 2.3 <= torch <= 2.12 (known AOTI ABI range). Memoized.
55 Lower bound: AOTI functions we use were introduced in PyTorch 2.3.
56 Upper bound: the ``pyobj_to_aten_handle`` trick relies on the
57 THPVariable struct layout (PyObject_HEAD followed by at::Tensor cdata)
58 and the identity ``AtenTensorHandle == at::Tensor*``. Both are
59 undocumented internals that could change in a future PyTorch version.
60 We cap at the latest version we have tested against; unknown versions
61 fall back to the standard DLPack/CAI paths. Bump the upper bound
62 after verifying a new PyTorch release.
63 """
64 global _torch_version_ok
65 if _torch_version_ok is not None:
66 return <bint>_torch_version_ok
67 torch = sys.modules.get("torch")
68 if torch is None:
69 _torch_version_ok = False
70 return False
71 try:
72 major, minor = int(torch.__version__.split(".")[0]), \
73 int(torch.__version__.split(".")[1])
74 _torch_version_ok = (2, 3) <= (major, minor) <= (2, 12)
75 except (ValueError, IndexError):
76 _torch_version_ok = False
77 return <bint>_torch_version_ok
80cdef inline bint _is_torch_tensor(object obj):
81 cdef type tp = type(obj) 25 6 7 9 Z T N O P Q R U V W X Y z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdb$ % k M ! L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
82 cdef object cached = _torch_type_cache.get(tp) 25 6 7 9 Z T N O P Q R U V W X Y z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdb$ % k M ! L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
83 if cached is not None: 25 6 7 9 Z T N O P Q R U V W X Y z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdb$ % k M ! L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
84 return <bint>cached 27 9 Z T N O P Q R U V W X Y B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb$ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a
85 cdef str mod = tp.__module__ or "" 25 6 9 z bbMbdb8 b e c j a Tb
86 cdef bint result = mod.startswith("torch") and hasattr(obj, "data_ptr") \ 25 6 9 z bbMbdb8 b e c j a Tb
87 and _torch_version_check()
88 _torch_type_cache[tp] = result # setdefault not needed for bools 25 6 9 z bbMbdb8 b e c j a Tb
89 return result 25 6 9 z bbMbdb8 b e c j a Tb
92cdef object _get_tensor_bridge():
93 """Bootstrap AOTI symbols, then import _tensor_bridge on first use."""
94 global _tensor_bridge
95 if _tensor_bridge is not None:
96 return _tensor_bridge
97 torch_C = sys.modules.get("torch._C")
98 if torch_C is None:
99 raise RuntimeError(
100 "torch._C is not loaded; cannot initialise the tensor bridge. "
101 "Make sure PyTorch is imported before passing a torch.Tensor.")
102 ctypes.CDLL(torch_C.__file__, mode=ctypes.RTLD_GLOBAL)
103 from cuda.core import _tensor_bridge as tb
104 _tensor_bridge = tb
105 return _tensor_bridge
108try:
109 from ml_dtypes import bfloat16
110except ImportError:
111 bfloat16 = None
113# TODO(leofang): support NumPy structured dtypes
116cdef extern from "Python.h":
117 ctypedef struct PyTypeObject:
118 void* tp_dict
119 void PyType_Modified(PyTypeObject*)
122cdef DLPackExchangeAPI _SMV_DLPACK_EXCHANGE_API
123cdef bint _SMV_DLPACK_EXCHANGE_API_INITED = False
124_SMV_DLPACK_EXCHANGE_API_CAPSULE = cpython.PyCapsule_New(
125 <void*>&_SMV_DLPACK_EXCHANGE_API,
126 b"dlpack_exchange_api",
127 NULL,
128)
131cdef class StridedMemoryView:
132 """A class holding metadata of a strided dense array/tensor.
134 A :obj:`StridedMemoryView` instance can be created in three ways:
136 1. Using the :obj:`args_viewable_as_strided_memory` decorator (recommended)
137 2. Explicit construction relying on DLPack or CUDA Array Interface, see below.
138 3. From :obj:`~_memory.Buffer` and shape and size tuples (see
139 :meth:`from_buffer` classmethod)
141 ``StridedMemoryView(obj, stream_ptr)`` can be used to create a view from
142 objects supporting either DLPack (up to v1.0) or CUDA Array Interface
143 (CAI) v3. When wrapping an arbitrary object it will try the DLPack protocol
144 first, then the CAI protocol. A :obj:`BufferError` is raised if neither is
145 supported.
147 Since either way would take a consumer stream, for DLPack it is passed to
148 ``obj.__dlpack__()`` as-is (except for :obj:`None`, see below); for CAI, a
149 stream order will be established between the consumer stream and the
150 producer stream (from ``obj.__cuda_array_interface__()["stream"]``), as if
151 ``cudaStreamWaitEvent`` is called by this method.
153 To opt-out of the stream ordering operation in either DLPack or CAI,
154 please pass ``stream_ptr=-1``. Note that this deviates (on purpose)
155 from the semantics of ``obj.__dlpack__(stream=None, ...)`` since ``cuda.core``
156 does not encourage using the (legacy) default/null stream, but is
157 consistent with the CAI's semantics. For DLPack, ``stream=-1`` will be
158 internally passed to ``obj.__dlpack__()`` instead.
160 Parameters
161 ----------
162 obj : Any
163 Any objects that supports either DLPack (up to v1.0) or CUDA Array
164 Interface (v3).
165 stream_ptr: int
166 The pointer address (as Python `int`) to the **consumer** stream.
167 Stream ordering will be properly established unless ``-1`` is passed.
170 Attributes
171 -----------
172 ptr : int
173 Pointer to the tensor buffer (as a Python `int`).
174 device_id : int
175 The device ID for where the tensor is located. It is -1 for CPU tensors
176 (meaning those only accessible from the host).
177 is_device_accessible : bool
178 Whether the tensor data can be accessed on the GPU.
179 readonly: bool
180 Whether the tensor data can be modified in place.
181 exporting_obj : Any
182 A reference to the original tensor object that is being viewed.
183 If the view is created with :meth:`from_buffer`,
184 it will be the Buffer instance passed to the method.
186 """
187 def __init__(self, obj: object = None, stream_ptr: int | None = None) -> None:
188 cdef str clsname = self.__class__.__name__ 20 1 2 3 4 Vbcb
189 if obj is not None: 20 1 2 3 4 Vbcb
190 # populate self's attributes
191 if check_has_dlpack(obj): 20 1 2 3 4 cb
192 warnings.warn( 101234
193 f"Constructing a {clsname} directly from a DLPack-supporting object is deprecated; " 101234
194 "Use `StridedMemoryView.from_dlpack` or `StridedMemoryView.from_any_interface` instead.",
195 DeprecationWarning, 101234
196 stacklevel=2,
197 )
198 view_as_dlpack(obj, stream_ptr, self) 101234
199 else:
200 warnings.warn( 2cb
201 f"Constructing a {clsname} directly from a CUDA-array-interface-supporting object is deprecated; " 2D cb
202 "Use `StridedMemoryView.from_cuda_array_interface` or `StridedMemoryView.from_any_interface` instead.",
203 DeprecationWarning, 2cb
204 stacklevel=2,
205 )
206 view_as_cai(obj, stream_ptr, self) 2cb
207 else:
208 warnings.warn( 2Vb
209 f"Constructing an empty {clsname} is deprecated; " 2Vb
210 "use one of the classmethods `from_dlpack`, `from_cuda_array_interface` or `from_any_interface` "
211 "to construct a StridedMemoryView from an object",
212 DeprecationWarning, 2Vb
213 stacklevel=2,
214 )
216 @classmethod
217 def from_dlpack(cls, obj: object, stream_ptr: int | None=None) -> StridedMemoryView:
218 """Create a view from an object supporting the `DLPack <https://dmlc.github.io/dlpack/latest/>`_ protocol.
220 Parameters
221 ----------
222 obj : object
223 An object implementing the `DLPack <https://dmlc.github.io/dlpack/latest/>`_ protocol
224 (via ``__dlpack__``).
225 stream_ptr : int, optional
226 Stream pointer for synchronization. If ``None``, no synchronization is performed.
227 """
228 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 25 6 7 9 Z T N O P Q R U V W X Y B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
229 if _is_torch_tensor(obj): 25 6 7 9 Z T N O P Q R U V W X Y B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
230 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf)
231 return buf
232 view_as_dlpack(obj, stream_ptr, buf) 2D 5 6 7 9 Z T N O P Q R U V W X Y B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
233 return buf 15679ZTNOPQRUVWXYBCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
235 @classmethod
236 def from_cuda_array_interface(cls, obj: object, stream_ptr: int | None=None) -> StridedMemoryView:
237 """Create a view from an object supporting the `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_ protocol.
239 Parameters
240 ----------
241 obj : object
242 An object implementing the `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_ protocol.
243 stream_ptr : int, optional
244 Stream pointer for synchronization. If ``None``, no synchronization is performed.
245 """
246 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 2D z bbMbdb8
247 if _is_torch_tensor(obj): 2z bbMbdb8
248 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf)
249 return buf
250 view_as_cai(obj, stream_ptr, buf) 2z bbMbdb8
251 return buf 2D z bbdb8
253 @classmethod
254 def from_array_interface(cls, obj: object) -> StridedMemoryView:
255 """Create a view from an object supporting the `__array_interface__ <https://numpy.org/doc/stable/reference/arrays.interface.html>`_ protocol.
257 Parameters
258 ----------
259 obj : object
260 An object implementing the `__array_interface__ <https://numpy.org/doc/stable/reference/arrays.interface.html>`_ protocol (e.g., a numpy array).
261 """
262 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
263 if _is_torch_tensor(obj): 2D ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
264 _get_tensor_bridge().view_as_torch_tensor(obj, None, buf)
265 return buf
266 view_as_array_interface(obj, buf) 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
267 return buf 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab?
269 @classmethod
270 def from_any_interface(cls, obj: object, stream_ptr: int | None = None) -> StridedMemoryView:
271 """Create a view by automatically selecting the best available protocol.
273 Tries `DLPack <https://dmlc.github.io/dlpack/latest/>`_ first, then falls back to
274 `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_.
275 ``torch.Tensor`` objects are transparently handled via a fast AOTI path
276 regardless of which protocol is selected.
278 Parameters
279 ----------
280 obj : object
281 An object implementing `DLPack <https://dmlc.github.io/dlpack/latest/>`_ or
282 `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_.
283 stream_ptr : int, optional
284 Stream pointer for synchronization. If ``None``, no synchronization is performed.
285 """
286 if check_has_dlpack(obj): 25 6 7 9 Z T U V W X Y 3bB C A $ % k M ! L E d w g h l m n s t u v o p q r i y x f b e c j a
287 return cls.from_dlpack(obj, stream_ptr) 15679ZTUVWXYBCA$%kM!LEdwghlmnstuvopqriyxfbecja
288 return cls.from_cuda_array_interface(obj, stream_ptr)
290 @classmethod
291 def from_buffer(
292 cls,
293 buffer : Buffer,
294 shape : tuple[int, ...],
295 strides : tuple[int, ...] | None = None,
296 *,
297 itemsize : int | None = None,
298 dtype : numpy.dtype | None = None,
299 is_readonly : bool = False
300 ) -> StridedMemoryView:
301 """
302 Creates a :obj:`StridedMemoryView` instance from a :obj:`~_memory.Buffer` and shape and strides tuples.
303 The Buffer can be either allocation coming from a :obj:`MemoryResource` or an external allocation
304 wrapped in a :obj:`~_memory.Buffer` object with ``Buffer.from_handle(ptr, size, owner=...)``.
306 .. caution::
307 When creating a :obj:`StridedMemoryView` from a :obj:`~_memory.Buffer`,
308 no synchronization is performed. It is the user's responsibility to ensure
309 the data in ``buffer`` is properly synchronized when consuming the view.
311 Parameters
312 ----------
313 buffer : :obj:`~_memory.Buffer`
314 The buffer to create the view from.
315 shape : :obj:`tuple`
316 The layout describing the shape, strides and itemsize of the elements in
317 the buffer.
318 strides : :obj:`tuple`
319 The layout describing the shape, strides and itemsize of the elements in
320 the buffer.
321 dtype : :obj:`numpy.dtype`
322 Optional dtype.
323 If specified, the dtype's itemsize must match the layout's itemsize.
324 is_readonly : bool, optional
325 Whether the mark the view as readonly.
326 """
327 cdef StridedMemoryView view = StridedMemoryView.__new__(cls) 2NbObgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbYbWbebfbRbPbS #
328 if itemsize is None and dtype is None: 2NbObgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbYbWbebfbRbPbS #
329 raise ValueError("Either itemsize or dtype must be specified") 2Yb
330 if itemsize is not None and dtype is not None and itemsize != dtype.itemsize: 2NbObgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbWbebfbRbPbS #
331 raise ValueError( 2Wb
332 f"itemsize ({itemsize}) does not match dtype.itemsize ({dtype.itemsize})" 2Wb
333 )
334 # (itemsize is None XOR dtype is None) OR they are equal
335 view_buffer_strided( 2NbObgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS #
336 view,
337 buffer,
338 _StridedLayout(shape=shape, strides=strides, itemsize=getattr(dtype, "itemsize", itemsize)), 2D NbObgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS #
339 dtype,
340 is_readonly,
341 )
342 return view 2NbObgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS #
344 def __dealloc__(self) -> None:
345 if self.dl_tensor == NULL: 2NbOb5 6 7 9 Z T N O P Q R U V W X Y 0 1 2 3 4 z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbYbWbebfbRbPbbbMbdb$ % k S Vbcb# M ! L 8 F G H I J K E w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
346 return 2NbObz B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbYbWbebfbRbPbbbMbdbS Vbcb# ! L 8 F G H I J K SbTb
348 if cpython.PyCapsule_IsValid( 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfbecja
349 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME): 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfbecja
350 data = cpython.PyCapsule_GetPointer( 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfeja
351 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME) 1D5679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfeja
352 dlm_tensor_ver = <DLManagedTensorVersioned*>data 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfeja
353 if dlm_tensor_ver.deleter != NULL: 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfeja
354 dlm_tensor_ver.deleter(dlm_tensor_ver) 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEwghlmnstuvopqriyxfea
355 elif cpython.PyCapsule_IsValid( 1bca
356 self.metadata, DLPACK_TENSOR_USED_NAME): 1bca
357 data = cpython.PyCapsule_GetPointer( 1bca
358 self.metadata, DLPACK_TENSOR_USED_NAME) 1Dbca
359 dlm_tensor = <DLManagedTensor*>data 1bca
360 if dlm_tensor.deleter != NULL: 1bca
361 dlm_tensor.deleter(dlm_tensor) 1ba
363 def view(
364 self, layout : _StridedLayout | None = None, dtype : numpy.dtype | None = None
365 ) -> StridedMemoryView:
366 """
367 Creates a new view with adjusted layout and dtype.
368 Same as calling :meth:`from_buffer` with the current buffer.
369 """
370 cdef StridedMemoryView view = StridedMemoryView.__new__(self.__class__) 2D B C A ebfb! L F G H I J K
371 if layout is None and dtype is None: 2B C A ebfb! L F G H I J K
372 return self 1!
373 if layout is None: 2B C A ebfbL F G H I J K
374 layout = self.get_layout() 1BCAL
375 if dtype is None: 2B C A ebfbL F G H I J K
376 dtype = self.get_dtype() 2ebfbF G H I J K
377 view_buffer_strided(view, self.get_buffer(), layout, dtype, self.readonly) 2B C A ebfbL F G H I J K
378 return view 2D B C A ebfbL F G H I J K
380 def as_tensor_map(
381 self,
382 box_dim: tuple[int, ...] | None = None,
383 *,
384 options: TensorMapDescriptorOptions | None = None,
385 element_strides: tuple[int, ...] | None = None,
386 data_type: object = None,
387 interleave: object = None,
388 swizzle: object = None,
389 l2_promotion: object = None,
390 oob_fill: object = None,
391 ) -> object:
392 """Create a tiled :obj:`TensorMapDescriptor` from this view.
394 This is the public entry point for creating tiled tensor map
395 descriptors in ``cuda.core``. Pass either ``box_dim`` and the
396 individual keyword arguments directly, or provide bundled tiled
397 options via ``options=``.
398 """
399 from cuda.core._tensor_map import TensorMapDescriptor 1T
401 kwargs = {} 1DT
402 if options is not None: 1T
403 kwargs["options"] = options
404 if element_strides is not None: 1T
405 kwargs["element_strides"] = element_strides 1T
406 if data_type is not None: 1T
407 kwargs["data_type"] = data_type 1T
408 if interleave is not None: 1T
409 kwargs["interleave"] = interleave
410 if swizzle is not None: 1T
411 kwargs["swizzle"] = swizzle 1T
412 if l2_promotion is not None: 1T
413 kwargs["l2_promotion"] = l2_promotion 1DT
414 if oob_fill is not None: 1T
415 kwargs["oob_fill"] = oob_fill 1T
416 return TensorMapDescriptor._from_tiled(self, box_dim, **kwargs) 1T
418 def copy_from(
419 self,
420 other: StridedMemoryView,
421 stream: Stream,
422 allocator: object = None,
423 blocking: bool | None = None,
424 ) -> None:
425 """
426 Copies the data from the other view into this view.
428 The copy can be performed between following memory spaces:
429 host-to-device, device-to-host, device-to-device (on the same device).
431 Parameters
432 ----------
433 other : StridedMemoryView
434 The view to copy data from.
435 stream : Stream | None, optional
436 The stream to schedule the copy on.
437 allocator : MemoryResource | None, optional
438 If temporary buffers are needed, the specified memory resources
439 will be used to allocate the memory. If not specified, default
440 resources will be used.
441 blocking : bool | None, optional
442 Whether the call should block until the copy is complete.
443 * ``True``: the ``stream`` is synchronized with the host at the end of the call,
444 blocking until the copy is complete.
445 * ``False``: if possible, the call returns immediately once the copy is scheduled.
446 However, in some cases of host-to-device or device-to-host copies, the call may
447 still synchronize with the host if necessary.
448 * ``None`` (default):
449 * for device-to-device, it defaults to ``False`` (non-blocking),
450 * for host-to-device or device-to-host, it defaults to ``True`` (blocking).
451 """
452 raise NotImplementedError("Sorry, not supported: copy_from") 1$
454 def copy_to(
455 self,
456 other: StridedMemoryView,
457 stream: Stream | None = None,
458 allocator: object = None,
459 blocking: bool | None = None,
460 ) -> None:
461 """
462 Copies the data from this view into the ``other`` view.
464 For details, see :meth:`copy_from`.
465 """
466 raise NotImplementedError("Sorry, not supported: copy_to") 1%
468 def __dlpack__(
469 self,
470 *,
471 stream: int | None = None,
472 max_version: tuple[int, int] | None = None,
473 dl_device: tuple[int, int] | None = None,
474 copy: bool | None = None,
475 ) -> object:
476 # Similar to Buffer.__dlpack__: no implicit synchronization is performed.
477 if dl_device is not None: 17zBCAkSwghlmnstuvopqriyxfbecja
478 raise BufferError("Sorry, not supported: dl_device other than None") 17
479 if copy is True: 17zBCAkSwghlmnstuvopqriyxfbecja
480 raise BufferError("Sorry, not supported: copy=True") 17
482 cdef bint versioned
483 if max_version is None: 17zBCAkSwghlmnstuvopqriyxfbecja
484 versioned = False 1zBCASxbca
485 else:
486 if not isinstance(max_version, tuple) or len(max_version) != 2: 17kwghlmnstuvopqriyfbeja
487 raise BufferError(f"Expected max_version tuple[int, int], got {max_version}") 17
488 versioned = max_version >= (1, 0) 1kwghlmnstuvopqriyfbeja
490 # NOTE: stream is accepted for protocol compatibility but not used.
491 cdef object capsule = _smv_make_py_capsule(self, versioned) 1zBCAkSwghlmnstuvopqriyxfbecja
492 return capsule 1zkwghlmnstuvopqriyxfbecja
494 def __dlpack_device__(self) -> tuple[int, int]:
495 cdef _DLDeviceType device_type
496 cdef int32_t device_id
497 _smv_get_dl_device(self, &device_type, &device_id) 1D56zkbecja
498 return (<int>device_type, int(device_id)) 156zkbecja
500 @property
501 def _layout(self) -> _StridedLayout:
502 """
503 The layout of the tensor. For StridedMemoryView created from DLPack or CAI,
504 the layout is inferred from the tensor object's metadata.
505 """
506 return self.get_layout() 2gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbVbL F G H I J K
508 @property
509 def size(self) -> int:
510 return self.get_layout().get_volume() 2N O P Q R U V W X Y 0 1 2 3 4 ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? bb
512 @property
513 def shape(self) -> tuple[int, ...]:
514 """
515 Shape of the tensor.
516 """
517 return self.get_layout().get_shape_tuple() 2NbObZ N O P Q R U V W X Y 0 1 2 3 4 ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbbbdbcb# M 8 F G H I J K d b e
519 @property
520 def strides(self) -> tuple[int, ...] | None:
521 """
522 Strides of the tensor (in **counts**, not bytes).
523 """
524 return self.get_layout().get_strides_tuple() 2Z N O P Q R U V W X Y 0 1 2 3 4 ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbbbdb# M
526 @property
527 def dtype(self) -> numpy.dtype | None:
528 """
529 Data type of the tensor.
531 Supports standard NumPy dtypes as well as narrow data types (e.g., ``bfloat16``)
532 when the optional `ml_dtypes <https://github.com/jax-ml/ml_dtypes>`_ package is
533 installed. If ``ml_dtypes`` is not available and such a tensor is encountered,
534 a :obj:`NotImplementedError` will be raised.
535 """
536 return self.get_dtype() 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbPb# M L F G H I J K
538 def __repr__(self) -> str:
539 return (f"StridedMemoryView(ptr={self.ptr},\n" 1#M
540 + f" shape={self.shape},\n" 1#M
541 + f" strides={self.strides},\n" 1D#M
542 + f" itemsize={self._layout.itemsize},\n" 1#M
543 + f" dtype={get_simple_repr(self.dtype)},\n" 1#M
544 + f" device_id={self.device_id},\n" 1#M
545 + f" is_device_accessible={self.is_device_accessible},\n" 1#M
546 + f" readonly={self.readonly},\n" 1#M
547 + f" exporting_obj={get_simple_repr(self.exporting_obj)})") 1#M
549 @cython.critical_section
550 cdef inline _StridedLayout get_layout(self):
551 cdef _StridedLayout layout
552 if self._layout is None: 2NbObZ N O P Q R U V W X Y 0 1 2 3 4 z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbbbMbdbk S Vbcb# M L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
553 if self.dl_tensor: 2Z N O P Q R U V W X Y 0 1 2 3 4 z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbk VbcbM L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
554 layout = layout_from_dlpack(self.dl_tensor) 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
555 elif self.metadata is not None: 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbVbcb8
556 layout = layout_from_cai(self.metadata) 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbcb8
557 else:
558 raise ValueError("Cannot infer layout from the exporting object") 2Vb
559 if self._layout is None: 2Z N O P Q R U V W X Y 0 1 2 3 4 z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? bbdbk cbM L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
560 self._layout = layout 2Z N O P Q R U V W X Y 0 1 2 3 4 z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? bbdbk cbM L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
561 return self._layout 2NbObZ N O P Q R U V W X Y 0 1 2 3 4 z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbbbdbk S cb# M L 8 F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
563 @cython.critical_section
564 cdef inline object get_buffer(self):
565 """
566 Returns Buffer instance with the underlying data.
567 If the SMV was created from a Buffer, it will return the same Buffer instance.
568 Otherwise, it will create a new instance with owner set to the exporting object.
569 """
570 cdef object buffer
571 if self._buffer is None: 2z B C A ebfbL F G H I J K
572 if isinstance(self.exporting_obj, Buffer): 1zBCALFGHIJK
573 buffer = self.exporting_obj
574 else:
575 buffer = Buffer.from_handle(self.ptr, 0, owner=self.exporting_obj) 1zBCALFGHIJK
576 if self._buffer is None: 1zBCALFGHIJK
577 self._buffer = buffer 1zBCALFGHIJK
578 return self._buffer 2z B C A ebfbL F G H I J K
580 @cython.critical_section
581 cdef inline object get_dtype(self):
582 cdef object dtype
583 if self._dtype is None: 2D z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbk S # M L F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
584 dtype = None 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? k S # M F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
585 if self.dl_tensor != NULL: 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? k S # M F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
586 dtype = dtype_dlpack_to_numpy(&self.dl_tensor.dtype) 1kMFGHIJKEdwghlmnstuvopqriyxfbecja
587 elif isinstance(self.metadata, int): 2D z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? S #
588 # AOTI dtype code stored by the torch tensor bridge
589 dtype = _get_tensor_bridge().resolve_aoti_dtype(
590 self.metadata)
591 elif self.metadata is not None: 2D z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? S #
592 dtype = _typestr2dtype(self.metadata["typestr"]) 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab?
593 if self._dtype is None: 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? k S # M F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
594 self._dtype = dtype 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? k S # M F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
595 return self._dtype 2D z B C A ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbk S # M L F G H I J K E d w g h l m n s t u v o p q r i y x f b e c j a
598cdef void _smv_pycapsule_deleter(object capsule) noexcept:
599 cdef DLManagedTensor* dlm_tensor
600 cdef DLManagedTensorVersioned* dlm_tensor_ver
601 # Do not invoke the deleter on a used capsule.
602 if cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME): 1zkwghlmnstuvopqriyxfbea
603 dlm_tensor = <DLManagedTensor*>( 1zx
604 cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME) 1zx
605 )
606 if dlm_tensor.deleter: 1zx
607 dlm_tensor.deleter(dlm_tensor) 1zx
608 elif cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1kwghlmnstuvopqriyfbea
609 dlm_tensor_ver = <DLManagedTensorVersioned*>( 1Df
610 cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 1f
611 )
612 if dlm_tensor_ver.deleter: 1f
613 dlm_tensor_ver.deleter(dlm_tensor_ver) 1Df
616cdef inline void _smv_release_export_resources(void* manager_ctx, int64_t* shape_ptr) noexcept with gil:
617 if shape_ptr: 1zBCAkSwghlmnstuvopqriyxfbecja
618 stdlib.free(shape_ptr) 1zkwghlmnstuvopqrixfbecja
619 if manager_ctx: 1zBCAkSwghlmnstuvopqriyxfbecja
620 cpython.Py_DECREF(<object>manager_ctx) 1zBCAkSwghlmnstuvopqriyxfbecja
623cdef void _smv_deleter(DLManagedTensor* tensor) noexcept with gil:
624 if tensor: 1zBCASxbca
625 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1zBCASxbca
626 tensor.manager_ctx = NULL 1zBCASxbca
627 stdlib.free(tensor) 1zBCASxbca
630cdef void _smv_versioned_deleter(DLManagedTensorVersioned* tensor) noexcept with gil:
631 if tensor: 1BCAkSwghlmnstuvopqriyfbeja
632 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1Dkwghlmnstuvopqriyfbeja
633 tensor.manager_ctx = NULL 1kwghlmnstuvopqriyfbeja
634 stdlib.free(tensor) 1kwghlmnstuvopqriyfbeja
637cdef inline DLManagedTensorVersioned* _smv_allocate_dlm_tensor_versioned() except? NULL:
638 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1kdwghlmnstuvopqriyfbeja
639 dlm_tensor_ver = <DLManagedTensorVersioned*>stdlib.malloc(sizeof(DLManagedTensorVersioned)) 1kdwghlmnstuvopqriyfbeja
640 if dlm_tensor_ver == NULL: 1Dkdwghlmnstuvopqriyfbeja
641 raise MemoryError()
642 dlm_tensor_ver.dl_tensor.shape = NULL 1kdwghlmnstuvopqriyfbeja
643 dlm_tensor_ver.manager_ctx = NULL 1kdwghlmnstuvopqriyfbeja
644 return dlm_tensor_ver 1kdwghlmnstuvopqriyfbeja
647cdef inline DLManagedTensor* _smv_allocate_dlm_tensor() except? NULL:
648 cdef DLManagedTensor* dlm_tensor = NULL 1DzBCASxbca
649 dlm_tensor = <DLManagedTensor*>stdlib.malloc(sizeof(DLManagedTensor)) 1zBCASxbca
650 if dlm_tensor == NULL: 1zBCASxbca
651 raise MemoryError()
652 dlm_tensor.dl_tensor.shape = NULL 1zBCASxbca
653 dlm_tensor.manager_ctx = NULL 1zBCASxbca
654 return dlm_tensor 1zBCASxbca
657cdef inline int _smv_dtype_numpy_to_dlpack(object dtype_obj, DLDataType* out_dtype) except -1:
658 cdef object np_dtype = numpy.dtype(dtype_obj) 1zBCAkEdwghlmnstuvopqriyxfbecja
659 if np_dtype.fields is not None: 1zBCAkEdwghlmnstuvopqriyxfbecja
660 raise BufferError("Structured dtypes are not supported for DLPack export") 1C
661 if not np_dtype.isnative and np_dtype.byteorder not in ("=", "|"): 1zBAkEdwghlmnstuvopqriyxfbecja
662 raise BufferError("Non-native-endian dtypes are not supported for DLPack export") 1B
664 cdef str kind = np_dtype.kind 1zAkEdwghlmnstuvopqriyxfbecja
665 cdef int bits = np_dtype.itemsize * 8 1zAkEdwghlmnstuvopqriyxfbecja
666 cdef uint8_t code
667 if kind == "b": 1zAkEdwghlmnstuvopqriyxfbecja
668 if bits != 8: 1Dw
669 raise BufferError(f"Unsupported bool dtype itemsize: {np_dtype.itemsize}")
670 code = <uint8_t>kDLBool 1w
671 elif kind == "i": 1zAkEdghlmnstuvopqriyxfbecja
672 if bits not in (8, 16, 32, 64): 1kEstuvxfbecja
673 raise BufferError(f"Unsupported signed integer dtype: {np_dtype}")
674 code = <uint8_t>kDLInt 1kEstuvxfbecja
675 elif kind == "u": 1zAdghlmnopqriy
676 if bits not in (8, 16, 32, 64): 1opqr
677 raise BufferError(f"Unsupported unsigned integer dtype: {np_dtype}")
678 code = <uint8_t>kDLUInt 1Dopqr
679 elif kind == "f": 1zAdghlmniy
680 if bits not in (16, 32, 64): 1zdlmn
681 raise BufferError(f"Unsupported floating dtype: {np_dtype}")
682 code = <uint8_t>kDLFloat 1zdlmn
683 elif kind == "c": 1DAghiy
684 if bits not in (64, 128): 1ghiy
685 raise BufferError(f"Unsupported complex dtype: {np_dtype}")
686 code = <uint8_t>kDLComplex 1ghiy
687 else:
688 raise BufferError(f"Unsupported dtype for DLPack export: {np_dtype}") 1A
690 out_dtype.code = code 1zkEdwghlmnstuvopqriyxfbecja
691 out_dtype.bits = <uint8_t>bits 1zkEdwghlmnstuvopqriyxfbecja
692 out_dtype.lanes = <uint16_t>1 1zkEdwghlmnstuvopqriyxfbecja
693 return 0 1zkEdwghlmnstuvopqriyxfbecja
696cdef inline int _smv_get_dl_device(
697 StridedMemoryView view,
698 _DLDeviceType* out_device_type,
699 int32_t* out_device_id,
700) except -1:
701 cdef _DLDeviceType device_type
702 cdef int32_t device_id
703 cdef object buf
704 if view.dl_tensor != NULL: 156zkEdwghlmnstuvopqriyxfbecja
705 device_type = view.dl_tensor.device.device_type 156kEdwghlmnstuvopqriyxfbecja
706 if device_type == _kDLCUDA: 156kEdwghlmnstuvopqriyxfbecja
707 device_id = view.dl_tensor.device.device_id
708 else:
709 # CPU, CUDAHost, and CUDAManaged use device_id=0 in DLPack.
710 device_id = 0 156kEdwghlmnstuvopqriyxfbecja
711 elif view.is_device_accessible: 1z
712 buf = view.get_buffer() 1z
713 dev_type, dev_id = classify_dl_device(buf) 1z
714 device_type = <_DLDeviceType>dev_type 1z
715 device_id = <int32_t>dev_id 1z
716 else:
717 device_type = _kDLCPU
718 device_id = 0
720 out_device_type[0] = device_type 156zkEdwghlmnstuvopqriyxfbecja
721 out_device_id[0] = device_id 156zkEdwghlmnstuvopqriyxfbecja
722 return 0 156zkEdwghlmnstuvopqriyxfbecja
725cdef inline int _smv_setup_dl_tensor_common(
726 DLTensor* dl_tensor,
727 StridedMemoryView view,
728 _StridedLayout layout,
729) except -1:
730 cdef object dtype_obj = view.get_dtype() 1zBCAkSEdwghlmnstuvopqriyxfbecja
731 if dtype_obj is None: 1zBCAkSEdwghlmnstuvopqriyxfbecja
732 raise BufferError( 1S
733 "Cannot export StridedMemoryView via DLPack without dtype information; "
734 "create the view with dtype specified."
735 )
736 _smv_dtype_numpy_to_dlpack(dtype_obj, &dl_tensor.dtype) 1zBCAkEdwghlmnstuvopqriyxfbecja
737 _smv_get_dl_device(view, &dl_tensor.device.device_type, &dl_tensor.device.device_id) 1zkEdwghlmnstuvopqriyxfbecja
739 cdef int ndim = layout.base.ndim 1zkEdwghlmnstuvopqriyxfbecja
740 dl_tensor.ndim = ndim 1zkEdwghlmnstuvopqriyxfbecja
741 if layout.get_volume() == 0: 1zkEdwghlmnstuvopqriyxfbecja
742 dl_tensor.data = NULL 1i
743 else:
744 dl_tensor.data = <void*><intptr_t>view.ptr 1zkEdwghlmnstuvopqryxfbecja
745 dl_tensor.byte_offset = 0 1zkEdwghlmnstuvopqriyxfbecja
746 return 0 1zkEdwghlmnstuvopqriyxfbecja
749cdef inline int _smv_setup_dl_tensor(DLTensor* dl_tensor, StridedMemoryView view) except -1:
750 cdef _StridedLayout layout = view.get_layout() 1zBCAkSdwghlmnstuvopqriyxfbecja
751 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1zBCAkSdwghlmnstuvopqriyxfbecja
753 cdef int i
754 cdef int64_t* shape_strides = NULL 1zkdwghlmnstuvopqriyxfbecja
755 cdef int64_t* strides_src = NULL 1zkdwghlmnstuvopqriyxfbecja
756 cdef int ndim = dl_tensor.ndim 1zkdwghlmnstuvopqriyxfbecja
757 if ndim == 0: 1zkdwghlmnstuvopqriyxfbecja
758 dl_tensor.shape = NULL 1y
759 dl_tensor.strides = NULL 1y
760 else:
761 # DLPack v1.2+ requires non-NULL strides for ndim != 0.
762 shape_strides = <int64_t*>stdlib.malloc(sizeof(int64_t) * 2 * ndim) 1zkdwghlmnstuvopqrixfbecja
763 if shape_strides == NULL: 1zkdwghlmnstuvopqrixfbecja
764 raise MemoryError()
765 try: 1zkdwghlmnstuvopqrixfbecja
766 strides_src = get_strides_ptr(layout.base) 1zkdwghlmnstuvopqrixfbecja
767 for i in range(ndim): 1zkdwghlmnstuvopqrixfbecja
768 shape_strides[i] = layout.base.shape[i] 1zkdwghlmnstuvopqrixfbecja
769 shape_strides[i + ndim] = strides_src[i] 1zkdwghlmnstuvopqrixfbecja
770 except Exception:
771 stdlib.free(shape_strides)
772 raise
773 dl_tensor.shape = shape_strides 1zkdwghlmnstuvopqrixfbecja
774 dl_tensor.strides = shape_strides + ndim 1zkdwghlmnstuvopqrixfbecja
775 return 0 1zkdwghlmnstuvopqriyxfbecja
778cdef inline int _smv_setup_dltensor_borrowed(DLTensor* dl_tensor, StridedMemoryView view) except -1:
779 cdef _StridedLayout layout = view.get_layout() 1E
780 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1E
782 if dl_tensor.ndim == 0: 1E
783 dl_tensor.shape = NULL
784 dl_tensor.strides = NULL
785 else:
786 dl_tensor.shape = layout.base.shape 1E
787 # For temporary/non-owning exchange we provide explicit strides.
788 dl_tensor.strides = get_strides_ptr(layout.base) 1E
789 return 0 1E
792cdef inline int _smv_fill_managed_tensor_versioned(
793 DLManagedTensorVersioned* dlm_tensor_ver,
794 StridedMemoryView view,
795) except -1:
796 cpython.Py_INCREF(view) 1kdwghlmnstuvopqriyfbeja
797 dlm_tensor_ver.manager_ctx = <void*>view 1kdwghlmnstuvopqriyfbeja
798 dlm_tensor_ver.deleter = _smv_versioned_deleter 1kdwghlmnstuvopqriyfbeja
799 dlm_tensor_ver.version.major = DLPACK_MAJOR_VERSION 1kdwghlmnstuvopqriyfbeja
800 dlm_tensor_ver.version.minor = DLPACK_MINOR_VERSION 1kdwghlmnstuvopqriyfbeja
801 dlm_tensor_ver.flags = DLPACK_FLAG_BITMASK_READ_ONLY if view.readonly else 0 1kdwghlmnstuvopqriyfbeja
802 _smv_setup_dl_tensor(&dlm_tensor_ver.dl_tensor, view) 1kdwghlmnstuvopqriyfbeja
803 return 0 1kdwghlmnstuvopqriyfbeja
806cdef inline int _smv_fill_managed_tensor(
807 DLManagedTensor* dlm_tensor,
808 StridedMemoryView view,
809) except -1:
810 cpython.Py_INCREF(view) 1zBCASxbca
811 dlm_tensor.manager_ctx = <void*>view 1zBCASxbca
812 dlm_tensor.deleter = _smv_deleter 1zBCASxbca
813 _smv_setup_dl_tensor(&dlm_tensor.dl_tensor, view) 1zBCASxbca
814 return 0 1zxbca
817cdef object _smv_make_py_capsule(StridedMemoryView view, bint versioned):
818 cdef DLManagedTensor* dlm_tensor = NULL 1zBCAkSwghlmnstuvopqriyxfbecja
819 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1zBCAkSwghlmnstuvopqriyxfbecja
820 cdef object capsule = None 1zBCAkSwghlmnstuvopqriyxfbecja
821 cdef void* tensor_ptr = NULL 1zBCAkSwghlmnstuvopqriyxfbecja
822 cdef const char* capsule_name
823 try: 1zBCAkSwghlmnstuvopqriyxfbecja
824 if versioned: 1zBCAkSwghlmnstuvopqriyxfbecja
825 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1kwghlmnstuvopqriyfbeja
826 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, view) 1kwghlmnstuvopqriyfbeja
827 tensor_ptr = <void*>dlm_tensor_ver 1kwghlmnstuvopqriyfbeja
828 capsule_name = DLPACK_VERSIONED_TENSOR_UNUSED_NAME 1kwghlmnstuvopqriyfbeja
829 else:
830 dlm_tensor = _smv_allocate_dlm_tensor() 1zBCASxbca
831 _smv_fill_managed_tensor(dlm_tensor, view) 1zBCASxbca
832 tensor_ptr = <void*>dlm_tensor 1zxbca
833 capsule_name = DLPACK_TENSOR_UNUSED_NAME 1zxbca
834 capsule = cpython.PyCapsule_New(tensor_ptr, capsule_name, _smv_pycapsule_deleter) 1zkwghlmnstuvopqriyxfbecja
835 except Exception: 1BCAS
836 if capsule is None: 1BCAS
837 _smv_deleter(dlm_tensor) 1BCAS
838 _smv_versioned_deleter(dlm_tensor_ver) 1BCAS
839 raise 1BCAS
840 return capsule 1zkwghlmnstuvopqriyxfbecja
843cdef inline StridedMemoryView _smv_from_dlpack_capsule(object capsule, object exporting_obj):
844 cdef void* data = NULL 1d
845 cdef DLTensor* dl_tensor = NULL 1d
846 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1d
847 cdef DLManagedTensor* dlm_tensor = NULL 1d
848 cdef bint is_readonly = False 1d
849 cdef const char* used_name = NULL 1d
850 if cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1d
851 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 1d
852 dlm_tensor_ver = <DLManagedTensorVersioned*>data 1d
853 dl_tensor = &dlm_tensor_ver.dl_tensor 1d
854 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 1d
855 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 1d
856 elif cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME):
857 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME)
858 dlm_tensor = <DLManagedTensor*>data
859 dl_tensor = &dlm_tensor.dl_tensor
860 is_readonly = False
861 used_name = DLPACK_TENSOR_USED_NAME
862 else:
863 raise BufferError("Invalid DLPack capsule")
865 cpython.PyCapsule_SetName(capsule, used_name) 1d
867 cdef StridedMemoryView view = StridedMemoryView.__new__(StridedMemoryView) 1d
868 view.dl_tensor = dl_tensor 1d
869 view.metadata = capsule 1d
870 view.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset) 1d
871 view.readonly = is_readonly 1d
872 view.exporting_obj = exporting_obj 1d
873 if dl_tensor.device.device_type == _kDLCPU: 1d
874 view.device_id = -1 1d
875 view.is_device_accessible = False 1d
876 elif dl_tensor.device.device_type in (_kDLCUDA, _kDLCUDAHost, _kDLCUDAManaged):
877 view.device_id = dl_tensor.device.device_id
878 view.is_device_accessible = True
879 else:
880 raise BufferError("device not supported")
881 return view 1d
884cdef int _smv_managed_tensor_allocator(
885 DLTensor* prototype,
886 DLManagedTensorVersioned** out,
887 void* error_ctx,
888 void (*SetError)(void* error_ctx, const char* kind, const char* message) noexcept,
889) noexcept with gil:
890 if out != NULL: 2Zb
891 out[0] = NULL 2Zb
892 if SetError != NULL: 2Zb
893 SetError(error_ctx, b"NotImplementedError", b"managed_tensor_allocator is not supported by StridedMemoryView")
894 cpython.PyErr_SetString(NotImplementedError, b"managed_tensor_allocator is not supported by StridedMemoryView") 2Zb
895 return -1 2Zb
898cdef int _smv_managed_tensor_from_py_object_no_sync(
899 void* py_object,
900 DLManagedTensorVersioned** out,
901) noexcept with gil:
902 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1d
903 if out == NULL: 1d
904 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL")
905 return -1
906 out[0] = NULL 1d
907 cdef object obj = <object>py_object 1d
908 if not isinstance(obj, StridedMemoryView): 1d
909 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView")
910 return -1
911 try: 1d
912 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1d
913 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, <StridedMemoryView>obj) 1d
914 except Exception:
915 _smv_versioned_deleter(dlm_tensor_ver)
916 return -1
917 out[0] = dlm_tensor_ver 1d
918 return 0 1d
921cdef int _smv_managed_tensor_to_py_object_no_sync(
922 DLManagedTensorVersioned* tensor,
923 void** out_py_object,
924) noexcept with gil:
925 cdef object capsule
926 cdef object py_view
927 if out_py_object == NULL: 2d 0b
928 cpython.PyErr_SetString(RuntimeError, b"out_py_object cannot be NULL")
929 return -1
930 out_py_object[0] = NULL 2d 0b
931 if tensor == NULL: 2d 0b
932 cpython.PyErr_SetString(RuntimeError, b"tensor cannot be NULL") 20b
933 return -1 20b
934 try: 1d
935 capsule = cpython.PyCapsule_New( 1d
936 <void*>tensor,
937 DLPACK_VERSIONED_TENSOR_UNUSED_NAME,
938 _smv_pycapsule_deleter,
939 )
940 py_view = _smv_from_dlpack_capsule(capsule, capsule) 1d
941 cpython.Py_INCREF(py_view) 1d
942 out_py_object[0] = <void*>py_view 1d
943 except Exception:
944 return -1
945 return 0 1d
948cdef int _smv_dltensor_from_py_object_no_sync(
949 void* py_object,
950 DLTensor* out,
951) noexcept with gil:
952 if out == NULL: 21bE
953 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL")
954 return -1
955 cdef object obj = <object>py_object 21bE
956 if not isinstance(obj, StridedMemoryView): 21bE
957 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView") 21b
958 return -1 21b
959 try: 1E
960 _smv_setup_dltensor_borrowed(out, <StridedMemoryView>obj) 1E
961 except Exception:
962 return -1
963 return 0 1E
966cdef int _smv_current_work_stream(
967 _DLDeviceType device_type,
968 int32_t device_id,
969 void** out_current_stream,
970) noexcept with gil:
971 if out_current_stream == NULL: 26b
972 cpython.PyErr_SetString(RuntimeError, b"out_current_stream cannot be NULL")
973 return -1
974 # cuda.core has no global/current stream state today.
975 out_current_stream[0] = NULL 26b
976 return 0 26b
979cdef void _init_smv_dlpack_exchange_api():
980 global _SMV_DLPACK_EXCHANGE_API_INITED
981 if _SMV_DLPACK_EXCHANGE_API_INITED:
982 return
983 _SMV_DLPACK_EXCHANGE_API.header.version.major = DLPACK_MAJOR_VERSION
984 _SMV_DLPACK_EXCHANGE_API.header.version.minor = DLPACK_MINOR_VERSION
985 _SMV_DLPACK_EXCHANGE_API.header.prev_api = NULL
986 _SMV_DLPACK_EXCHANGE_API.managed_tensor_allocator = _smv_managed_tensor_allocator
987 _SMV_DLPACK_EXCHANGE_API.managed_tensor_from_py_object_no_sync = _smv_managed_tensor_from_py_object_no_sync
988 _SMV_DLPACK_EXCHANGE_API.managed_tensor_to_py_object_no_sync = _smv_managed_tensor_to_py_object_no_sync
989 _SMV_DLPACK_EXCHANGE_API.dltensor_from_py_object_no_sync = _smv_dltensor_from_py_object_no_sync
990 _SMV_DLPACK_EXCHANGE_API.current_work_stream = _smv_current_work_stream
991 _SMV_DLPACK_EXCHANGE_API_INITED = True
994_init_smv_dlpack_exchange_api()
995# cdef classes are immutable types in Cython 3, so inject these attributes
996# directly into the type dict.
997(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__dlpack_c_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE
998(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__c_dlpack_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE
999PyType_Modified(<PyTypeObject*>StridedMemoryView)
1002cdef str get_simple_repr(obj):
1003 # TODO: better handling in np.dtype objects
1004 cdef object obj_class
1005 cdef str obj_repr
1006 if isinstance(obj, type): 1#M
1007 obj_class = obj
1008 else:
1009 obj_class = obj.__class__ 1#M
1010 if obj_class.__module__ in (None, "builtins"): 1#M
1011 obj_repr = obj_class.__name__ 1#
1012 else:
1013 obj_repr = f"{obj_class.__module__}.{obj_class.__name__}" 1#M
1014 return obj_repr 1#M
1018cdef bint check_has_dlpack(obj) except*:
1019 cdef bint has_dlpack
1020 if hasattr(obj, "__dlpack__") and hasattr(obj, "__dlpack_device__"): 25 6 7 9 Z T N O P Q R U V W X Y 0 1 2 3 4 3bB C A $ % k cbXbM ! L E d w g h l m n s t u v o p q r i y x f b e c j a
1021 has_dlpack = True 15679ZTNOPQRUVWXY01234BCA$%kM!LEdwghlmnstuvopqriyxfbecja
1022 elif hasattr(obj, "__cuda_array_interface__"): 23bcbXb
1023 has_dlpack = False 2cbXb
1024 else:
1025 raise BufferError( 23b
1026 "the input object does not support any data exchange protocol")
1027 return has_dlpack 25 6 7 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k cbXbM ! L E d w g h l m n s t u v o p q r i y x f b e c j a
1030cdef class _StridedMemoryViewProxy:
1031 cdef readonly:
1032 object obj
1033 bint has_dlpack
1035 def __init__(self, obj: object) -> None:
1036 self.obj = obj 2N O P Q R Xb
1037 self.has_dlpack = check_has_dlpack(obj) 2N O P Q R Xb
1039 cpdef StridedMemoryView view(self, stream_ptr=None):
1040 if self.has_dlpack: 1NOPQR
1041 return StridedMemoryView.from_dlpack(self.obj, stream_ptr) 1NOPQR
1042 else:
1043 return StridedMemoryView.from_cuda_array_interface(self.obj, stream_ptr)
1046cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
1047 cdef int dldevice, device_id
1048 cdef bint is_device_accessible, is_readonly
1049 is_device_accessible = False 25 6 7 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
1050 dldevice, device_id = obj.__dlpack_device__() 25 6 7 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
1051 if dldevice == _kDLCPU: 25 6 7 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a Tb
1052 assert device_id == 0 27 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a
1053 device_id = -1 27 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a
1054 if stream_ptr is None: 27 9 Z T N O P Q R U V W X Y 0 1 2 3 4 B C A $ % k M ! L F G H I J K E d w g h l m n s t u v o p q r i y x f Sbb e c j a
1055 raise BufferError("stream=None is ambiguous with view()") 2Sb
1056 elif stream_ptr == -1: 179ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1057 stream_ptr = None 179ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1058 elif dldevice == _kDLCUDA:
1059 assert device_id >= 0
1060 is_device_accessible = True
1061 # no need to check other stream values, it's a pass-through
1062 if stream_ptr is None:
1063 raise BufferError("stream=None is ambiguous with view()")
1064 elif dldevice in (_kDLCUDAHost, _kDLCUDAManaged):
1065 is_device_accessible = True 156
1066 # just do a pass-through without any checks, as pinned/managed memory can be
1067 # accessed on both host and device
1068 else:
1069 raise BufferError("device not supported") 2Tb
1071 cdef object capsule
1072 try: 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1073 capsule = obj.__dlpack__( 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1074 stream=int(stream_ptr) if stream_ptr else None, 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1075 max_version=(DLPACK_MAJOR_VERSION, DLPACK_MINOR_VERSION)) 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1076 except TypeError: 1bca
1077 capsule = obj.__dlpack__( 1bca
1078 stream=int(stream_ptr) if stream_ptr else None) 1bca
1080 cdef void* data = NULL 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1081 cdef DLTensor* dl_tensor
1082 cdef DLManagedTensorVersioned* dlm_tensor_ver
1083 cdef DLManagedTensor* dlm_tensor
1084 cdef const char *used_name
1085 if cpython.PyCapsule_IsValid( 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1086 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME):
1087 data = cpython.PyCapsule_GetPointer( 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1088 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME)
1089 dlm_tensor_ver = <DLManagedTensorVersioned*>data 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1090 dl_tensor = &dlm_tensor_ver.dl_tensor 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1091 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1092 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1093 elif cpython.PyCapsule_IsValid( 1bca
1094 capsule, DLPACK_TENSOR_UNUSED_NAME):
1095 data = cpython.PyCapsule_GetPointer( 1bca
1096 capsule, DLPACK_TENSOR_UNUSED_NAME)
1097 dlm_tensor = <DLManagedTensor*>data 1bca
1098 dl_tensor = &dlm_tensor.dl_tensor 1bca
1099 is_readonly = False 1bca
1100 used_name = DLPACK_TENSOR_USED_NAME 1bca
1101 else:
1102 assert False
1104 cpython.PyCapsule_SetName(capsule, used_name) 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1106 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1107 buf.dl_tensor = dl_tensor 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1108 buf.metadata = capsule 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1109 buf.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset) 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1110 buf.device_id = device_id 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1111 buf.is_device_accessible = is_device_accessible 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1112 buf.readonly = is_readonly 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1113 buf.exporting_obj = obj 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1115 return buf 15679ZTNOPQRUVWXY01234BCA$%kM!LFGHIJKEdwghlmnstuvopqriyxfbecja
1118@functools.lru_cache
1119def _typestr2dtype(str typestr) -> numpy.dtype:
1120 return numpy.dtype(typestr) 2' ( ) * + , - . / : ; = ? cb
1123@functools.lru_cache
1124def _typestr2itemsize(str typestr) -> int:
1125 return _typestr2dtype(typestr).itemsize 2' ( ) * + , - . / : ; = ? cb
1128cdef object dtype_dlpack_to_numpy(DLDataType* dtype):
1129 cdef int bits = dtype.bits 1kMFGHIJKEdwghlmnstuvopqriyxfbecja
1130 if dtype.lanes != 1: 1kMFGHIJKEdwghlmnstuvopqriyxfbecja
1131 # TODO: return a NumPy structured dtype?
1132 raise NotImplementedError(
1133 f'vector dtypes (lanes={dtype.lanes}) is not supported')
1134 if dtype.code == kDLUInt: 1kMFGHIJKEdwghlmnstuvopqriyxfbecja
1135 if bits == 8: 1opqr
1136 np_dtype = numpy.uint8 1o
1137 elif bits == 16:
1138 np_dtype = numpy.uint16 1p
1139 elif bits == 32:
1140 np_dtype = numpy.uint32 1q
1141 elif bits == 64:
1142 np_dtype = numpy.uint64 1r
1143 else:
1144 raise TypeError('uint{} is not supported.'.format(bits))
1145 elif dtype.code == kDLInt:
1146 if bits == 8: 1kMFGHIJKEstuvxfbecja
1147 np_dtype = numpy.int8 1s
1148 elif bits == 16:
1149 np_dtype = numpy.int16 1t
1150 elif bits == 32:
1151 np_dtype = numpy.int32 1kMFGHIJKEuxfbecja
1152 elif bits == 64:
1153 np_dtype = numpy.int64 1v
1154 else:
1155 raise TypeError('int{} is not supported.'.format(bits))
1156 elif dtype.code == kDLFloat:
1157 if bits == 16: 1dlmn
1158 np_dtype = numpy.float16 1l
1159 elif bits == 32:
1160 np_dtype = numpy.float32 1m
1161 elif bits == 64:
1162 np_dtype = numpy.float64 1dn
1163 else:
1164 raise TypeError('float{} is not supported.'.format(bits))
1165 elif dtype.code == kDLComplex:
1166 # TODO(leofang): support complex32
1167 if bits == 64: 1ghiy
1168 np_dtype = numpy.complex64 1g
1169 elif bits == 128:
1170 np_dtype = numpy.complex128 1hiy
1171 else:
1172 raise TypeError('complex{} is not supported.'.format(bits))
1173 elif dtype.code == kDLBool:
1174 if bits == 8: 1w
1175 np_dtype = numpy.bool_ 1w
1176 else:
1177 raise TypeError(f'{bits}-bit bool is not supported')
1178 elif dtype.code == kDLBfloat:
1179 if bfloat16 is not None:
1180 np_dtype = numpy.dtype("bfloat16")
1181 else:
1182 raise NotImplementedError(
1183 'Support for bfloat16 within cuda-core requires `ml_dtypes`'
1184 'to be installed.'
1185 )
1186 else:
1187 raise TypeError('Unsupported dtype. dtype code: {}'.format(dtype.code))
1189 # We want the dtype object not just the type object
1190 return numpy.dtype(np_dtype) 1kMFGHIJKEdwghlmnstuvopqriyxfbecja
1193cpdef StridedMemoryView view_as_cai(obj, stream_ptr, view=None):
1194 cdef dict cai_data = obj.__cuda_array_interface__ 24b2b7b8bz bbMbdbcb8
1195 if cai_data.get("version", 0) < 3: 24b2b7b8bz bbMbdbcb8
1196 raise BufferError("only CUDA Array Interface v3 or above is supported") 27b8b
1197 if cai_data.get("mask") is not None: 24b2bz bbMbdbcb8
1198 raise BufferError("mask is not supported") 24b
1199 if stream_ptr is None: 22bz bbMbdbcb8
1200 raise BufferError("stream=None is ambiguous with view()") 22b
1202 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2z bbMbdbcb8
1203 buf.exporting_obj = obj 2z bbMbdbcb8
1204 buf.metadata = cai_data 2z bbMbdbcb8
1205 buf.dl_tensor = NULL 2z bbMbdbcb8
1206 # Validate shape/strides/typestr eagerly so constructor paths fail fast.
1207 buf.get_layout() 2z bbMbdbcb8
1208 buf.ptr, buf.readonly = cai_data["data"] 2z bbdbcb8
1209 buf.is_device_accessible = True 2z bbdbcb8
1210 if buf.ptr != 0: 2z bbdbcb8
1211 buf.device_id = handle_return( 1z8
1212 driver.cuPointerGetAttribute( 1z8
1213 driver.CUpointer_attribute.CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL, 1z8
1214 buf.ptr)) 1z8
1215 else:
1216 buf.device_id = handle_return(driver.cuCtxGetDevice()) 2bbdbcb
1218 cdef intptr_t producer_s, consumer_s
1219 cdef EventHandle h_event
1220 stream_ptr = int(stream_ptr) 2z bbdbcb8
1221 if stream_ptr != -1: 2z bbdbcb8
1222 stream = cai_data.get("stream") 18
1223 if stream is not None: 18
1224 producer_s = <intptr_t>(stream) 18
1225 consumer_s = <intptr_t>(stream_ptr) 18
1226 assert producer_s > 0 18
1227 # establish stream order
1228 if producer_s != consumer_s: 18
1229 with nogil: 18
1230 h_event = create_event_handle_noctx(cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING) 18
1231 HANDLE_RETURN(cydriver.cuEventRecord( 18
1232 as_cu(h_event), <cydriver.CUstream>producer_s))
1233 HANDLE_RETURN(cydriver.cuStreamWaitEvent( 18
1234 <cydriver.CUstream>consumer_s, as_cu(h_event), 0))
1235 elif _is_torch_tensor(obj):
1236 # PyTorch's __cuda_array_interface__ reports version 2 and
1237 # omits the "stream" field, so the standard CAI sync path
1238 # above is a no-op for torch tensors. This is unsafe: the
1239 # consumer has no guarantee that the producer's work is
1240 # visible. We fix this by querying PyTorch's current CUDA
1241 # stream via the AOTI stable C ABI and performing the same
1242 # event-based stream ordering.
1243 _get_tensor_bridge().sync_torch_stream(
1244 buf.device_id, <intptr_t>(stream_ptr))
1246 return buf 2z bbdbcb8
1249cpdef StridedMemoryView view_as_array_interface(obj, view=None):
1250 cdef dict data = obj.__array_interface__ 25b9b!b' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1251 if data.get("version", 0) < 3: 25b9b!b' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1252 raise BufferError("only NumPy Array Interface v3 or above is supported") 29b!b
1253 if data.get("mask") is not None: 25b' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1254 raise BufferError("mask is not supported") 25b
1256 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1257 buf.exporting_obj = obj 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1258 buf.metadata = data 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1259 buf.dl_tensor = NULL 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1260 # Validate shape/strides/typestr eagerly so constructor paths fail fast.
1261 buf.get_layout() 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? Qb
1262 buf.ptr, buf.readonly = data["data"] 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab?
1263 buf.is_device_accessible = False 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab?
1264 buf.device_id = handle_return(driver.cuCtxGetDevice()) 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab?
1265 return buf 2' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab?
1268def args_viewable_as_strided_memory(arg_indices: tuple[int, ...]) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
1269 """
1270 Decorator to create proxy objects to :obj:`StridedMemoryView` for the
1271 specified positional arguments.
1273 This allows array/tensor attributes to be accessed inside the function
1274 implementation, while keeping the function body array-library-agnostic (if
1275 desired).
1277 Inside the decorated function, the specified arguments become instances
1278 of an (undocumented) proxy type, regardless of its original source. A
1279 :obj:`StridedMemoryView` instance can be obtained by passing the (consumer)
1280 stream pointer (as a Python `int`) to the proxies's ``view()`` method. For
1281 example:
1283 .. code-block:: python
1285 @args_viewable_as_strided_memory((1,))
1286 def my_func(arg0, arg1, arg2, stream: Stream):
1287 # arg1 can be any object supporting DLPack or CUDA Array Interface
1288 view = arg1.view(stream.handle)
1289 assert isinstance(view, StridedMemoryView)
1290 ...
1292 Parameters
1293 ----------
1294 arg_indices : tuple
1295 The indices of the target positional arguments.
1296 """
1297 def wrapped_func_with_indices(func: "Callable") -> "Callable": 1NOPQR
1298 @functools.wraps(func) 1NOPQR
1299 def wrapped_func(*args, **kwargs) -> object:
1300 args = list(args) 1NOPQR
1301 cdef int idx
1302 for idx in arg_indices: 1NOPQR
1303 args[idx] = _StridedMemoryViewProxy(args[idx]) 1NOPQR
1304 return func(*args, **kwargs) 1NOPQR
1305 return wrapped_func 1NOPQR
1306 return wrapped_func_with_indices 1NOPQR
1309cdef inline _StridedLayout layout_from_dlpack(DLTensor* dl_tensor):
1310 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
1311 cdef int nbits = dl_tensor.dtype.bits * dl_tensor.dtype.lanes 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
1312 cdef int itemsize = nbits >> 3 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
1313 if (itemsize << 3) != nbits: 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
1314 raise ValueError("dl_tensor.dtype.bits must be a multiple of 8")
1315 layout.init_from_ptr(dl_tensor.ndim, dl_tensor.shape, dl_tensor.strides, itemsize) 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
1316 return layout 1ZNOPQRUVWXY01234BCAkMLFGHIJKEdwghlmnstuvopqriyxfbecja
1319cdef _StridedLayout layout_from_cai(object metadata):
1320 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbcb8
1321 cdef object shape = metadata["shape"] 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbcb8
1322 cdef object strides = metadata.get("strides") 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbcb8
1323 cdef int itemsize = _typestr2itemsize(metadata["typestr"]) 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbcb8
1324 layout.init_from_tuple(shape, strides, itemsize, True) 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? QbbbMbdbcb8
1325 return layout 2z ' ( ) * @ + [ , - . / ] ^ _ : ; ` = { | } ~ ab? bbdbcb8
1328cdef inline intptr_t get_data_ptr(object buffer, _StridedLayout layout) except? 0:
1329 return <intptr_t>(int(buffer.handle)) + layout.get_slice_offset_in_bytes() 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1332cdef inline int view_buffer_strided(
1333 StridedMemoryView view,
1334 object buffer,
1335 _StridedLayout layout,
1336 object dtype,
1337 bint is_readonly,
1338) except -1:
1339 if isinstance(buffer, Buffer): 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS # L F G H I J K
1340 Buffer_check_open(<cyBuffer>buffer) 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS # L F G H I J K
1341 if dtype is not None: 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS # L F G H I J K
1342 dtype = numpy.dtype(dtype) 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbL F G H I J K
1343 if dtype.itemsize != layout.itemsize: 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbL F G H I J K
1344 raise ValueError(
1345 f"The dtype's itemsize ({dtype.itemsize}) does not match the layout's "
1346 f"itemsize ({layout.itemsize})."
1347 )
1348 # Check the layout's offset range [min_offset, max_offset] fits
1349 # within the [0, buffer.size - 1] range.
1350 # The required_size_in_bytes fails if min_offset < 0.
1351 # NB. For external memory, both positive and negative offsets can be valid,
1352 # but for a proper check we'd need to know both size and data offset,
1353 # while neither is reported by the packages.
1354 cdef bint is_allocated = buffer.memory_resource is not None 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS # L F G H I J K
1355 if is_allocated and buffer.size < layout.get_required_size_in_bytes(): 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbUbebfbRbPbS # L F G H I J K
1356 raise ValueError( 2Rb
1357 f"Buffer size is too small for the layout. " 2Rb
1358 f"Expected at least {layout.get_required_size_in_bytes()} bytes, " 2Rb
1359 f"got {buffer.size} bytes." 2Rb
1360 )
1361 # set the public attributes
1362 view.ptr = get_data_ptr(buffer, layout) 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1363 view.device_id = buffer.device_id 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1364 view.is_device_accessible = buffer.is_device_accessible 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1365 view.readonly = is_readonly 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1366 view.exporting_obj = view._buffer = buffer 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1367 # no dlpack/cai metadata
1368 view.dl_tensor = NULL 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1369 view.metadata = None 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1370 # we get the layout from the caller
1371 view._layout = layout 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1372 view._dtype = dtype 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K
1373 return 0 2NbObB C A gbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbebfbPbS # L F G H I J K