npu_data.py Source File

npu_data.py Source File#

SDK qb Runtime Library: npu_data.py Source File
SDK qb Runtime Library v1.4
MCS001-EN
npu_data.py
Go to the documentation of this file.
1
4
5from typing import Tuple
6
7import numpy as np
8
9import qbruntime.qbruntime as _cQbRuntime
10from .accelerator import Accelerator
11
12_Shape = Tuple[int, ...]
13
14__all__ = ["NPUData"]
15
16# Maps the C++ DataType enum to the corresponding numpy dtype.
17_DTYPE_MAP = {
18 _cQbRuntime.DataType.Float32: np.float32,
19 _cQbRuntime.DataType.Float16: np.float16,
20 _cQbRuntime.DataType.Int8: np.int8,
21 _cQbRuntime.DataType.Uint8: np.uint8,
22}
23
24
27
28
29class NPUData:
30 """@brief A model input or output tensor that can reside on the host (CPU) or NPU.
31
32 An NPUData owns the storage backing a single model input/output tensor on the
33 host and/or the NPU, and lets the data be moved between CPU and NPU. Instances
34 are created through `Model.acquire_input_npu_data` /
35 `Model.acquire_output_npu_data`, not constructed directly.
36
37 The data is accessible as a numpy array (via indexing, e.g. `nd[...]`) only while
38 it resides on the CPU. Bring it back with `cpu` before reading results that were
39 produced on the NPU.
40
41 An NPU-resident NPUData may be passed to any model that expects a tensor of the
42 same shape and dtype on the same accelerator, so several models can share one
43 NPU-resident tensor without copying it back to the host in between.
44
45 @note This is an advanced API rather than a typical usage.
46 @warning This API is in beta: it may still contain bugs, and its behavior may
47 change in a future release.
48 """
49
50 def __init__(self, _npu_data: _cQbRuntime.NPUData):
51 """@brief Internal constructor.
52
53 @param _npu_data The underlying C++ NPUData object. Use
54 `Model.acquire_input_npu_data` / `Model.acquire_output_npu_data`
55 to create instances.
56 """
57 self._npu_data = _npu_data
58 # Zero-copy numpy view over the host buffer, built once here (like PinnedMemory)
59 # so indexing does not rebuild it. Unlike PinnedMemory, an NPUData is only
60 # host-accessible while CPU-resident, so this is None while the data lives on
61 # the NPU (acquired with upload=True or after launch()) and is (re)built when
62 # the data is brought back via cpu(). Shares memory with the host buffer and is
63 # only valid while this NPUData is alive and CPU-resident.
64 self._array = self._make_array()
65
66 def _make_array(self):
67 """@brief Builds the numpy view, or returns None if not CPU-resident.
68
69 The residency is checked first (dev_no() == -1 means the data is on the host):
70 requesting the buffer protocol while the data is on the NPU would propagate a
71 C++ exception out of pybind's `extern "C"` buffer slot and call std::terminate,
72 so it must not be attempted.
73 """
74 if self._npu_data.dev_no() != -1:
75 return None
76 return np.asarray(self._npu_data)
77
78 def launch(self, acc: Accelerator) -> None:
79 """@brief Uploads the data to the NPU memory of the given accelerator.
80
81 @param acc The accelerator on which to place the data.
82 """
83 self._npu_data.launch(acc._accelerator)
84 # No longer host-accessible.
85 self._array = None
86
87 def cpu(self) -> None:
88 """@brief Brings the data back to host (CPU) memory."""
89 self._npu_data.cpu()
90 # Host buffer is available again; rebuild the view.
91 self._array = self._make_array()
92
93 def copy_from(self, src: "NPUData") -> None:
94 """@brief Copies the data from another NPUData.
95
96 Both tensors must have the same shape and dtype; the source may reside on
97 either side (a CPU-to-NPU or NPU-to-CPU copy is staged through the host).
98
99 @param src The source NPUData to copy from.
100 """
101 self._npu_data.copy_from(src._npu_data)
102
103 def __getitem__(self, key):
104 """@brief Reads element(s) from the host buffer via numpy indexing."""
105 if self._array is None:
106 raise ValueError("NPUData is not on the CPU; call cpu() first.")
107 return self._array[key]
108
109 def __setitem__(self, key, value):
110 """@brief Writes value(s) into the host buffer via numpy indexing."""
111 if self._array is None:
112 raise ValueError("NPUData is not on the CPU; call cpu() first.")
113 self._array[key] = value
114
115 def __len__(self) -> int:
116 """@brief Returns the length of the buffer's first dimension."""
117 return self.shape[0]
118
119 @property
120 def shape(self) -> _Shape:
121 """@brief Returns the shape of the data."""
122 return tuple(self._npu_data.shape())
123
124 @property
125 def dtype(self) -> np.dtype:
126 """@brief Returns the numpy dtype of the data's elements."""
127 data_type = self._npu_data.data_type()
128 if data_type not in _DTYPE_MAP:
129 # NPUData rejects 16-bit float types at acquisition, so this only happens
130 # for a corrupted object.
131 raise ValueError(f"NPUData has no numpy equivalent for {data_type}.")
132 return np.dtype(_DTYPE_MAP[data_type])
133
134 @property
135 def dev_no(self) -> int:
136 """@brief Device number of the accelerator the data resides on.
137
138 @return The device number when the data is on the NPU, or -1 when it is on the
139 host (CPU).
140 """
141 return self._npu_data.dev_no()
142
143 @property
144 def hardware_name(self) -> str:
145 """@brief Hardware name (device type) of the accelerator the data resides on.
146
147 Device numbers are assigned per hardware type (e.g. an "aries-rb" and a
148 "regulus-ra" can both be device #0), so ``dev_no`` alone does not identify a
149 physical device; pair it with this name to distinguish devices.
150
151 @return The hardware name (e.g. "aries-rb") when the data is on the NPU, or an
152 empty string when it is on the host (CPU).
153 """
154 return self._npu_data.hardware_name()
155
156
157
A model input or output tensor that can reside on the host (CPU) or NPU.
Definition npu_data.py:29
np.dtype dtype(self)
Returns the numpy dtype of the data's elements.
Definition npu_data.py:125
_make_array(self)
Builds the numpy view, or returns None if not CPU-resident.
Definition npu_data.py:66
None launch(self, Accelerator acc)
Uploads the data to the NPU memory of the given accelerator.
Definition npu_data.py:78
__getitem__(self, key)
Reads element(s) from the host buffer via numpy indexing.
Definition npu_data.py:103
None copy_from(self, "NPUData" src)
Copies the data from another NPUData.
Definition npu_data.py:93
str hardware_name(self)
Hardware name (device type) of the accelerator the data resides on.
Definition npu_data.py:144
__init__(self, _cQbRuntime.NPUData _npu_data)
Internal constructor.
Definition npu_data.py:50
_Shape shape(self)
Returns the shape of the data.
Definition npu_data.py:120
__setitem__(self, key, value)
Writes value(s) into the host buffer via numpy indexing.
Definition npu_data.py:109
int dev_no(self)
Device number of the accelerator the data resides on.
Definition npu_data.py:135
int __len__(self)
Returns the length of the buffer's first dimension.
Definition npu_data.py:115
None cpu(self)
Brings the data back to host (CPU) memory.
Definition npu_data.py:87