Fix NIC to GPU proximity detection on systems with multiple PCIe domains (candidate-1.70) - #353
Fix NIC to GPU proximity detection on systems with multiple PCIe domains (candidate-1.70)#353paklui wants to merge 3 commits into
Conversation
Replace single-winner NIC selection with all equally-close NICs. Previously, when multiple NICs tied as closest to a GPU, only the least-used NIC (tracked by assignedCount) was recorded, so systems where each GPU has 2 NICs at equal PCIe distance reported only 1 NIC per GPU instead of 2 -- closestNicsToGpu is a topology map, not a traffic assignment, so recording all equally-close NICs is the correct semantic. Apply the same fix to the reverse mapping: the bus-ID-distance fallback now collects all GPUs at minimum distance rather than stopping at the first one found.
Use GetClosestNicsToGpu (vector) instead of GetClosestNicToGpu (int) in both the NIC table and GPU table so all equally-close NICs are shown as a comma/space-separated list (e.g. "0,1") instead of a single index, matching the topology map populated by the previous commit. Also fix the NIC table loop: the old code called GetClosestNicsToGpu inside a per-NIC outer loop, making numNics*numGpus calls. Build an inverse GPU-per-NIC map up front (numGpus calls) and look up the result directly in the print loop instead.
…break Rename ExtractBusNumber/GetBusIdDistance to ExtractDomain/GetDomainDistance. Bus numbers are firmware-assigned and do not reliably track physical closeness within a PCIe domain, so they are no longer used to discriminate among candidates that already share the same LCA depth or that fall back to the no-tree-match path -- doing so could break ties between NICs that are genuinely equidistant from a GPU. Distance is now computed from the PCIe domain field, which only matters as a deterministic tiebreak across domains. Also fix GetNearestDevicesInTree to treat an unparseable address (-1) as the largest possible distance rather than using -1 directly in the comparison, so it never incorrectly outranks a candidate with a real distance while still remaining eligible as a last resort. The GPU-to-NIC fallback loop now reads distances from ibvAddressList instead of the raw device list, so it stays consistent with the PCIe-tree path above and never maps a GPU to a NIC without an active port.
|
Thanks Pak for this contribution. I have a couple of comments
|
Candidate-1.70 counterpart of #352 (same fix, rebased onto
candidate-1.70instead ofdevelop).Motivation
On systems where each GPU sits in its own PCIe domain, TransferBench picked the wrong NIC as closest, thus every GPU mapped to a single front-end NIC that is not wired to any GPU, while the back-end NICs actually adjacent to each GPU mapped to nothing. The mapping decides which NIC a GPU uses for RDMA transfers (
NIC_NEARESTexecutor,nic_ringspreset). With incorrect mapping it would mean benchmarking using a NIC that is not attached to that GPU's root complex, producing misleading numbers. The causes are:This PR fixes the topology detection so it works correctly on systems with multiple PCIe domains and multiple NICs per GPU (verified on a 4-GPU, 8 BE + 1 FE, or 2 BE NICs per GPU).
Technical Details
Use PCIe domain, not bus number, for the distance metric.
ExtractBusNumberbecameExtractDomain, andGetBusIdDistancebecameGetDomainDistance.The distance between two devices is now computed as follows:
Bus numbers are no longer used to compare devices within a domain. Firmware assigns them, and they do not reliably indicate which device is physically closer. Using them would also separate NICs that are genuinely the same distance from a GPU, and reporting those ties is the point of this fix.
An unparseable address (-1) is clamped to
INT_MAXrather than compared directly, so it remains eligible as a last resort but can never incorrectly outrank a candidate with a known distance.Record all equally-close NICs/GPUs. Replaces the
assignedCountround-robin, which picked one NIC per GPU even when several were tied. Both directions now keep the full tie set, matching howGetClosestNicsToGpu()was already shaped.Trade-off: the removed round-robin was intended to spread multi-port NICs (ports sharing one busId) across GPUs. With ties recorded,
nic_ringsnow uses all tied ports, better, but a bareNIC_NEARESTresolves slot 0 for every GPU rather than spreading. Use the explicitN<gpu>.<slot>form to select a specific port.Update the CLI topology display. The display called the scalar
GetClosestNicToGpu(), so it would have shown one NIC even after (2). Switched to the vector API. The NIC list is space-separated so the field stays a single column underOUTPUT_TO_CSV=1, where the separator is a comma.Both fallback paths (GPU->NIC and NIC->GPU) now read from
ibvAddressListrather than the raw device list, so a NIC with a down port is never recorded as "closest" --NicIsActive()would otherwise abort the Transfer withERR_FATALrather than falling back.Test Plan
Built and ran on a
candidate-1.70base (92b0df7) on a gfx1250 rack node: 4 GPUs, 8 BE NICs + 1 FE NIC, 2 NICs wired via PCIe per GPU, each GPU in its own PCIe domain, ROCm 7.15.0.Test Result
Currently with candidate-1.70 or develop HEAD:
With
./TransferBench(no args):Each GPU is now correctly mapped to the NIC(s) in its own PCIe domain (GPU0->1,2, GPU1->5,6, GPU2->3,4, GPU3->7,8), and the front-end NIC (domain
0000) correctly shows no GPU mapping.Also see #352 (same fix against
develop), which was additionally verified against a real link-flap event: a NIC going down/up correctly changed a GPU from a tie to a single mapping and back.Submission Checklist