Repository navigation
feat(hipblaslt): add host API for the fused GEMM + all-to-all epilogue - #11342
Conversation
✅ All Checks Passed — Ready for Review
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🎉 All checks passed! This PR is ready for review. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #11342 +/- ##
========================================
Coverage 70.29% 70.29%
========================================
Files 2812 2812
Lines 462769 462779 +10
Branches 68122 68124 +2
========================================
+ Hits 325271 325281 +10
Misses 113943 113943
Partials 23555 23555
*This pull request uses carry forward flags. Click here to find out more.
🚀 New features to boost your workflow:
|
A peer in another process gets its flag region through hipIpcOpenMemHandle with hipIpcMemLazyEnablePeerAccess, but a peer in this process had its raw device pointer stored and nothing else, leaving another device's memory unreachable when the copy engine writes to it. The ordinal that hipDeviceEnablePeerAccess needs was not in the exchange payload, so it travels there now. Already-enabled is a success: in a single process the W handles enable each other pairwise.
#11342 defines hipblasLtFusedEpilogueDescriptor inside amd_detail/hipblaslt.cpp and hands rocblaslt only an opaque pointer, so the launch path cannot read the A2A attributes it needs. Resolve the descriptor into a POD at the C-API layer, where its fields are visible, and carry that plus the handle's communicator view on RocblasltContractionProblem. This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. The macro-tile requirements the function defers to are enforced by the kernel generator as solution rejections, which is what its own comment already describes. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
#11342 defines hipblasLtFusedEpilogueDescriptor inside amd_detail/hipblaslt.cpp and hands rocblaslt only an opaque pointer, so the launch path cannot read the A2A attributes it needs. Resolve the descriptor into a POD at the C-API layer, where its fields are visible, and carry that plus the handle's communicator view on RocblasltContractionProblem. This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
## Motivation Review of #11342 found the two peer branches of hipblasLtSetDeviceComm asymmetric: a peer in another process is mapped with hipIpcMemLazyEnablePeerAccess, while a peer in this process had only its raw device pointer recorded. A pointer from this process is not addressable on another device until peer access is enabled, so the copy engine would fault on a region it was handed. The same review found the communicator state on the handle holding two of its values twice. ## Technical Details - DeviceCommExchange carries the contributing rank's device ordinal, which is what hipDeviceEnablePeerAccess names. A same-process peer on another device gets peer access enabled, already-enabled counting as success since the W handles enable each other pairwise. A peer on this device needs nothing, and HIP rejects a request to reach a device from itself. - device_comm_flags is dropped: the handle's own allocation is entry device_comm_rank of device_comm_peer_flags. release_device_comm tells the three kinds of entry apart by index and by mapped, so it frees its own region, closes its IPC mappings, and leaves a same-process peer's pointer to the handle that allocated it. - device_comm_registered is dropped for device_comm_world != 0. Registration rejects a world below one and publishes world last, so nonzero already means what the bool did. ## Test Plan FusedA2ACommPeers.TwoRanksInOneProcessRegister: two ranks on two handles rendezvousing through a real allgather, which is the only way more than one rank can register in a single process. Where a second device exists the ranks take one each, so the peer branch has to make the region reachable rather than merely record its address; destroying both handles then exercises the ownership split. ## Test Result 40 tests across 7 suites pass on gfx950, with aux_handle_test, aux_attr_test, and aux_ext_test unaffected. The test machine has one device, so the cross-device hipDeviceEnablePeerAccess call itself is reached only where a second is present. ## Submission Checklist - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests. Co-authored-by: Ding, Yi <yi.ding@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
#11342 defines hipblasLtFusedEpilogueDescriptor inside amd_detail/hipblaslt.cpp and hands rocblaslt only an opaque pointer, so the launch path cannot read the A2A attributes it needs. Resolve the descriptor into a POD at the C-API layer, where its fields are visible, and carry that plus the handle's communicator view on RocblasltContractionProblem. This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
Review of #11342 found the two peer branches of hipblasLtSetDeviceComm asymmetric: a peer in another process is mapped with hipIpcMemLazyEnablePeerAccess, while a peer in this process had only its raw device pointer recorded. A pointer from this process is not addressable on another device until peer access is enabled, so the copy engine would fault on a region it was handed. The same review found the communicator state on the handle holding two of its values twice. - DeviceCommExchange carries the contributing rank's device ordinal, which is what hipDeviceEnablePeerAccess names. A same-process peer on another device gets peer access enabled, already-enabled counting as success since the W handles enable each other pairwise. A peer on this device needs nothing, and HIP rejects a request to reach a device from itself. - device_comm_flags is dropped: the handle's own allocation is entry device_comm_rank of device_comm_peer_flags. release_device_comm tells the three kinds of entry apart by index and by mapped, so it frees its own region, closes its IPC mappings, and leaves a same-process peer's pointer to the handle that allocated it. - device_comm_registered is dropped for device_comm_world != 0. Registration rejects a world below one and publishes world last, so nonzero already means what the bool did. FusedA2ACommPeers.TwoRanksInOneProcessRegister: two ranks on two handles rendezvousing through a real allgather, which is the only way more than one rank can register in a single process. Where a second device exists the ranks take one each, so the peer branch has to make the region reachable rather than merely record its address; destroying both handles then exercises the ownership split. 40 tests across 7 suites pass on gfx950, with aux_handle_test, aux_attr_test, and aux_ext_test unaffected. The test machine has one device, so the cross-device hipDeviceEnablePeerAccess call itself is reached only where a second is present. - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests. Co-authored-by: Ding, Yi <yi.ding@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1901f36 to
f332481
Compare
#11342 defines hipblasLtFusedEpilogueDescriptor inside amd_detail/hipblaslt.cpp and hands rocblaslt only an opaque pointer, so the launch path cannot read the A2A attributes it needs. Resolve the descriptor into a POD at the C-API layer, where its fields are visible, and carry that plus the handle's communicator view on RocblasltContractionProblem. This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
|
Are we required to exclude this feature at compile time? If not, compile it unconditionally and return If the implementation must remain optional, isolate it behind a stable interface and select enabled or stub translation units at build time. The disabled implementation should return |
|
This change makes hipBLASLt own communication-transport work in addition to the GEMM-specific flag layout. That code becomes a long-term maintenance responsibility of the hipBLASLt team. rocSHMEM exposes the corresponding primitives today for one processing element per process: bootstrap, symmetric allocation, peer-address lookup, synchronization, and release. RCCL develop also exposes symmetric-window peer-pointer APIs. I implemented the rocSHMEM path behind a separate C ABI adapter against the current PR head ( Current PR: // Error handling and logging omitted.
if(j == rank || peer.pid == mine.pid)
{
if(j != rank && peer.device != mine.device)
hipDeviceEnablePeerAccess(peer.device, 0);
h->device_comm_peer_flags[j] = peer.flags;
h->device_comm_peer_flags_mapped[j] = false;
}
else
{
void* mapped = nullptr;
hipIpcOpenMemHandle(&mapped, peer.ipc, hipIpcMemLazyEnablePeerAccess);
h->device_comm_peer_flags[j] = mapped;
h->device_comm_peer_flags_mapped[j] = true;
}External rocSHMEM adapter: // Error handling omitted.
rocshmem_uniqueid_t uid{};
rocshmem_get_uniqueid(&uid); // rank 0 obtains it, then distributes it
rocshmem_init_attr_t attr{};
rocshmem_set_attr_uniqueid_args(rank, world, &uid, &attr);
rocshmem_init_attr(ROCSHMEM_INIT_WITH_UNIQUEID, &attr);
void* localFlags = rocshmem_malloc(bytes); // collective symmetric allocation
hipMemset(localFlags, 0, bytes);
rocshmem_barrier_all();
for(int peer = 0; peer < world; ++peer)
peerFlags[peer] = rocshmem_ptr(localFlags, peer);
// On coordinated teardown:
rocshmem_free(localFlags); // also collective |
Review of #11342 found the two peer branches of hipblasLtSetDeviceComm asymmetric: a peer in another process is mapped with hipIpcMemLazyEnablePeerAccess, while a peer in this process had only its raw device pointer recorded. A pointer from this process is not addressable on another device until peer access is enabled, so the copy engine would fault on a region it was handed. The same review found the communicator state on the handle holding two of its values twice. - DeviceCommExchange carries the contributing rank's device ordinal, which is what hipDeviceEnablePeerAccess names. A same-process peer on another device gets peer access enabled, already-enabled counting as success since the W handles enable each other pairwise. A peer on this device needs nothing, and HIP rejects a request to reach a device from itself. - device_comm_flags is dropped: the handle's own allocation is entry device_comm_rank of device_comm_peer_flags. release_device_comm tells the three kinds of entry apart by index and by mapped, so it frees its own region, closes its IPC mappings, and leaves a same-process peer's pointer to the handle that allocated it. - device_comm_registered is dropped for device_comm_world != 0. Registration rejects a world below one and publishes world last, so nonzero already means what the bool did. FusedA2ACommPeers.TwoRanksInOneProcessRegister: two ranks on two handles rendezvousing through a real allgather, which is the only way more than one rank can register in a single process. Where a second device exists the ranks take one each, so the peer branch has to make the region reachable rather than merely record its address; destroying both handles then exercises the ownership split. 40 tests across 7 suites pass on gfx950, with aux_handle_test, aux_attr_test, and aux_ext_test unaffected. The test machine has one device, so the cross-device hipDeviceEnablePeerAccess call itself is reached only where a second is present. - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests. Co-authored-by: Ding, Yi <yi.ding@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
ec9fb61 to
ebc274d
Compare
Review of #11342 found the two peer branches of hipblasLtSetDeviceComm asymmetric: a peer in another process is mapped with hipIpcMemLazyEnablePeerAccess, while a peer in this process had only its raw device pointer recorded. A pointer from this process is not addressable on another device until peer access is enabled, so the copy engine would fault on a region it was handed. The same review found the communicator state on the handle holding two of its values twice. - DeviceCommExchange carries the contributing rank's device ordinal, which is what hipDeviceEnablePeerAccess names. A same-process peer on another device gets peer access enabled, already-enabled counting as success since the W handles enable each other pairwise. A peer on this device needs nothing, and HIP rejects a request to reach a device from itself. - device_comm_flags is dropped: the handle's own allocation is entry device_comm_rank of device_comm_peer_flags. release_device_comm tells the three kinds of entry apart by index and by mapped, so it frees its own region, closes its IPC mappings, and leaves a same-process peer's pointer to the handle that allocated it. - device_comm_registered is dropped for device_comm_world != 0. Registration rejects a world below one and publishes world last, so nonzero already means what the bool did. FusedA2ACommPeers.TwoRanksInOneProcessRegister: two ranks on two handles rendezvousing through a real allgather, which is the only way more than one rank can register in a single process. Where a second device exists the ranks take one each, so the peer branch has to make the region reachable rather than merely record its address; destroying both handles then exercises the ownership split. 40 tests across 7 suites pass on gfx950, with aux_handle_test, aux_attr_test, and aux_ext_test unaffected. The test machine has one device, so the cross-device hipDeviceEnablePeerAccess call itself is reached only where a second is present. - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests. Co-authored-by: Ding, Yi <yi.ding@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
ebc274d to
a8c370e
Compare
This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
The fused GEMM + all-to-all epilogue redistributes the leading run of D's feature dimension across the ranks of a device communicator from the GEMM's own store path, removing a separate collective pass. This lands the host side ahead of the TensileLite kernel so callers can build and validate a request, and so the family's enum and attribute ranges are reserved before the other fused-epilogue families arrive. - Public API: `HIPBLASLT_FUSEABLE_EPILOGUE_A2A_PREFIX` (7, leaving 0-6 to the chainable families) and its five attributes (12-16), plus `hipblasLtSdmaQueue_t`, `hipblasLtA2ACompletionMode_t`, and the shared `hipblasLtFusedEpilogueCreate` / `Add` / `SetAttribute` / `Destroy` builder. The builder documents family-neutral rules; per-family specifics stay on the enums. - `hipblasLtSetDeviceComm` registers a per-handle view of a device communicator, allocating the fine-grained flag regions and resolving peer addresses through a caller-supplied allgather, with IPC mapping for out-of-process peers. The flag-block layout is twinned with `FUSED_A2A_FLAG_BLOCK_BYTES` in the kernel's `FusedA2AKernArg.hpp`. - The descriptor attaches through `HIPBLASLT_MATMUL_DESC_FUSED_EPILOGUE` (106, after develop's `UNIFORM_SUMMATION_ORDER_EXT`) and is carried by `_rocblaslt_matmul_desc` as a non-owning pointer. - Validation is split three ways: stage composition at `Add`, required attributes at attach, and communicator, shape, and layout constraints before the heuristic and again at launch. No kernel is wired, so a well-formed request reports `HIPBLAS_STATUS_NOT_SUPPORTED`. Build `hipblaslt-test` and run `--gtest_filter='FusedA2A*'` on gfx950, covering descriptor lifecycle, builder composition rules, attribute range and width checks, communicator registration, attach-time completeness, and each dispatch-time rejection. 39 tests across 6 suites pass on gfx950. Nothing dispatches a GEMM: a valid request reports `NOT_SUPPORTED`, confirming the validation chain runs to completion. - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests. Co-authored-by: Cursor <cursoragent@cursor.com>
Review of #11342 found the two peer branches of hipblasLtSetDeviceComm asymmetric: a peer in another process is mapped with hipIpcMemLazyEnablePeerAccess, while a peer in this process had only its raw device pointer recorded. A pointer from this process is not addressable on another device until peer access is enabled, so the copy engine would fault on a region it was handed. The same review found the communicator state on the handle holding two of its values twice. - DeviceCommExchange carries the contributing rank's device ordinal, which is what hipDeviceEnablePeerAccess names. A same-process peer on another device gets peer access enabled, already-enabled counting as success since the W handles enable each other pairwise. A peer on this device needs nothing, and HIP rejects a request to reach a device from itself. - device_comm_flags is dropped: the handle's own allocation is entry device_comm_rank of device_comm_peer_flags. release_device_comm tells the three kinds of entry apart by index and by mapped, so it frees its own region, closes its IPC mappings, and leaves a same-process peer's pointer to the handle that allocated it. - device_comm_registered is dropped for device_comm_world != 0. Registration rejects a world below one and publishes world last, so nonzero already means what the bool did. FusedA2ACommPeers.TwoRanksInOneProcessRegister: two ranks on two handles rendezvousing through a real allgather, which is the only way more than one rank can register in a single process. Where a second device exists the ranks take one each, so the peer branch has to make the region reachable rather than merely record its address; destroying both handles then exercises the ownership split. 40 tests across 7 suites pass on gfx950, with aux_handle_test, aux_attr_test, and aux_ext_test unaffected. The test machine has one device, so the cross-device hipDeviceEnablePeerAccess call itself is reached only where a second is present. - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests. Co-authored-by: Ding, Yi <yi.ding@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
- Guard the getpid() include so Windows finds <process.h>, which was breaking the Windows CI build. - Stop re-deriving the flag region's geometry on the host. Its size now comes from the kernel's FUSED_A2A_FLAG_BLOCK_BYTES via rocblaslt_device_comm_flag_block_bytes(), declared in rocblaslt-auxiliary.h so hipblaslt.cpp gains no Tensile dependency. Only HIPBLASLT_DEVICE_COMM_MAX_WORLD still needs a static_assert, being public API that sizes the handle's per-rank arrays. - Split the two-rank registration test. The same-device case stays in pre_checkin; the cross-device case becomes a multi_gpu suite that skips below two GPUs instead of placing both ranks on device 0, where it passed without running the branch it was meant to cover. - Have that test assert registration's effect: each rank disables peer access first, so a later request reporting already-enabled can only be registration's work. - Add a multi_gpu ctest category so a two-device runner can select the suite. - Scope the allgather doc's "memcpy" to world == 1 and note that returning before every rank contributes fails registration rather than hanging. Co-authored-by: Cursor <cursoragent@cursor.com>
- register EnableGemmA2AFusion in _GLOBAL_PARAMETER_IGNORE_KEYS, without which the strict global-parameter gate aborted every device-library codegen run with ConfigTypeError, failing every Linux, Windows, ASAN and internal build on all architectures - admit a logic entry whose problem type is absent rather than subscripting it, which raised TypeError in three generateLogicDataAndSolutions characterization tests Co-authored-by: Cursor <cursoragent@cursor.com>
…A gate A MagicMock problem type is truthy under both subscript and .get, so the fusion filter read the synthetic logic files in test_gfx1250_asic_revision.py as fused and dropped them, leaving masterLibraries empty and failing three tests. Exclude only on an explicit True, so a problem type that cannot answer is admitted rather than silently emptying the library. Co-authored-by: Cursor <cursoragent@cursor.com>
The gate's only unit coverage called the predicate directly, so the branch that actually drops a logic file and reports the count went untested. That is the path a previously over-matching filter took when it emptied the library behind a successful build, which is what makes the reported count worth asserting alongside the drop. Drives the real merge loop over one fused logic file with the gate off. Co-authored-by: Cursor <cursoragent@cursor.com>
Review feedback on the fused GEMM + all-to-all suite. The fake allgathers silenced unused-parameter warnings with (void) no-op statements; leaving the names in comments does the same thing without the statements, and keeps the send and receive buffers distinguishable in failingAllgather, which takes two void* in a row. This matches the /*overwrite*/ style already used in testing_aux_check_numerics_gemm.hpp and tensile_host.cpp. Also tighten the file header and the helper comments. What stays is what the code cannot show: that suite names must keep their category token or the ctest presets silently stop selecting them, and why the multi_gpu test has to assert on peer access as a side effect. Co-authored-by: Cursor <cursoragent@cursor.com>
hipblasLtSetDeviceComm establishes a device communicator itself: rank exchange, peer access, IPC mapping of a peer's flag region, and ownership of the flag allocations. RCCL already owns communicator topology and peer transport, so leaving this here risks a second communication ABI. Point at the tracking issue for moving the setup to RCCL or a shared transport layer. Packet emission stays in TensileLite regardless, since it depends on GEMM tile completion and matrix layout. Co-authored-by: Cursor <cursoragent@cursor.com>
a8c370e to
7e28eb9
Compare
This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
TheRock Submodule Bump ActivityNewest first
|
This is what is left of the commit that used to add the public API and the device communicator here. #11342 now provides hipblasLtSetDeviceComm, the A2A_PREFIX stage and every attribute, so the header additions, the communicator implementation and its peer-kind bookkeeping are all dropped; what remains is the rocblaslt-level plumbing that PR does not reach. Everything about such a request that is checkable before the heuristic runs goes in a header of its own; handle.h stays limited to the handle definitions and the helpers that resolve an effective value out of handle/desc/pref. JIRA ID : ROCM-27524
#11342 closed validate_fused_a2a_launch with an unconditional NOT_SUPPORTED, standing in for the kernels that did not exist yet. Those kernels land on this branch, so the placeholder rejected every request before rocblaslt was reached and both SDMA samples failed at hipblasLtMatmulAlgoGetHeuristic with it. What the function defers is still enforced, in two places rather than one. The shape-independent half -- MacroTile0/1 in {128, 256}, data-parallel only, no split-K, a BF16 D, gfx94x/95x -- is a generation-time solution rejection in Solution.py. The half that depends on the problem is the FusedA2ATileDivisible predicate, which requires extent % (MacroTile0 * world) == 0 and rejects a world of zero, and so runs at selection. Both also filter the size-independent getAllSolutions fallback, which is what keeps a shape with no Equality logic entry from picking up an algo that does not fit it. WellFormedRequestReportsMissingCapability asserted that placeholder. A replacement would have had three different correct answers - SUCCESS with an A2A device library, NOT_SUPPORTED with an ordinary one, and INVALID_VALUE with none - so it is dropped rather than pinned to one environment, and the file's header note is updated to match. Samples 30 and 31 cover that path end to end; the twelve rejection tests around it are untouched. JIRA ID : ROCM-27524
Motivation
The fused GEMM + all-to-all epilogue redistributes the leading run of D's feature dimension across the ranks of a device communicator from the GEMM's own store path, removing a separate collective pass. This lands the host side ahead of the TensileLite kernel so callers can build and validate a request, and so the family's enum and attribute ranges are reserved before the other fused-epilogue families arrive.
JIRA ID : ROCM-27524
Technical Details
HIPBLASLT_FUSEABLE_EPILOGUE_A2A_PREFIX(7, leaving 0-6 to the chainable families) and its five attributes (12-16), plushipblasLtSdmaQueue_t,hipblasLtA2ACompletionMode_t, and the sharedhipblasLtFusedEpilogueCreate/Add/SetAttribute/Destroybuilder. The builder documents family-neutral rules; per-family specifics stay on the enums.hipblasLtSetDeviceCommregisters a per-handle view of a device communicator, allocating the fine-grained flag regions and resolving peer addresses through a caller-supplied allgather, with IPC mapping for out-of-process peers. The flag-block layout is twinned withFUSED_A2A_FLAG_BLOCK_BYTESin the kernel'sFusedA2AKernArg.hpp.HIPBLASLT_MATMUL_DESC_FUSED_EPILOGUE(106, after develop'sUNIFORM_SUMMATION_ORDER_EXT) and is carried by_rocblaslt_matmul_descas a non-owning pointer.Add, required attributes at attach, and communicator, shape, and layout constraints before the heuristic and again at launch. No kernel is wired, so a well-formed request reportsHIPBLAS_STATUS_NOT_SUPPORTED.Test Plan
Build
hipblaslt-testand run--gtest_filter='FusedA2A*'on gfx950, covering descriptor lifecycle, builder composition rules, attribute range and width checks, communicator registration, attach-time completeness, and each dispatch-time rejection.Test Result
39 tests across 6 suites pass on gfx950. Nothing dispatches a GEMM: a valid request reports
NOT_SUPPORTED, confirming the validation chain runs to completion.Submission Checklist