Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

222 lines
7.5 KiB
Python

"""Torch-free smoke tests for rocshmem4py.
These tests exercise the native host API through ``ctypes`` + HIP, so they
run in any CI environment even when ``torch`` is not installed.
Scope:
* ``test_create_buffer`` -- validates the framework-agnostic collective
allocation API (:func:`rocshmem4py.rocshmem_create_buffer`).
* ``test_symmetric_buffer_lifecycle`` -- SymmetricBuffer RAII lifecycle;
torch integration path is covered by ``test_basic.py::test_symmetric_buffer``.
* ``test_host_device_roundtrip`` -- validates the symmetric heap is
accessible as ordinary HIP device memory via ctypes H2D/D2H transfers.
* ``test_get_peer_buffer`` -- validates :func:`rocshmem4py.rocshmem_get_peer_buffer`
(torch-free equivalent of ``symm_rocshmem_tensor``).
* Multi-PE stream-based transfers using ``rocshmem_create_buffer`` for
allocation (``*_on_stream`` portable APIs only -- see test_collective.py
docstring for rationale).
"""
import ctypes
import pytest
from conftest import requires_multi_pe # noqa: E402
from hip_test_utils import HipStream, d2h, h2d, hip_sync # noqa: E402
import rocshmem4py # noqa: E402
# ---------------------------------------------------------------------------
# Single-PE tests unique to this file (torch-free but HIP-specific)
# ---------------------------------------------------------------------------
def test_create_buffer():
"""rocshmem_create_buffer is the framework-agnostic collective allocator.
Validates that the returned SymmetricBuffer has the correct shape, pointer,
__cuda_array_interface__ fields, and a valid symmetric address for self.
"""
nbytes = 4096
buf = rocshmem4py.rocshmem_create_buffer(nbytes)
try:
assert isinstance(buf, rocshmem4py.SymmetricBuffer)
assert buf.ptr > 0
assert buf.nbytes == nbytes
assert buf.size == nbytes
assert buf.own_data is True
assert int(buf) == buf.ptr
# __cuda_array_interface__ must be present for framework interop (e.g. torch)
cai = buf.__cuda_array_interface__
assert cai["data"] == (buf.ptr, False)
assert cai["shape"] == (nbytes,)
assert cai["version"] == 3
# Symmetric pointer to self must be valid
my_pe = rocshmem4py.rocshmem_my_pe()
assert buf.get_remote_ptr(my_pe) >= 0
finally:
buf.free()
assert buf._freed is True
def test_symmetric_buffer_lifecycle():
"""SymmetricBuffer RAII lifecycle: allocation, pointer validity, remote ptr, and free.
Covers the torch-free API surface only. The torch integration path
(``__cuda_array_interface__``, ``torch.as_tensor``, non-owning view,
``_device``) is covered by ``test_basic.py::test_symmetric_buffer``.
"""
size = 2048
buf = rocshmem4py.SymmetricBuffer(size)
try:
assert buf.size == size and buf.nbytes == size and buf.ptr > 0
assert int(buf) == buf.ptr
my_pe = rocshmem4py.rocshmem_my_pe()
assert buf.get_remote_ptr(my_pe) >= 0
finally:
buf.free()
assert buf._freed is True
def test_host_device_roundtrip():
"""Symmetric heap memory is accessible as ordinary HIP device memory."""
nelems = 16
nbytes = nelems * ctypes.sizeof(ctypes.c_int32)
buf = rocshmem4py.rocshmem_create_buffer(nbytes)
try:
tx = (ctypes.c_int32 * nelems)(*range(nelems))
h2d(buf.ptr, tx)
hip_sync()
rx = (ctypes.c_int32 * nelems)()
d2h(rx, buf.ptr)
hip_sync()
assert list(rx) == list(range(nelems))
finally:
buf.free()
# ---------------------------------------------------------------------------
# Multi-PE tests — stream-based portable APIs only.
# Host-side blocking APIs are not exercised here; see test_collective.py
# module docstring for the rationale.
# ---------------------------------------------------------------------------
@requires_multi_pe
def test_putmem_on_stream_peer():
"""Torch-free: push local buffer to a peer via rocshmem_putmem_on_stream."""
nelems = 32
nbytes = nelems * ctypes.sizeof(ctypes.c_int32)
my_pe = rocshmem4py.rocshmem_my_pe()
n_pes = rocshmem4py.rocshmem_n_pes()
peer = (my_pe + 1) % n_pes
src = rocshmem4py.rocshmem_create_buffer(nbytes)
dst = rocshmem4py.rocshmem_create_buffer(nbytes)
try:
src_host = (ctypes.c_int32 * nelems)(*([my_pe] * nelems))
dst_host = (ctypes.c_int32 * nelems)(*([-1] * nelems))
h2d(src.ptr, src_host)
h2d(dst.ptr, dst_host)
hip_sync()
rocshmem4py.rocshmem_barrier_all()
with HipStream() as s:
rocshmem4py.rocshmem_putmem_on_stream(
dst.ptr, src.ptr, nbytes, peer, s.handle
)
rocshmem4py.rocshmem_barrier_all_on_stream(s.handle)
s.synchronize()
rx = (ctypes.c_int32 * nelems)()
d2h(rx, dst.ptr)
hip_sync()
sender = (my_pe - 1 + n_pes) % n_pes
assert all(v == sender for v in rx), (
f"PE {my_pe}: expected all {sender}, got {list(rx)[:8]}..."
)
finally:
src.free()
dst.free()
@requires_multi_pe
def test_getmem_on_stream_peer():
"""Torch-free: pull peer buffer into local buffer via rocshmem_getmem_on_stream."""
nelems = 32
nbytes = nelems * ctypes.sizeof(ctypes.c_int32)
my_pe = rocshmem4py.rocshmem_my_pe()
n_pes = rocshmem4py.rocshmem_n_pes()
peer = (my_pe + 1) % n_pes
src = rocshmem4py.rocshmem_create_buffer(nbytes)
dst = rocshmem4py.rocshmem_create_buffer(nbytes)
try:
src_host = (ctypes.c_int32 * nelems)(*([my_pe] * nelems))
dst_host = (ctypes.c_int32 * nelems)(*([-1] * nelems))
h2d(src.ptr, src_host)
h2d(dst.ptr, dst_host)
hip_sync()
with HipStream() as s:
rocshmem4py.rocshmem_barrier_all_on_stream(s.handle)
rocshmem4py.rocshmem_getmem_on_stream(
dst.ptr, src.ptr, nbytes, peer, s.handle
)
rocshmem4py.rocshmem_barrier_all_on_stream(s.handle)
s.synchronize()
rx = (ctypes.c_int32 * nelems)()
d2h(rx, dst.ptr)
hip_sync()
assert all(v == peer for v in rx), (
f"PE {my_pe}: expected all {peer}, got {list(rx)[:8]}..."
)
finally:
src.free()
dst.free()
@requires_multi_pe
def test_get_peer_buffer():
"""``rocshmem_get_peer_buffer`` returns a zero-copy view on IPC backends.
On backends without direct remote memory access ``rocshmem_ptr`` returns
NULL and the function raises ``RuntimeError`` (skipped here). Use
``rocshmem_getmem_on_stream`` to copy peer data on those backends —
that path is exercised by ``test_getmem_on_stream_peer`` above.
"""
my_pe = rocshmem4py.rocshmem_my_pe()
n_pes = rocshmem4py.rocshmem_n_pes()
peer = (my_pe + 1) % n_pes
nbytes = 256
buf = rocshmem4py.rocshmem_create_buffer(nbytes)
try:
rocshmem4py.rocshmem_barrier_all()
try:
peer_buf = rocshmem4py.rocshmem_get_peer_buffer(buf, peer)
except RuntimeError:
pytest.skip(
"rocshmem_ptr returned NULL -- backend lacks direct remote "
"memory access; copy path covered by test_getmem_on_stream_peer"
)
assert isinstance(peer_buf, rocshmem4py.SymmetricBuffer)
assert peer_buf.ptr > 0
assert peer_buf.own_data is False
assert peer_buf.nbytes == nbytes
finally:
buf.free()
if __name__ == "__main__":
pytest.main([__file__, "-v"])