Files
TheRock/rocm-systems/projects/rocshmem/python/tests/test_collective.py
T
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

194 lines
6.5 KiB
Python

"""Multi-PE tests for rocshmem4py (requires >= 2 PEs).
These tests exercise the Python binding layer using the portable rocSHMEM
surface -- ``rocshmem_barrier_all``, ``*_on_stream`` data movement, and
``rocshmem_ptr`` -- so a single test pass is valid across every rocSHMEM
backend (RO / IPC / GDA).
Host-side blocking APIs (``rocshmem_fence`` / ``rocshmem_quiet`` /
``rocshmem_putmem`` / ``rocshmem_getmem`` / ``rocshmem_*_atomic_*``) are
not exercised here. Their coverage depends on the backend rocSHMEM was
built with and belongs in rocSHMEM's own integration tests, not in the
Python-binding test suite.
"""
import pytest
from conftest import requires_multi_pe, requires_torch # noqa: E402
try:
import torch
except ImportError:
torch = None # type: ignore[assignment]
import rocshmem4py # noqa: E402
from rocshmem4py.interop import torch as rshmem_torch # noqa: E402
pytestmark = [requires_torch, requires_multi_pe]
def _pe_info():
my_pe = rocshmem4py.rocshmem_my_pe()
n_pes = rocshmem4py.rocshmem_n_pes()
peer = (my_pe + 1) % n_pes
return my_pe, n_pes, peer
def test_getmem_on_stream():
my_pe, n_pes, peer = _pe_info()
nelems = 64
src = rshmem_torch.create_tensor((nelems,), torch.int32)
dst = rshmem_torch.create_tensor((nelems,), torch.int32)
src.fill_(my_pe)
dst.fill_(-1)
torch.cuda.synchronize()
stream = torch.cuda.current_stream()
rocshmem4py.rocshmem_barrier_all_on_stream(stream.cuda_stream)
rocshmem4py.rocshmem_getmem_on_stream(
dst.data_ptr(), src.data_ptr(), nelems * src.element_size(),
peer, stream.cuda_stream)
rocshmem4py.rocshmem_barrier_all_on_stream(stream.cuda_stream)
torch.cuda.synchronize()
torch.testing.assert_close(
dst, torch.full((nelems,), peer, dtype=torch.int32, device="cuda"))
def test_putmem_on_stream():
my_pe, n_pes, peer = _pe_info()
nelems = 128
src = rshmem_torch.create_tensor((nelems,), torch.float32)
dst = rshmem_torch.create_tensor((nelems,), torch.float32)
src.fill_(float(my_pe))
dst.fill_(-1.0)
torch.cuda.synchronize()
rocshmem4py.rocshmem_barrier_all()
stream = torch.cuda.current_stream()
rocshmem4py.rocshmem_putmem_on_stream(
dst.data_ptr(), src.data_ptr(), nelems * src.element_size(),
peer, stream.cuda_stream)
rocshmem4py.rocshmem_barrier_all_on_stream(stream.cuda_stream)
torch.cuda.synchronize()
sender = (my_pe - 1 + n_pes) % n_pes
torch.testing.assert_close(
dst, torch.full((nelems,), float(sender), dtype=torch.float32,
device="cuda"))
def test_tensor_list_intra_node():
my_pe, n_pes, peer = _pe_info()
nelems_per_rank = 32
buf = rshmem_torch.create_tensor((n_pes * nelems_per_rank,), torch.int32)
buf.fill_(0)
torch.cuda.synchronize()
ref = torch.arange(n_pes * nelems_per_rank, dtype=torch.int32, device="cuda")
start = nelems_per_rank * my_pe
buf[start:start + nelems_per_rank].copy_(ref[start:start + nelems_per_rank])
torch.cuda.synchronize()
stream = torch.cuda.current_stream()
rocshmem4py.rocshmem_barrier_all_on_stream(stream.cuda_stream)
# Use the stream-based transfer to propagate my slice to the peer;
# portable across all rocSHMEM backends.
nbytes = nelems_per_rank * buf.element_size()
offset = buf.data_ptr() + start * buf.element_size()
rocshmem4py.rocshmem_putmem_on_stream(
offset, offset, nbytes, peer, stream.cuda_stream)
rocshmem4py.rocshmem_barrier_all_on_stream(stream.cuda_stream)
torch.cuda.synchronize()
sender = (my_pe - 1 + n_pes) % n_pes
s = nelems_per_rank * sender
torch.testing.assert_close(buf[s:s + nelems_per_rank], ref[s:s + nelems_per_rank])
def test_symm_rocshmem_tensor_peer_view():
"""``get_peer_tensor`` returns a zero-copy view of peer's symmetric tensor
on IPC backends. On backends without direct remote memory access,
``rocshmem_ptr`` returns NULL and the function raises -- skipped here;
the explicit copy path is covered by ``test_getmem_on_stream`` above.
"""
my_pe, n_pes, peer = _pe_info()
t = rshmem_torch.create_tensor((16,), torch.float32)
t.fill_(float(my_pe))
torch.cuda.synchronize()
rocshmem4py.rocshmem_barrier_all()
try:
peer_view = rshmem_torch.get_peer_tensor(t, peer)
except RuntimeError:
pytest.skip("rocshmem_ptr returned NULL -- backend lacks direct remote access")
assert peer_view.is_cuda
assert peer_view.shape == t.shape
assert peer_view.dtype == t.dtype
torch.cuda.synchronize()
expected = torch.full((16,), float(peer), dtype=torch.float32, device="cuda")
torch.testing.assert_close(peer_view, expected)
def test_interop_torch_put_get():
"""Tensor-aware ``interop.torch.put`` / ``get`` / ``barrier_all`` wrappers.
These wrappers accept torch tensors directly and default the stream to
``torch.cuda.current_stream()``. Portable across every rocSHMEM backend
since they delegate to the ``*_on_stream`` primitives.
"""
my_pe, n_pes, peer = _pe_info()
nelems = 64
src = rshmem_torch.create_tensor((nelems,), torch.int32)
dst = rshmem_torch.create_tensor((nelems,), torch.int32)
src.fill_(my_pe)
dst.fill_(-1)
torch.cuda.synchronize()
rshmem_torch.barrier_all() # default stream
# get: pull peer's src into local dst
rshmem_torch.get(dst, src, peer) # default stream
rshmem_torch.barrier_all()
torch.cuda.synchronize()
torch.testing.assert_close(
dst, torch.full((nelems,), peer, dtype=torch.int32, device="cuda")
)
# put: push local src (still filled with my_pe) to peer's dst
dst.fill_(-1)
torch.cuda.synchronize()
rshmem_torch.barrier_all()
stream = torch.cuda.current_stream()
rshmem_torch.put(dst, src, peer, stream=stream)
rshmem_torch.barrier_all(stream=stream)
torch.cuda.synchronize()
sender = (my_pe - 1 + n_pes) % n_pes
torch.testing.assert_close(
dst, torch.full((nelems,), sender, dtype=torch.int32, device="cuda")
)
def test_interop_torch_put_get_nbytes_mismatch():
"""``put`` / ``get`` wrappers must reject nbytes mismatches up-front."""
my_pe, n_pes, peer = _pe_info()
a = rshmem_torch.create_tensor((8,), torch.int32)
b = rshmem_torch.create_tensor((16,), torch.int32) # different nbytes
with pytest.raises(ValueError, match="nbytes"):
rshmem_torch.put(a, b, peer)
with pytest.raises(ValueError, match="nbytes"):
rshmem_torch.get(a, b, peer)
if __name__ == "__main__":
pytest.main([__file__, "-v"])