Skip to content

Add a design note for segmented strided elementwise execution - #125

Open
ianmccul wants to merge 3 commits into
mainfrom
docs/segmented-strided-elementwise
Open

ianmccul wants to merge 3 commits into
mainfrom
docs/segmented-strided-elementwise

Conversation

@ianmccul

@ianmccul ianmccul commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Doc-only. Adds docs/backends/cuda/segmented_strided_elementwise.md and links it from the CUDA docs index and from block_coalescing.md.

It also adds a short programmatic dependent launch (PDL) subsection to docs/backends/cuda/runtime.md. That subsection records PDL as the one case the no-stream-affinity rule does not cover. Same-stream use within one submission and programmatic graph edges fit the runtime. Cross-stream programmatic events are undocumented outside stream capture and need a test on compute capability 9.0 or later before use.

Problem. Block tensors use the allocation-wide cuda_partitioned_* path only when both operands share one packed layout, and never for permute/repartition views or three-operand add. Everything else makes one CUDA submission per block: assignment from views, key-superset add/axpy, zero fills (including the contraction's unbound-block fill), CUDA block-SVD sector assembly, and block-SVD factor materialization, which launches once per selected singular vector. Packed CUDA storage also keeps one completion ledger per block, and Stream::wait_on does not deduplicate. After an allocation-wide operation, the next whole-tensor acquisition therefore issues one cudaStreamWaitEvent per block on the same event.

Proposal. A symmetry-agnostic dense primitive: one elementwise operation over a list of differently shaped strided view tuples, in one submission.

  • Segments are built from block view mappings with the existing make_multi_iteration_plan. Adjacent contiguous segments fuse, so the unpadded same-layout case is the allocation-wide path, and padding is never touched.
  • Sorted selections become runs of segments, so SVD truncation needs no index tables.
  • Each operand's offsets are relative to its allocation. Bases are 64-bit; the per-segment decode is 32-bit when the segment fits. Plans depend only on layout, so they can be reused.
  • A load-balancing search over fixed-size element chunks (moderngpu transform_lbs) uses one barrier per chunk, however many tiny blocks the chunk crosses.
  • The submission acquires only the touched block ledgers, waits once per distinct completion, and publishes one shared completion. Untouched blocks stay free for block-local work.
  • Each operation form (for example lhs-only and rhs-only blocks of add) gets its own segment list. Missing inputs are not emulated with zero, which would turn -0.0 into +0.0 and give NaN for non-finite scaling factors.
  • Rejected alternatives include device-side child launches ([gpu] Tensor get/set silently drops elements (and occasionally hangs) for selections over 2048 elements Cytnx-dev/Cytnx#1229), cudaMemcpyBatchAsync/cudaMemcpy3DBatchAsync, CUB DeviceMemcpy::Batched, and CUDA graph capture of the per-block loop.

Open questions for maintainer decision, with recommendations in the note: separate CUDA block storage (recommend declining), when to add the tiled transpose (after benchmarks), plan ownership once caching is added, whether to merge operation forms into one launch, and whether to support async block tensors.

Verification. I checked the note's claims against block_tensor_linear.hpp, block_tensor_svd.hpp, block_tensor_contract.hpp, two_site_split.hpp, backend/cuda/buffer.hpp, runtime.cpp, cuda_storage.hpp and the CUDA elementwise plan and kernel. I checked Cytnx#1229 with gh, and the CUDA 12.8 batch-copy and CUDA 12.1 kernel-parameter facts against NVIDIA's documentation. python3 scripts/check-docs.py and python3 -m unittest discover -s tests/docs pass. I did not run the Doxygen web build. No code changed, and no GPU measurement backs the performance statements.

🤖 Generated with Claude Code

Block tensors reach the whole-buffer CUDA path only when both operands
share one packed layout. Every other case (permute/repartition
materialization, add/axpy with a key superset, truncation, GEMM pack and
scatter, SVD sector assembly) loops over blocks on the host with one
launch and one ledger round per block.

The note proposes a symmetry-agnostic dense primitive: one elementwise
operation over a list of differently shaped strided view tuples in one
node, scheduled by load-balanced element chunks rather than per block,
with plan-time segment classes for permuted copies and structure-keyed
plan caching. Open questions are listed for maintainer decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 13, 2026 02:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-13T02:17:47.215924Z
We couldn’t complete this review. Comment @codex review to try again.
f352ac1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new active design document, segmented_strided_elementwise.md, detailing a dense primitive for segmented strided elementwise execution on CUDA to optimize block-sparse operations into a single kernel launch. It also updates the CUDA backend README and block coalescing documentation to reference this new design. There are no review comments, and I have no feedback to provide.

@ian-review-bot

Copy link
Copy Markdown
Collaborator

Review Bot: review of exact head f352ac12d43ba9e72e5247ebf4f0cee26b768ed1

No defect found.

What the PR does. Doc-only. Adds a design note for one-launch segmented strided elementwise execution on CUDA (per-segment descriptors, load-balanced chunk schedule, segment classes, plan caching) and links it from the CUDA docs index and block_coalescing.md. It is marked as not implemented.

What I reviewed. All three changed files. I checked every relative link in the new note for existence. I compared its claims about the current code with src/uni20/symmetry/block_tensor_linear.hpp and src/uni20/linalg/backends/cuda/elementwise_{plan.hpp,kernel.cuh}.

Verified. All nine relative links resolve (including epoch_design_draft.md). same_packed_layout compares keys, offsets and buffer size, as stated. The partitioned fast paths and per-block host loops exist as described. has_padding() || isfinite(factor) guards exist. The 32/64-bit plan lowering and the grid-stride kernel exist. offsets() exists on packed storage. The ~220-byte descriptor estimate for rank 8 with 2 operands and 64-bit indices checks out arithmetically.

Not checked. The Cytnx#1229 claim and the cudaMemcpyBatchAsync limitations (external). The maximum-rank-of-8 value and the SVD sector-assembly claim, which I did not trace in detail. The performance statements about R/A/B/C data. No build or doc validator was run. CI status was not examined.


Review tier: light (documentation/data-only new pull request (3 files)) · model: claude-sonnet-5-5 · effort: medium

ianmccul and others added 2 commits November 13, 2026 11:09
The first draft treated each operand as one buffer with one access.
Packed CUDA storage keeps one completion ledger per block, and
Stream::wait_on does not deduplicate, so the draft would have kept one
cudaStreamWaitEvent per block. The submission now acquires only the
touched children, waits once per distinct completion, and publishes one
shared completion.

Other changes:

- List the per-block callers that exist today, including CUDA block-SVD
  materialization (one launch per selected singular vector) and the
  contraction zero fill. Coalesced-GEMM pack and scatter are future
  callers only.
- Build segments from block view mappings rather than storage offsets.
  Fuse adjacent contiguous segments, so the unpadded same-layout case
  becomes the allocation-wide path.
- Express sorted selections as runs of segments rather than index tables.
- Keep 64-bit allocation-relative bases with a per-segment 32-bit decode.
- Replace the per-segment barrier walk with a load-balancing search that
  uses one barrier per chunk.
- Use one segment list per operation form instead of emulating missing
  inputs with zero, which would change signed zeros and non-finite
  scaling.
- Add batched-copy, CUB and graph-capture alternatives, and give
  recommendations on the open questions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The no-affinity rule assumes a producer's stream is only available once
the producer has completed. Programmatic dependent launch (compute
capability 9.0+) is the case where overlapping with a running producer
has value, so record its three forms and how each fits the runtime. Only
same-stream use within one submission and programmatic graph edges fit
without changes. Cross-stream programmatic events are undocumented
outside stream capture, and a misregistered dependency would be a silent
race, so they need a hardware test before use.

Also note same-stream PDL for the launches of one segmented submission.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants