Skip to content

feat(tensilelite): reusable ClusterLoad component and tri-state Multicast#9599

Open
jaopaulolc wants to merge 2 commits into
developfrom
users/jolabega/clusterload-tristate-multicast
Open

feat(tensilelite): reusable ClusterLoad component and tri-state Multicast#9599
jaopaulolc wants to merge 2 commits into
developfrom
users/jolabega/clusterload-tristate-multicast

Conversation

@jaopaulolc

@jaopaulolc jaopaulolc commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

JIRA ID : AIHPBLAS-3929

Motivation

The TDM multicast ("cluster load") mask machinery was duplicated inline across KernelWriter, KernelWriterAssembly, and Subtile/SubtileGREmit, and Multicast was implicitly coupled to ClusterDim != [1, 1]. That coupling made it impossible to compose barrier-only clustering and cooperative-load clustering independently, or to reuse the mask attach at a new load site. This PR extracts that machinery into a reusable component and decouples the multicast opt-in from ClusterDim via a tri-state parameter.

This is PR (i) of a stacked series ("reusable ClusterLoad component + tri-state Multicast"): a byte-exact foundation with no new StreamK cluster feature. It supersedes the earlier #9570 (recreated on a clean, rebased single-commit branch).

Technical Details

  • Reusable ClusterLoad component (Components/ClusterLoad.py, ClusterLoadTDM; registered via Component.py + Components/__init__.py). It centralizes the mask value computation (computeMasks), the MulticastMask* SGPR declare/undeclare (declareSgprs / undeclareSgprs), the combined-vs-split A/B topology decision (usesCombinedMask), and the descriptor attach at each load site (applyToDescriptor). It is a byte-exact TDM cluster-load extraction: every method emits assembly identical to the previous inline code, and computeMasks receives the exact SGPR operands the caller already holds so register indices are unchanged. Selection is capability-based (HasTDM + TDMInst == 3), like TensorDataMoverLoad. It contains no StreamK-cluster symbols (grep -c StreamKMulticast == 0).
  • KernelWriter / KernelWriterAssembly / SubtileGREmit rewired to route mask declare, undeclare, compute, and descriptor attach through ClusterLoadTDM.find(...).
  • Tri-state Multicast (ValidParameters = [-1, 0, 1], GlobalParameters default -1): -1 = auto (legacy: int(ClusterDim != [1,1] and StreamK == 0)), 0 = force off, 1 = force on. Multicast is now an int end-to-end (derivation emits 0/1). A pre-load coercion (coerceLegacyMulticastType, scoped to the serialized-state loading path) normalizes the legacy serialized bool (false→0 / true→1) for already-shipped library-logic YAMLs, so the strict type gate and emitted msgpack both see an int (avoids a std::bad_cast at C++ deserialization). Default -1 reproduces the historic ClusterDim-coupled derivation exactly, so every existing YAML (which omits Multicast) derives byte-identically.
  • Multicast is excluded from the kernel name (SolutionStructs/Naming.py discards it): it is a derivation/runtime knob that does not alter emitted assembly, so kernel names stay byte-identical to develop (no _M token) and all _codegen assembly goldens are unchanged.
  • No StreamK cluster feature: the Multicast derivation and ClusterLoad.py reference no StreamK-cluster symbols; the auto (-1) path is exactly int(ClusterDim != [1,1] and StreamK == 0), and ClusterBarrier is gated on StreamK == 0.

Test Plan

Ran the previously-affected characterization suites, the full _codegen assembly golden suite, and the new unit gates (CPU-only, gfx942/gfx1250 fixtures):
SolutionArms, SolutionClass, ValidParameters, test_PrefetchAcrossPersistent.py, characterization/_codegen, test_cluster_load_component.py, test_multicast_legacy_coercion.py, test_multicast_tristate.py.

Test Result

