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
173 lines
5.3 KiB
Python
173 lines
5.3 KiB
Python
"""Full host AMO matrix tests.
|
|
|
|
Binding coverage: 69 host AMO symbols are generated in ``rocshmem4py.cc`` and
|
|
re-exported via ``_import_amo_symbols()``. ``test_amo_matrix_completeness``
|
|
checks the same 69 (type, op) pairs — there is no drift vs the C++ macro block.
|
|
|
|
rocSHMEM also declares ``longlong`` fetch/cas/swap/etc. in the header, but these
|
|
bindings intentionally expose only ``longlong`` set/add/inc (see comment in
|
|
``rocshmem4py.cc``).
|
|
|
|
Runtime limitation: these are host AMO bindings. IPC no-MPI runtime support
|
|
for host AMOs is not implemented yet, so behavioral AMO tests are opt-in via
|
|
``ROCSHMEM4PY_RUN_HOST_AMO_IPC=1`` until the runtime grows that support.
|
|
"""
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
import rocshmem4py
|
|
from rocshmem4py import (
|
|
rocshmem_my_pe,
|
|
rocshmem_n_pes,
|
|
rocshmem_barrier_all,
|
|
)
|
|
from conftest import requires_torch, requires_multi_pe
|
|
|
|
try:
|
|
import torch
|
|
except ImportError:
|
|
torch = None # type: ignore[assignment]
|
|
|
|
|
|
requires_host_amo_runtime = pytest.mark.skipif(
|
|
os.environ.get("ROCSHMEM4PY_RUN_HOST_AMO_IPC") != "1",
|
|
reason=(
|
|
"IPC no-MPI host AMOs are not supported in rocSHMEM runtime yet; "
|
|
"set ROCSHMEM4PY_RUN_HOST_AMO_IPC=1 only when validating a runtime fix"
|
|
),
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Coverage matrix — every (type, op) we expect the binding layer to expose.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
INTEGER_TYPES = ["int", "long", "uint32", "uint64", "size", "ptrdiff"]
|
|
FLOAT_TYPES = ["float", "double"]
|
|
ALL_NUMERIC_TYPES = INTEGER_TYPES + FLOAT_TYPES
|
|
|
|
_EXPECTED_OPS = {
|
|
"fetch": ALL_NUMERIC_TYPES,
|
|
"set": ALL_NUMERIC_TYPES + ["longlong"],
|
|
"swap": ALL_NUMERIC_TYPES,
|
|
"compare_swap": INTEGER_TYPES, # CAS not provided on float/double.
|
|
"fetch_add": INTEGER_TYPES,
|
|
"fetch_inc": INTEGER_TYPES,
|
|
"add": INTEGER_TYPES + ["longlong"],
|
|
"inc": INTEGER_TYPES + ["longlong"],
|
|
"fetch_and": ["uint32", "uint64"],
|
|
"fetch_or": ["uint32", "uint64"],
|
|
"fetch_xor": ["uint32", "uint64"],
|
|
"and": ["uint32", "uint64"],
|
|
"or": ["uint32", "uint64"],
|
|
"xor": ["uint32", "uint64"],
|
|
}
|
|
|
|
|
|
def test_amo_matrix_completeness():
|
|
"""Every (type, op) in the expected matrix is bound in rocshmem4py."""
|
|
missing = []
|
|
for op, types in _EXPECTED_OPS.items():
|
|
for t in types:
|
|
name = f"rocshmem_{t}_atomic_{op}"
|
|
if not hasattr(rocshmem4py, name):
|
|
missing.append(name)
|
|
assert not missing, f"Missing AMO bindings: {missing}"
|
|
|
|
from rocshmem4py import _AMO_NAMES
|
|
|
|
expected_count = sum(len(types) for types in _EXPECTED_OPS.values())
|
|
assert expected_count == len(_AMO_NAMES), (
|
|
f"test matrix ({expected_count}) vs imported AMOs ({len(_AMO_NAMES)})"
|
|
)
|
|
# longlong: only set/add/inc per rocshMEM header subset in rocshmem4py.cc.
|
|
longlong_ops = {
|
|
n.removeprefix("rocshmem_longlong_atomic_")
|
|
for n in _AMO_NAMES
|
|
if "longlong" in n
|
|
}
|
|
assert longlong_ops == {"set", "add", "inc"}
|
|
|
|
|
|
@requires_torch
|
|
@requires_multi_pe
|
|
@requires_host_amo_runtime
|
|
def test_int_atomic_fetch_add_against_peer0():
|
|
"""Every PE atomically adds 1 to peer 0's counter; peer 0 sees n_pes."""
|
|
n = rocshmem_n_pes()
|
|
me = rocshmem_my_pe()
|
|
|
|
# Symmetric int counter.
|
|
nbytes = 4
|
|
buf = rocshmem4py.SymmetricBuffer(nbytes)
|
|
try:
|
|
t = torch.as_tensor(buf, device="cuda").view(torch.int32).view([1])
|
|
t.zero_()
|
|
rocshmem_barrier_all()
|
|
torch.cuda.synchronize()
|
|
|
|
rocshmem4py.rocshmem_int_atomic_fetch_add(buf.ptr, 1, 0)
|
|
rocshmem_barrier_all()
|
|
torch.cuda.synchronize()
|
|
|
|
if me == 0:
|
|
assert int(t.item()) == n
|
|
finally:
|
|
buf.free()
|
|
|
|
|
|
@requires_torch
|
|
@requires_multi_pe
|
|
@requires_host_amo_runtime
|
|
def test_int_atomic_compare_swap_lock_pattern():
|
|
"""Use CAS to acquire a lock from peer 0; only one PE wins."""
|
|
me = rocshmem_my_pe()
|
|
nbytes = 4
|
|
buf = rocshmem4py.SymmetricBuffer(nbytes)
|
|
try:
|
|
t = torch.as_tensor(buf, device="cuda").view(torch.int32).view([1])
|
|
t.zero_()
|
|
rocshmem_barrier_all()
|
|
torch.cuda.synchronize()
|
|
|
|
# Each PE attempts CAS(0 -> me+1) on peer 0's counter.
|
|
rocshmem4py.rocshmem_int_atomic_compare_swap(buf.ptr, 0, me + 1, 0)
|
|
rocshmem_barrier_all()
|
|
torch.cuda.synchronize()
|
|
|
|
if me == 0:
|
|
assert 1 <= int(t.item()) <= rocshmem_n_pes()
|
|
finally:
|
|
buf.free()
|
|
|
|
|
|
@requires_torch
|
|
@requires_multi_pe
|
|
@requires_host_amo_runtime
|
|
def test_uint64_atomic_or_against_peer0():
|
|
"""Every PE ORs (1 << me) into peer 0's mask; peer 0 sees full bitmask."""
|
|
n = rocshmem_n_pes()
|
|
me = rocshmem_my_pe()
|
|
|
|
nbytes = 8
|
|
buf = rocshmem4py.SymmetricBuffer(nbytes)
|
|
try:
|
|
# uint64 = int64 view here; we set/check via ints.
|
|
t = torch.as_tensor(buf, device="cuda").view(torch.int64).view([1])
|
|
t.zero_()
|
|
rocshmem_barrier_all()
|
|
torch.cuda.synchronize()
|
|
|
|
rocshmem4py.rocshmem_uint64_atomic_or(buf.ptr, 1 << me, 0)
|
|
rocshmem_barrier_all()
|
|
torch.cuda.synchronize()
|
|
|
|
if me == 0:
|
|
# Use unsigned interpretation: shift mask is fine as int up to 63 PEs.
|
|
expected = (1 << n) - 1 if n < 64 else -1
|
|
assert int(t.item()) == expected
|
|
finally:
|
|
buf.free()
|