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