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
194 lines
6.5 KiB
Python
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"])
|