Skip to content

Make cuBLASLt descriptor wrappers move-safe#3078

Open
fallintoplace wants to merge 1 commit into
NVIDIA:mainfrom
fallintoplace:agent/fix-cublaslt-move-safety
Open

Make cuBLASLt descriptor wrappers move-safe#3078
fallintoplace wants to merge 1 commit into
NVIDIA:mainfrom
fallintoplace:agent/fix-cublaslt-move-safety

Conversation

@fallintoplace

Copy link
Copy Markdown

Summary

  • make the cuBLASLt matrix layout and matmul descriptor wrappers move-safe
  • transfer descriptor ownership explicitly on move construction and move assignment
  • make moved-from wrappers null-safe on destruction
  • add focused tests that verify each wrapper transfers its raw handle and nulls the source on move

Why

These wrappers own raw cublasLt* descriptors and destroy them in their destructors, but their move constructor and move assignment operator were defaulted. That meant moving a wrapper copied the raw handle into the destination without clearing the source, so both objects believed they owned the same descriptor.

Impact

This closes a double-destroy hazard in any path that moves or move-assigns these wrappers, including code that caches or returns compound matmul descriptor objects.

Root cause

The wrapper types were acting like RAII owners while still using raw-pointer default move semantics.

Validation

  • git diff --check
  • attempted PARALLEL_LEVEL=8 ./build.sh tests -n --limit-tests=LINALG_TEST
  • local build is blocked here because nvcc is not installed and CMake cannot resolve CUDAToolkit_ROOT

@copy-pr-bot

copy-pr-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 8, 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: 0d3c072f-7b13-42cf-9a11-3b2538816602

📥 Commits

Reviewing files that changed from the base of the PR and between 2b09036 and 98290ce.

📒 Files selected for processing (3)
  • cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp
  • cpp/tests/CMakeLists.txt
  • cpp/tests/linalg/cublaslt_wrappers.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • cpp/tests/CMakeLists.txt
  • cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved ownership handling for cuBLASLt wrapper objects during move operations, preventing double-destruction and ensuring moved-from wrappers don’t attempt cleanup.
    • Destructors now only release the underlying cuBLASLt resources when they are still valid.
  • Tests

    • Added new unit tests covering move construction and move assignment for the cuBLASLt matrix layout and matmul descriptor wrappers.
    • Updated test build configuration to include the new test source.

Walkthrough

Updates cuBLASLt handle wrappers with explicit noexcept move operations and null-safe destruction. Adds four GoogleTest cases covering move construction and assignment, and registers the test source in the CMake target.

Changes

cuBLASLt RAII Move Semantics

Layer / File(s) Summary
Move semantics and conditional destruction
cpp/include/raft/linalg/detail/cublaslt_wrappers.hpp
Adds <utility>, updates both wrapper types with explicit move construction and swap-based move assignment, and conditionally destroys non-null handles.
Move behavior tests and build wiring
cpp/tests/linalg/cublaslt_wrappers.cpp, cpp/tests/CMakeLists.txt
Adds four tests for ownership transfer in both wrapper types and includes the new translation unit in LINALG_TEST.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: making the cuBLASLt wrapper types move-safe.
Description check ✅ Passed The description matches the changeset and accurately explains the move-safety fix, tests, and validation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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 your contribution! The change looks reasonable. However, before we move forward, could you please tell what is the original bug/problem being addressed? These wrapper types are in the detail namespace, that is not supposed to be used outside of the raft implementation details.

Comment on lines +119 to +121
if (res != nullptr) { RAFT_CUBLAS_TRY_NO_THROW(cublasLtMatrixLayoutDestroy(res)); }
res = std::exchange(other.res, nullptr);
}

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.

Rather than having multiple cublasLtMatrixLayoutDestroy uses, perhaps, a cleaner solution would be to use std::swap(this->res, other.res) and rely on the other one being correctly destroyed?

@fallintoplace fallintoplace force-pushed the agent/fix-cublaslt-move-safety branch from 2b09036 to 98290ce Compare July 13, 2026 12:56
@fallintoplace

Copy link
Copy Markdown
Author

A small repro is:

auto src = cublastlt_matmul_desc{CUBLAS_COMPUTE_32F, CUDA_R_32F};
auto raw = static_cast<cublasLtMatmulDesc_t>(src);
auto dst = std::move(src);

EXPECT_EQ(static_cast<cublasLtMatmulDesc_t>(src), nullptr);
EXPECT_EQ(static_cast<cublasLtMatmulDesc_t>(dst), raw);

On main, the first expectation fails because the defaulted move leaves both objects holding the same handle. When they go out of scope, both destructors try to destroy it.
The same move can happen internally because cublastlt_matmul_desc::for_matmul() and matmul_desc::create() return named local objects. Building with -fno-elide-constructors forces that path.
I also changed move assignment to use std::swap as suggested and updated the tests accordingly.

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.

2 participants