Skip to content

[REVIEW] Addition of gemm_strided_batched - #3106

Open
landrumb wants to merge 2 commits into
NVIDIA:mainfrom
landrumb:landrumb/cublaslt-strided-batched-matmul
Open

[REVIEW] Addition of gemm_strided_batched#3106
landrumb wants to merge 2 commits into
NVIDIA:mainfrom
landrumb:landrumb/cublaslt-strided-batched-matmul

Conversation

@landrumb

Copy link
Copy Markdown

This PR adds the wrapper for strided and batched matrix multiplication with cublasLt requested by @achirkin in the reviews for NVIDIA/cuvs#2352 , where this operation is used for batched row-wise dot products. TF32 support is included.

The associated test covers most of the functionality besides eg transposed inputs, but can be expanded if reviewer(s) prefer.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8d6171c2-1f92-4d81-8880-890d18203f9d

📥 Commits

Reviewing files that changed from the base of the PR and between 088de61 and a8d89a2.

📒 Files selected for processing (1)
  • cpp/include/raft/linalg/gemm.cuh
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/include/raft/linalg/gemm.cuh

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added strided-batched matrix multiplication for processing multiple matrices in a single operation.
    • Added configurable compute types, including TF32 support.
    • Added support for host- and device-resident scalar parameters, custom matrix strides, batch counts, and transpose options.
  • Bug Fixes
    • Added validation for batch counts and matrix strides to help prevent invalid operations.
    • Improved handling of default scalar values during batched matrix multiplication.

Walkthrough

Changes

The change adds cuBLASLt strided-batched matrix multiplication support, exposes it through gemm_strided_batched, and adds coverage for padded column-major matrix batches.

Strided-batched GEMM

Layer / File(s) Summary
Batched layouts and compute descriptors
cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp
Matrix layouts now validate batch counts and set batch-count and batch-stride attributes. Matmul descriptors accept an optional explicit compute type.
Batched execution and GEMM API
cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp, cpp/include/raft/linalg/gemm.cuh
The new wrappers construct batched layouts, prepare scalar coefficients, and call cublasLtMatmul. The public API forwards dimensions, strides, batch count, pointer mode, and compute type.
Padded batch validation
cpp/tests/linalg/gemm_basic.cpp
The test validates two strided batches, computed output values, and preserved padding.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: adding the gemm_strided_batched wrapper.
Description check ✅ Passed The description explains the strided-batched cuBLASLt wrapper, its use case, TF32 support, and associated test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cpp/tests/linalg/gemm_basic.cpp (1)

188-212: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extend the batched coverage to the remaining argument combinations.

The test covers only the non-transposed, default-coefficient, host-pointer path. The following cases stay untested and each one exercises distinct code in matmul_strided_batched:

  • trans_a / trans_b set to true, which changes the layout row/column swap and the lda/ldb requirements.
  • Explicit non-null alpha and beta, which verifies that existing C padding and values are combined correctly.
  • DevicePointerMode = true, which exercises the coef_wrapper device allocation path.
  • A zero batch stride, which is the cuBLASLt broadcast case.

Do you want me to generate these additional test cases?

As per path instructions: "Tests should use synthetic data, validate numerical results and padding, and cover important edge cases."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/tests/linalg/gemm_basic.cpp` around lines 188 - 212, Extend the batched
GEMM coverage around gemm_strided_batched to add synthetic cases for transposed
A/B operands with corresponding lda/ldb values, non-null alpha and beta while
validating C padding and accumulation, DevicePointerMode enabled to exercise
device coefficient allocation, and zero batch strides for broadcast behavior.
Assert numerical outputs and untouched padding for each case while preserving
the existing baseline test.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp`:
- Around line 378-416: Update matmul_strided_batched to validate positive m, n,
k, and batch_count; require lda and ldc to meet the row requirements for the
configured layouts and ldb to meet the corresponding B-matrix requirement;
validate non-null matrix and scalar pointers and permitted pointer locations
before constructing cuBLASLt layouts. Validate strides for invalid values while
explicitly allowing zero strides for broadcasting, and reject any precondition
failure before forwarding inputs to cuBLASLt.
- Around line 417-432: Update the strided-batched matmul flow around
cublasLtMatmul to create or reuse a valid initialized cublasLtMatmulAlgo_t
instead of passing nullptr. Cache heuristic algorithm results and their
descriptors using strides, batch_count, and compute_type in the key, then
configure and pass workspace when supported while preserving the existing
operation and coefficient parameters.

---

Nitpick comments:
In `@cpp/tests/linalg/gemm_basic.cpp`:
- Around line 188-212: Extend the batched GEMM coverage around
gemm_strided_batched to add synthetic cases for transposed A/B operands with
corresponding lda/ldb values, non-null alpha and beta while validating C padding
and accumulation, DevicePointerMode enabled to exercise device coefficient
allocation, and zero batch strides for broadcast behavior. Assert numerical
outputs and untouched padding for each case while preserving the existing
baseline test.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 821297de-e419-4c57-93c2-210c59d3c380

📥 Commits

Reviewing files that changed from the base of the PR and between 07652ba and 088de61.

📒 Files selected for processing (3)
  • cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp
  • cpp/include/raft/linalg/gemm.cuh
  • cpp/tests/linalg/gemm_basic.cpp

Comment thread cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp
Comment thread cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp

@achirkin achirkin 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.

Thanks for implementing this! Let's refine the API (see below)

Comment on lines +318 to +338
template <typename A_t, typename B_t, typename C_t, typename S_t, bool DevicePointerMode = false>
void gemm_strided_batched(
raft::resources const& res,
bool trans_a,
bool trans_b,
uint64_t m,
uint64_t n,
uint64_t k,
const S_t* alpha,
const A_t* a,
uint64_t lda,
int64_t stride_a,
const B_t* b,
uint64_t ldb,
int64_t stride_b,
const S_t* beta,
C_t* c,
uint64_t ldc,
int64_t stride_c,
int32_t batch_count,
cublasComputeType_t compute_type = detail::get_matmul_type<S_t, A_t, B_t, C_t>())

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.

Please rewrite the public API to use mdspans - we should not add new legacy overloads to public functions.
Also note, we can encode the _strided_ part as in the layout policy - it doesn't need to be a part of the name; I'd suggest we make it here generic and pass the stride = matrix size if the layout is not strided.

Suggested change
template <typename A_t, typename B_t, typename C_t, typename S_t, bool DevicePointerMode = false>
void gemm_strided_batched(
raft::resources const& res,
bool trans_a,
bool trans_b,
uint64_t m,
uint64_t n,
uint64_t k,
const S_t* alpha,
const A_t* a,
uint64_t lda,
int64_t stride_a,
const B_t* b,
uint64_t ldb,
int64_t stride_b,
const S_t* beta,
C_t* c,
uint64_t ldc,
int64_t stride_c,
int32_t batch_count,
cublasComputeType_t compute_type = detail::get_matmul_type<S_t, A_t, B_t, C_t>())
template <typename ValueType,
typename IndexType,
typename LayoutPolicyX,
typename LayoutPolicyY,
typename LayoutPolicyZ,
typename ScalarIdxType = std::uint32_t,
typename ScalarViewType = raft::host_scalar_view<ValueType, ScalarIdxType>,
typename = std::enable_if_t<std::disjunction_v<
std::is_same<ScalarViewType, raft::host_scalar_view<ValueType, ScalarIdxType>>,
std::is_same<ScalarViewType, raft::device_scalar_view<ValueType, ScalarIdxType>>>>>
void gemm_batched(raft::resources const& res,
raft::device_mdspan<ValueType, raft::extent_3d<IndexType>, LayoutPolicyX> x,
raft::device_mdspan<ValueType, raft::extent_3d<IndexType>, LayoutPolicyY> y,
raft::device_mdspan<ValueType, raft::extent_3d<IndexType>, LayoutPolicyZ> z,
std::optional<ScalarViewType> alpha = std::nullopt,
std::optional<ScalarViewType> beta = std::nullopt,
cublasComputeType_t compute_type_override = detail::get_matmul_type<ValueType, ValueType, ValueType, ValueType>()))

uint64_t ldc,
int64_t stride_c,
int32_t batch_count,
cublasComputeType_t compute_type = detail::get_matmul_type<S_t, A_t, B_t, C_t>())

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.

Also I like the way you suggest to optionally pass the cublas compute type override - looks like a good compromise between the user-friendliness and flexibility.
However, maybe we could sacrifice a bit of brevity add replace the optional default argument with two overloads (with/without argument) - to avoid detail namespace from appearing in the public headers?

@achirkin achirkin added feature request New feature or request non-breaking Non-breaking change labels Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request New feature or request non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants