Skip to content

Commit 598a2f1

Browse files
committed
Add SMV.from_buffer/view tests
Signed-off-by: Kamil Tokarski <ktokarski@nvidia.com>
1 parent 2c0343f commit 598a2f1

File tree

1 file changed

+113
-1
lines changed

1 file changed

+113
-1
lines changed

cuda_core/tests/test_utils.py

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#
33
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
44

5+
import math
6+
57
try:
68
import cupy as cp
79
except ImportError:
@@ -15,7 +17,7 @@
1517
import pytest
1618
from cuda.core.experimental import Device
1719
from cuda.core.experimental._memoryview import view_as_cai
18-
from cuda.core.experimental.utils import StridedMemoryView, args_viewable_as_strided_memory
20+
from cuda.core.experimental.utils import StridedLayout, StridedMemoryView, args_viewable_as_strided_memory
1921

2022

2123
def test_cast_to_3_tuple_success():
@@ -195,3 +197,113 @@ def _check_view(self, view, in_arr, dev):
195197
assert view.device_id == dev.device_id
196198
assert view.is_device_accessible is True
197199
assert view.exporting_obj is in_arr
200+
201+
202+
def _dense_strides(shape, stride_order):
203+
ndim = len(shape)
204+
strides = [None] * ndim
205+
if ndim > 0:
206+
if stride_order == "C":
207+
strides[-1] = 1
208+
for i in range(ndim - 2, -1, -1):
209+
strides[i] = strides[i + 1] * shape[i + 1]
210+
else:
211+
assert stride_order == "F"
212+
strides[0] = 1
213+
for i in range(1, ndim):
214+
strides[i] = strides[i - 1] * shape[i - 1]
215+
return tuple(strides)
216+
217+
218+
@pytest.mark.parametrize("shape", [tuple(), (2, 3), (10, 10), (10, 13, 11)])
219+
@pytest.mark.parametrize("itemsize", [1, 4])
220+
@pytest.mark.parametrize("stride_order", ["C", "F"])
221+
@pytest.mark.parametrize("readonly", [True, False])
222+
def test_from_buffer(shape, itemsize, stride_order, readonly):
223+
dev = Device()
224+
dev.set_current()
225+
layout = StridedLayout.dense(shape=shape, itemsize=itemsize, stride_order=stride_order)
226+
required_size = layout.required_size_in_bytes()
227+
assert required_size == math.prod(shape) * itemsize
228+
buffer = dev.memory_resource.allocate(required_size)
229+
view = StridedMemoryView.from_buffer(buffer, layout, is_readonly=readonly)
230+
assert view.exporting_obj is buffer
231+
assert view.layout is layout
232+
assert view.ptr == int(buffer.handle)
233+
assert view.shape == shape
234+
assert view.strides == _dense_strides(shape, stride_order)
235+
assert view.dtype is None
236+
assert view.device_id == dev.device_id
237+
assert view.is_device_accessible
238+
assert view.readonly == readonly
239+
240+
241+
@pytest.mark.parametrize("stride_order", ["C", "F"])
242+
def test_from_buffer_sliced(stride_order):
243+
layout = StridedLayout.dense((5, 7), 2, stride_order=stride_order)
244+
device = Device()
245+
device.set_current()
246+
buffer = device.memory_resource.allocate(layout.required_size_in_bytes())
247+
view = StridedMemoryView.from_buffer(buffer, layout)
248+
assert view.shape == (5, 7)
249+
250+
sliced_view = view.view(layout[:-2, 3:])
251+
assert sliced_view.shape == (3, 4)
252+
expected_offset = 3 if stride_order == "C" else 3 * 5
253+
assert sliced_view.layout.slice_offset == expected_offset
254+
assert sliced_view.layout.slice_offset_in_bytes == expected_offset * 2
255+
assert sliced_view.ptr == view.ptr + expected_offset * 2
256+
257+
258+
def test_from_buffer_too_small():
259+
layout = StridedLayout.dense((5, 4), 2)
260+
d = Device()
261+
d.set_current()
262+
buffer = d.memory_resource.allocate(20)
263+
with pytest.raises(ValueError, match="Expected at least 40 bytes, got 20 bytes."):
264+
StridedMemoryView.from_buffer(buffer, layout)
265+
266+
267+
def test_from_buffer_disallowed_negative_offset():
268+
layout = StridedLayout((5, 4), (-4, 1), 1)
269+
d = Device()
270+
d.set_current()
271+
buffer = d.memory_resource.allocate(20)
272+
with pytest.raises(ValueError, match="please use StridedLayout.to_dense()."):
273+
StridedMemoryView.from_buffer(buffer, layout)
274+
275+
276+
@pytest.mark.parametrize(
277+
("shape", "slices", "stride_order"),
278+
[
279+
(shape, slices, stride_order)
280+
for shape, slices in [
281+
((5, 6), (2, slice(1, -1))),
282+
((10, 13, 11), (slice(None, None, 2), slice(None, None, -1), slice(2, -3))),
283+
]
284+
for stride_order in ["C", "F"]
285+
],
286+
)
287+
def test_from_buffer_sliced_external(shape, slices, stride_order):
288+
if np is None:
289+
pytest.skip("NumPy is not installed")
290+
a = np.arange(math.prod(shape), dtype=np.int32).reshape(shape, order=stride_order)
291+
view = StridedMemoryView(a, -1)
292+
layout = view.layout
293+
assert layout.is_dense
294+
assert layout.required_size_in_bytes() == a.nbytes
295+
assert view.ptr == a.ctypes.data
296+
297+
sliced_layout = layout[slices]
298+
sliced_view = view.view(sliced_layout)
299+
a_sliced = a[slices]
300+
assert sliced_view.ptr == a_sliced.ctypes.data
301+
assert sliced_view.ptr != view.ptr
302+
303+
assert 0 <= sliced_layout.required_size_in_bytes() <= a.nbytes
304+
assert not sliced_layout.is_dense
305+
assert sliced_view.layout is sliced_layout
306+
assert view.dtype == sliced_view.dtype
307+
assert sliced_view.layout.itemsize == a_sliced.itemsize == layout.itemsize
308+
assert sliced_view.shape == a_sliced.shape
309+
assert sliced_view.layout.strides_in_bytes == a_sliced.strides

0 commit comments

Comments
 (0)