All green: ≈416 passed, 100 _codegen snapshots byte-identical (kernel names unchanged). Develop-owned goldens/tests touched are purely the intentional new int Multicast param:

  • SolutionArms char asserts (is True/False== 1/0) plus _reset restoring the -1 auto sentinel so re-derivation exercises the legacy path.
  • ValidParameters roster/structure snapshots regenerated (purely +Multicast: [-1, 0, 1]).
  • One test-only mock gap fixed in test_PrefetchAcrossPersistent.py (asmCaps HasTDM / kernel TDMInst on the PAP-TDM writer mock so ClusterLoadTDM.find() matches).

FFM: the component is exercised by non-StreamK cluster-load configs (gemm/gfx12/tdm_multicast_gfx1250.yaml, sparse/gfx1250/spmm_tdm_multicast.yaml) — asm-confirmed multicast masks, clean.

gfx1250: (gemm/gfx12/tdm_multicast_gfx1250.yaml, sparse/gfx1250/spmm_tdm_multicast.yaml pass.

Submission Checklist

Risk level

Low. Behavior-preserving refactor plus an opt-in Multicast parameter that defaults to -1 (legacy auto); Multicast is excluded from the kernel name, so emitted assembly and kernel names are byte-identical to develop for all existing configs.

@therock-pr-bot

therock-pr-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
🌿 Branch Name ✅ Pass
📝 PR Title/Description ✅ Pass
Forbidden Files ✅ Pass
🧪 Unit Test ✅ Pass
🔎 pre-commit ✅ Pass
🚫 Draft PR 🔜 To Be Enabled
🚩 Feature Flag 🔜 To Be Enabled
📊 Code Coverage 🔜 To Be Enabled
🤖 therock-pr-bot ✅ Pass

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

@jaopaulolc jaopaulolc added the rocm:gemm algos Label to identify PRs of the GEMM Algorithms team. label Jul 20, 2026
@therock-pr-bot

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

…ulticast

JIRA ID: AIHPBLAS-3929

The TDM multicast ("cluster load") mask machinery was duplicated inline across
KernelWriter, KernelWriterAssembly, and Subtile/SubtileGREmit, and Multicast was
implicitly coupled to ClusterDim != [1, 1]. That coupling made it impossible to
compose barrier-only clustering and cooperative-load clustering independently, or
to reuse the mask attach at a new load site.

- New ClusterLoad component (Components/ClusterLoad.py, registered via
  Component.py + Components/__init__.py). ClusterLoadTDM centralizes the mask
  value computation (computeMasks), the MulticastMask* SGPR declare/undeclare
  (declareSgprs / undeclareSgprs), the topology decision (combined vs split
  A/B masks), and the descriptor attach at each load site (applyToDescriptor).
  It is a behavior-preserving extraction: every method emits byte-identical
  assembly to the previous inline code, and computeMasks receives the exact
  SGPR operands the caller already holds so register indices are unchanged.
  Selection is capability-based (HasTDM + TDMInst == 3), like TensorDataMoverLoad.
- KernelWriter / KernelWriterAssembly / SubtileGREmit now route mask declare,
  undeclare, compute, and descriptor attach through ClusterLoadTDM.find(...).
- Tri-state Multicast parameter (ValidParameters + GlobalParameters default -1):
  -1 = auto (legacy: ClusterDim != [1,1] implies Multicast, minus the StreamK
  cluster paths), 0 = force off, 1 = force on. Default -1 reproduces the historic
  derivation exactly, so every existing YAML (which omits Multicast) derives
  byte-identically.
- Solution.py derives Multicast/ClusterBarrier from the tri-state, keying the
  legacy auto coupling and the ClusterBarrier gate on StreamK == 0.
- Common/Utilities.clusterEnabled() helper for the ClusterDim != [1,1] test.

- Unit: test_cluster_load_component.py (component find/declare/compute/attach),
  test_multicast_tristate.py (-1/0/1 derivation, legacy equivalence).

Low. Behavior-preserving refactor; the tri-state defaults to -1 (legacy auto),
so emitted assembly is unchanged for all existing configs.
@jaopaulolc
jaopaulolc force-pushed the users/jolabega/clusterload-tristate-multicast branch from ddd66d8 to 0bd356e Compare July 20, 2026 17:46
@codecov-commenter

codecov-commenter commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.51240% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...blaslt/tensilelite/Tensile/KernelWriterAssembly.py 10.00% 9 Missing ⚠️
...aslt/tensilelite/Tensile/Components/ClusterLoad.py 90.48% 6 Missing and 2 partials ⚠️
...ects/hipblaslt/tensilelite/Tensile/KernelWriter.py 42.86% 3 Missing and 1 partial ⚠️
...lelite/Tensile/Components/Subtile/SubtileGREmit.py 0.00% 3 Missing ⚠️
...lt/tensilelite/Tensile/SolutionStructs/Solution.py 86.67% 1 Missing and 1 partial ⚠️

❌ Your project check has failed because the head coverage (76.84%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #9599      +/-   ##
===========================================
+ Coverage    64.16%   64.56%   +0.40%     
===========================================
  Files         2757     2758       +1     
  Lines       449555   449605      +50     
  Branches     66143    66150       +7     
===========================================
+ Hits        288443   290261    +1818     
+ Misses      139965   138783    -1182     
+ Partials     21147    20561     -586     
Flag Coverage Δ *Carryforward flag
TensileLite 34.38% <ø> (+0.04%) ⬆️ Carriedforward from 0bd356e
TensileLite-CPP 38.19% <ø> (-0.02%) ⬇️
TensileLite-Unit 35.78% <78.51%> (+0.30%) ⬆️
hipBLAS 90.81% <ø> (+0.19%) ⬆️ Carriedforward from 0bd356e
hipBLASLt 34.63% <ø> (ø)
hipCUB 82.68% <ø> (ø) Carriedforward from 0bd356e
hipDNN 86.20% <ø> (-<0.01%) ⬇️ Carriedforward from 0bd356e
hipFFT 50.30% <ø> (ø) Carriedforward from 0bd356e
hipRAND 76.12% <ø> (ø) Carriedforward from 0bd356e
hipSOLVER 69.18% <ø> (ø) Carriedforward from 0bd356e
hipSPARSE 86.27% <ø> (ø) Carriedforward from 0bd356e
rocBLAS 47.95% <ø> (ø) Carriedforward from 0bd356e
rocFFT 49.16% <ø> (ø) Carriedforward from 0bd356e
rocRAND 57.01% <ø> (ø) Carriedforward from 0bd356e
rocSOLVER 76.84% <ø> (ø) Carriedforward from 0bd356e
rocSPARSE 72.41% <ø> (ø) Carriedforward from 0bd356e
rocThrust 91.36% <ø> (ø) Carriedforward from 0bd356e

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...slt/tensilelite/Tensile/Common/GlobalParameters.py 92.49% <ø> (+5.14%) ⬆️
...aslt/tensilelite/Tensile/Common/ValidParameters.py 93.00% <ø> (ø)
...rojects/hipblaslt/tensilelite/Tensile/Component.py 54.81% <100.00%> (+0.34%) ⬆️
...pblaslt/tensilelite/Tensile/Components/__init__.py 100.00% <ø> (ø)
...aslt/tensilelite/Tensile/SolutionStructs/Naming.py 81.40% <100.00%> (+2.49%) ⬆️
...lt/tensilelite/Tensile/SolutionStructs/Solution.py 62.87% <86.67%> (+3.38%) ⬆️
...lelite/Tensile/Components/Subtile/SubtileGREmit.py 57.38% <0.00%> (-0.10%) ⬇️
...ects/hipblaslt/tensilelite/Tensile/KernelWriter.py 36.09% <42.86%> (+12.33%) ⬆️
...aslt/tensilelite/Tensile/Components/ClusterLoad.py 90.48% <90.48%> (ø)
...blaslt/tensilelite/Tensile/KernelWriterAssembly.py 30.80% <10.00%> (+4.97%) ⬆️

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants