Skip to content

Commit aa9a6d5

Browse files
Make cuBLASLt descriptor wrappers move-safe (#3078)
## 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` Authors: - Minh Vu (https://github.com/fallintoplace) - Divye Gala (https://github.com/divyegala) Approvers: - Divye Gala (https://github.com/divyegala) URL: #3078
1 parent 49df104 commit aa9a6d5

3 files changed

Lines changed: 96 additions & 8 deletions

File tree

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION.
2+
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
#pragma once
@@ -19,6 +19,7 @@
1919
#include <cublasLt.h>
2020

2121
#include <type_traits>
22+
#include <utility>
2223

2324
namespace raft {
2425
namespace linalg::detail {
@@ -108,12 +109,19 @@ struct cublastlt_matrix_layout {
108109
}
109110
inline cublastlt_matrix_layout(const cublastlt_matrix_layout&) = delete;
110111
inline auto operator=(const cublastlt_matrix_layout&) -> cublastlt_matrix_layout& = delete;
111-
inline cublastlt_matrix_layout(cublastlt_matrix_layout&&) = default;
112-
inline auto operator=(cublastlt_matrix_layout&&) -> cublastlt_matrix_layout& = default;
112+
inline cublastlt_matrix_layout(cublastlt_matrix_layout&& other) noexcept
113+
: res(std::exchange(other.res, nullptr))
114+
{
115+
}
116+
inline auto operator=(cublastlt_matrix_layout&& other) noexcept -> cublastlt_matrix_layout&
117+
{
118+
std::swap(res, other.res);
119+
return *this;
120+
}
113121

114122
inline ~cublastlt_matrix_layout() noexcept
115123
{
116-
RAFT_CUBLAS_TRY_NO_THROW(cublasLtMatrixLayoutDestroy(res));
124+
if (res != nullptr) { RAFT_CUBLAS_TRY_NO_THROW(cublasLtMatrixLayoutDestroy(res)); }
117125
}
118126

119127
// NOLINTNEXTLINE
@@ -137,12 +145,19 @@ struct cublastlt_matmul_desc {
137145
}
138146
inline cublastlt_matmul_desc(const cublastlt_matmul_desc&) = delete;
139147
inline auto operator=(const cublastlt_matmul_desc&) -> cublastlt_matmul_desc& = delete;
140-
inline cublastlt_matmul_desc(cublastlt_matmul_desc&&) = default;
141-
inline auto operator=(cublastlt_matmul_desc&&) -> cublastlt_matmul_desc& = default;
148+
inline cublastlt_matmul_desc(cublastlt_matmul_desc&& other) noexcept
149+
: res(std::exchange(other.res, nullptr))
150+
{
151+
}
152+
inline auto operator=(cublastlt_matmul_desc&& other) noexcept -> cublastlt_matmul_desc&
153+
{
154+
std::swap(res, other.res);
155+
return *this;
156+
}
142157

143158
inline ~cublastlt_matmul_desc() noexcept
144159
{
145-
RAFT_CUBLAS_TRY_NO_THROW(cublasLtMatmulDescDestroy(res));
160+
if (res != nullptr) { RAFT_CUBLAS_TRY_NO_THROW(cublasLtMatmulDescDestroy(res)); }
146161
}
147162

148163
// NOLINTNEXTLINE

cpp/tests/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# =============================================================================
22
# cmake-format: off
3-
# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
3+
# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# SPDX-License-Identifier: Apache-2.0
55
# cmake-format: on
66
# =============================================================================
@@ -149,6 +149,7 @@ if(BUILD_TESTS)
149149
linalg/binary_op.cu
150150
linalg/cholesky_r1.cu
151151
linalg/coalesced_reduction.cu
152+
linalg/cublaslt_wrappers.cpp
152153
linalg/divide.cu
153154
linalg/dot.cu
154155
linalg/eig.cu
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <raft/linalg/detail/cublaslt_wrappers.hpp>
7+
8+
#include <gtest/gtest.h>
9+
10+
#include <utility>
11+
12+
namespace raft::linalg::detail {
13+
14+
TEST(Raft, CublasLtMatrixLayoutMoveConstructorTransfersOwnership)
15+
{
16+
auto layout = cublastlt_matrix_layout{CUDA_R_32F, 2, 3, 2};
17+
auto raw = static_cast<cublasLtMatrixLayout_t>(layout);
18+
19+
ASSERT_NE(raw, nullptr);
20+
21+
auto moved = std::move(layout);
22+
23+
EXPECT_EQ(static_cast<cublasLtMatrixLayout_t>(layout), nullptr);
24+
EXPECT_EQ(static_cast<cublasLtMatrixLayout_t>(moved), raw);
25+
}
26+
27+
TEST(Raft, CublasLtMatrixLayoutMoveAssignmentTransfersOwnership)
28+
{
29+
auto src = cublastlt_matrix_layout{CUDA_R_32F, 2, 3, 2};
30+
auto src_raw = static_cast<cublasLtMatrixLayout_t>(src);
31+
auto dst = cublastlt_matrix_layout{CUDA_R_16F, 4, 5, 4};
32+
auto dst_raw = static_cast<cublasLtMatrixLayout_t>(dst);
33+
34+
ASSERT_NE(src_raw, nullptr);
35+
ASSERT_NE(dst_raw, nullptr);
36+
37+
dst = std::move(src);
38+
39+
EXPECT_EQ(static_cast<cublasLtMatrixLayout_t>(src), dst_raw);
40+
EXPECT_EQ(static_cast<cublasLtMatrixLayout_t>(dst), src_raw);
41+
}
42+
43+
TEST(Raft, CublasLtMatmulDescMoveConstructorTransfersOwnership)
44+
{
45+
auto desc = cublastlt_matmul_desc{CUBLAS_COMPUTE_32F, CUDA_R_32F};
46+
auto raw = static_cast<cublasLtMatmulDesc_t>(desc);
47+
48+
ASSERT_NE(raw, nullptr);
49+
50+
auto moved = std::move(desc);
51+
52+
EXPECT_EQ(static_cast<cublasLtMatmulDesc_t>(desc), nullptr);
53+
EXPECT_EQ(static_cast<cublasLtMatmulDesc_t>(moved), raw);
54+
}
55+
56+
TEST(Raft, CublasLtMatmulDescMoveAssignmentTransfersOwnership)
57+
{
58+
auto src = cublastlt_matmul_desc{CUBLAS_COMPUTE_32F, CUDA_R_32F};
59+
auto src_raw = static_cast<cublasLtMatmulDesc_t>(src);
60+
auto dst = cublastlt_matmul_desc{CUBLAS_COMPUTE_16F, CUDA_R_16F};
61+
auto dst_raw = static_cast<cublasLtMatmulDesc_t>(dst);
62+
63+
ASSERT_NE(src_raw, nullptr);
64+
ASSERT_NE(dst_raw, nullptr);
65+
66+
dst = std::move(src);
67+
68+
EXPECT_EQ(static_cast<cublasLtMatmulDesc_t>(src), dst_raw);
69+
EXPECT_EQ(static_cast<cublasLtMatmulDesc_t>(dst), src_raw);
70+
}
71+
72+
} // namespace raft::linalg::detail

0 commit comments

Comments
 (0)