Skip to content

[HLSL] Add LinAlg matrix accumulation contention coverage - #8818

Open
Jack Elliott (JoeCitizen) wants to merge 2 commits into
microsoft:mainfrom
JoeCitizen:linalg-hlk-accumulation-contention-L1
Open

[HLSL] Add LinAlg matrix accumulation contention coverage#8818
Jack Elliott (JoeCitizen) wants to merge 2 commits into
microsoft:mainfrom
JoeCitizen:linalg-hlk-accumulation-contention-L1

Conversation

@JoeCitizen

@JoeCitizen Jack Elliott (JoeCitizen) commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Adds four matrix accumulation contention cases that run several Waves across several groups against one destination, where the existing cases run a single Wave in a single group. Each Wave contributes a distinct amount, so the expected total is weighted by Wave index rather than only pinning how many additions were applied. The two existing single-Wave cases are unchanged by construction, since the weighted totals reduce to their previous values when one Wave is active.

Assisted-by: GitHub Copilot

The existing matrix accumulation tests run a single Wave in a single
group, so they demonstrate that an accumulation lands but never that it
lands atomically. An implementation that dropped or duplicated
concurrent accumulations would pass all of them. These four cases run
several Waves across several groups against one destination.

Each Wave contributes a distinct amount rather than the same one. Wave w
repeats its additions w plus one times in the descriptor cases, and
scales the value it accumulates by w plus one in the group-shared cases,
so the expected total is weighted by Wave index. Had every Wave
contributed the same amount the total would only pin the number of
additions applied, and a dropped update from one Wave would be cancelled
exactly by a duplicated update from another. Weighting also rejects a
lowering that assumes the participating Waves hold the same operand and
applies one representative value scaled by the participant count.

Both shaders already selected a single Wave with a hardcoded index test,
so the change is to compare against an ACTIVE_WAVE_COUNT define instead
and let the existing helpers take a Wave count and a dispatch width. The
existing callers pass one Wave and one group, which is what they did
before. The group-shared oracle is now computed per element as
InitialValue + WaveWeightSum * (AccumulateStartingValue + Index), where
WaveWeightSum is the sum of one through the active Wave count. Both that
and the descriptor count reduce to their previous values when one Wave
is active, so the two existing cases are unchanged.

The counts are chosen so every intermediate value is exactly
representable, which is what lets the result be compared for equality
rather than within a tolerance. The descriptor cases accumulate a fill
of seven or one across eighty additions, and the group-shared cases
reach at most three hundred and twenty-seven, both well inside the range
where F16 represents integers exactly. Because every partial sum is
exact, the F32 case cannot depend on the order the hardware happens to
apply the additions, which is what OrderInvariant in its name refers to.

The group-shared helper previously rejected anything that was not F16
through one compound condition covering four unrelated parameters. It
now accepts I32 as well and reports each rejected parameter separately,
so a mistake in a future case says which parameter was wrong.

Verified by making every Wave contribute the same amount while leaving
the oracles expecting the weighted total: the two cases that run on WARP
fail, and every other test is unaffected, including the two single-Wave
cases the weighting is designed not to disturb. The two I32 cases skip
on WARP because it reports no I32 accumulation support.

This does not prove the Waves physically overlap in time. Nothing forces
an implementation to run them concurrently, so one that serialises them
passes. WARP reports a Wave size of four, so the contention depth
actually exercised here is low and real depth is untested until this
runs on hardware.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 83725f5d-8e98-4c1d-91ee-ad47629e007b
Assisted-by: GitHub Copilot
Copilot AI balanced review requested due to automatic review settings August 21, 2026 22:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds contention coverage for atomic LinAlg matrix accumulation across multiple Waves and groups.

Changes:

  • Adds four descriptor/group-shared contention tests.
  • Uses weighted Wave contributions and expanded F16/I32 oracles.
  • Preserves existing single-Wave behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tools/clang/unittests/HLSLExec/LinAlgTests.cpp Outdated
The descriptor cases varied how many times each Wave added the same
matrix rather than what each Wave added. Every one of the additions was
therefore identical, so the total pinned only how many were applied and
a dropped update could still be cancelled exactly by a duplicated one.
That is the property the cases were added to reject, so they did not
reject it.

Each Wave now scales what it accumulates and issues a fixed two
additions. The scale is one plus a global index built from SV_GroupID
and the Wave index, so it is distinct across every Wave in every group
and the total pins which Waves landed. A Wave index on its own would
repeat in each group, and an update dropped in one group could be
cancelled by a duplicate in another.

The group-shared shader already scaled the value it accumulated rather
than repeating it, and its destination is group-shared so contention is
confined to one group where Wave indices are already distinct. It is
unchanged.

The oracle becomes the fill times two times the sum of one through the
number of contending Waves, which is sixteen Waves and so 272 times the
fill. The I32 case reaches 1904 and the F32 case 272, and every partial
sum along the way is a smaller integer, so both stay exact and the
results can still be compared for equality. One Wave in one group gives
a scale of one and two additions, which is what the expression produced
before, so the existing single-Wave case is unchanged by construction
rather than by a preprocessor guard.

Verified by dropping the SV_GroupID term so the scale repeats in each
group: the F32 contention case reaches 80 times the fill instead of 272
and fails, while the single-Wave case is unaffected because its scale is
one either way. The I32 contention case skips on WARP, which reports no
I32 accumulation support.

Reported by Copilot code review on the pull request.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 83725f5d-8e98-4c1d-91ee-ad47629e007b
Assisted-by: GitHub Copilot
Copilot AI review requested due to automatic review settings August 21, 2026 23:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment on lines +7896 to +7899
runGroupSharedAccumulate(Device, DxcSupport, Params, Memory,
/*InitialValue=*/7,
/*AccumulateStartingValue=*/1, Verbose,
SelectedWaveSize, ActiveWaveCount);

@damyanp Damyan Pepper (damyanp) left a comment

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.

This LGTM, but I'll leave approval to someone who understands the domain better than I do.

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

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

3 participants