Skip to content

[P2P] Bind one-sided IPC test tensors to the endpoint device - #1050

Merged
YangZhou1997 merged 3 commits into
uccl-project:mainfrom
0z5a:fix/bug01-onesided-ipc-device-binding
Sep 20, 2026
Merged

YangZhou1997 merged 3 commits into
uccl-project:mainfrom
0z5a:fix/bug01-onesided-ipc-device-binding

Conversation

@0z5a

@0z5a 0z5a commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Description

Bind every payload and reference tensor to the endpoint's process-local GPU ordinal. The test previously put rank 1's endpoint on GPU 1 while allocating its tensors on GPU 0, so it could report success without exercising the intended cross-GPU payload path.

The preflight compares canonical full PCI addresses: hexadecimal case and domain padding are normalized, while distinct domains remain distinct. It queries the active CUDA/HIP runtime directly and has no nvidia-smi dependency.

Test result

2× L40S, torch 2.13.0+cu129, native extension built with CUDA 12.8 from this PR's engine sources. This host has no RDMA devices, so the endpoint uses UCCL_P2P_TRANSPORT=nccl; all one-sided operations below use the actual CUDA IPC path.

UCCL_P2P_TRANSPORT=nccl CUDA_VISIBLE_DEVICES=0,1 OMP_NUM_THREADS=1 \
  python -m torch.distributed.run --standalone --nproc-per-node=2 \
  p2p/tests/test_engine_onesided_ipc.py
# Repeated with CUDA_VISIBLE_DEVICES=1,0.
python -m pytest -q p2p/tests/test_engine_device_binding.py
Check Result
GPU order 0,1 All 16 operation/buffer-mode combinations pass; both processes exit 0
GPU order 1,0 All 16 combinations pass; both processes exit 0
Physical identities 0000:21:00.0 and 0000:81:00.0, correctly reversed with visibility order
Deliberate endpoint/payload GPU mismatch Rejected before transfer
Case, domain and CUDA/HIP runtime unit checks 6 passed
Native build and Black checks Passed

HIP runtime selection is covered with mocks; AMD hardware was not available. These are correctness tests, with no throughput speedup claim. No engine or transport code changes are included.

test_engine_onesided_ipc.py built its endpoint on local_gpu_idx=rank but
allocated every payload and reference tensor with an explicit device="cuda:0"
after torch.cuda.set_device(0). With two processes sharing one visible device
list, rank 1 therefore had its endpoint on GPU 1 while all of its payload
tensors were on GPU 0, so no transfer in the suite exercised a cross-GPU
payload path and the run stayed green.

Resolve the device once per process, use it for the endpoint, for every GPU
tensor and for the reference tensors, and assert that the endpoint BDF matches
the payload device BDF plus that the two ranks resolve to different physical
GPUs.

Verified on 4x L20 (PCIe, no NVLink): pairs 0/1, 1/0 and 0/3 (cross NUMA
island) each run 24/24 PASSED with the endpoint and payload on 34:00.0/35:00.0
respectively, and the pre-fix binding is rejected by the new check.
payload_bdf = _visible_bdf_by_uuid().get(uuid)
assert payload_bdf is not None, f"no nvidia-smi BDF for GPU uuid {uuid}"

assert payload_bdf == endpoint_bdf, (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Normalize PCI-address case before comparing

The engine stores GPU BDFs in lowercase through normalize_pci_bus_id, while nvidia-smi emits uppercase hexadecimal digits. _normalize_bdf only removes the domain, so a correctly bound GPU such as endpoint 0000:ca:00.0 / nvidia-smi 00000000:CA:00.0 fails this assertion before any IPC test runs. I reproduced this using the new helpers with mocked metadata. The numeric-only 34/35/37 addresses in the reported validation do not exercise it. Please canonicalize case on both sides.

Comment thread p2p/tests/test_engine_onesided_ipc.py Outdated

def _normalize_bdf(bdf: str) -> str:
"""Drop the PCI domain so UCCL metadata and nvidia-smi are comparable."""
return bdf.split(":", 1)[1] if bdf.count(":") >= 2 else bdf

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve the PCI domain in physical GPU identities

Dropping the domain makes distinct GPUs such as 0008:01:00.0 and 0009:01:00.0 both resolve to 01:00.0. These addresses occur on Grace Blackwell systems, so the final gathered[0] != gathered[1] assertion rejects a valid cross-GPU run. It also loses the ability to detect a binding mismatch that differs only in the domain. I reproduced the collision with the new helpers. Please normalize the domain's zero padding rather than dropping it, or use full GPU UUIDs for the cross-rank identity check.

Comment thread p2p/tests/test_engine_onesided_ipc.py Outdated
def _visible_bdf_by_uuid() -> dict:
"""Map every visible GPU UUID to its PCI bus id (domain stripped)."""
out = subprocess.run(
["nvidia-smi", "--query-gpu=uuid,pci.bus_id", "--format=csv,noheader"],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep the binding check usable on HIP systems

This now unconditionally runs nvidia-smi during startup. The P2P engine and this torch.cuda-based test also support AMD/HIP, where nvidia-smi is normally absent, so the new preflight raises FileNotFoundError before exercising any IPC operation. I reproduced that failure by making the subprocess call report a missing executable. Please retrieve the PCI address through the active GPU runtime or add an appropriate HIP path instead of imposing an NVIDIA-only executable dependency.

Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
@0z5a

0z5a commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Fixed all three identity issues: canonical lowercase full-domain BDFs, preserved domain distinctions, and active CUDA/HIP runtime queries without nvidia-smi.

Validation Result
L40S cross-GPU IPC, order 0,1 16/16 combinations passed, clean exit
Reversed order 1,0 16/16 passed, clean exit
Real endpoint/payload mismatch Rejected
Case/domain/runtime unit checks 6 passed

NCCL endpoint backend on this non-RDMA host; transfers use real CUDA IPC. HIP selection was mocked, not tested on AMD hardware.

@YangZhou1997
YangZhou1997 self-requested a review September 20, 2026 17:37
@YangZhou1997
YangZhou1997 merged commit e487c78 into uccl-project:main Sep 20, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants