Skip to content

Commit 97d22c8

Browse files
authored
cublas team verified workaround for large GEMM algo68 bug (#3100)
The fix in #3098 was incomplete, as according to the cublas team there is no guarantee that `algo13` will be returned along with `algo68` (the failing version for A matrix with > 2^31 elements). Also, the cublas team will patch this bug in version `13.6.1` so we will automatically receive the fix by guarding only against `13.6.0`.
1 parent fcef0d3 commit 97d22c8

2 files changed

Lines changed: 84 additions & 66 deletions

File tree

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

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include <raft/core/resource/cublaslt_handle.hpp>
1111
#include <raft/core/resource/cuda_stream.hpp>
1212
#include <raft/core/resource/custom_resource.hpp>
13-
#include <raft/core/resource/device_properties.hpp>
1413
#include <raft/core/resources.hpp>
1514
#include <raft/util/cache.hpp>
1615
#include <raft/util/cuda_data_type.hpp>
@@ -19,7 +18,6 @@
1918

2019
#include <cublasLt.h>
2120

22-
#include <array>
2321
#include <type_traits>
2422

2523
namespace raft {
@@ -102,21 +100,27 @@ struct matmul_key_hash {
102100
};
103101

104102
/**
105-
* cuBLASLt 13.6 and later may select algorithm 68 once A's physical span reaches 2^31 elements.
106-
* That algorithm fails during execution for FP32, so select the next ranked heuristic instead.
103+
* cuBLASLt 13.6.0, shipped with CUDA 13.3, may select algorithm 68 once A's physical span reaches
104+
* 2^31 elements. That algorithm fails during execution for FP32.
107105
*/
108-
inline auto needs_cublaslt_13_6_workaround(const matmul_key_t& args,
109-
std::size_t version,
110-
int device_major,
111-
int device_minor) noexcept -> bool
106+
inline auto needs_cublaslt_13_6_workaround(const matmul_key_t& args, std::size_t version) noexcept
107+
-> bool
112108
{
113109
constexpr uint64_t max_safe_span = (uint64_t{1} << 31) - 1;
114110
const auto a_columns = args.trans_a ? args.m : args.k;
115-
const bool is_affected_architecture =
116-
(device_major == 10 && device_minor == 0) ||
117-
(device_major == 12 && (device_minor == 0 || device_minor == 1));
118-
return version >= 130600 && is_affected_architecture && args.lda != 0 &&
119-
a_columns > max_safe_span / args.lda;
111+
return version == 130600 && args.lda != 0 && a_columns > max_safe_span / args.lda;
112+
}
113+
114+
/**
115+
* Querying with a physical A leading dimension that is not 16-byte aligned suppresses algorithm 68.
116+
* The returned algorithm is then used with the real descriptors.
117+
*/
118+
inline auto get_cublaslt_13_6_heuristic_args(const matmul_key_t& args) noexcept -> matmul_key_t
119+
{
120+
constexpr uint64_t fp32_elements_per_16_bytes = 4;
121+
auto heuristic_args = args;
122+
if (heuristic_args.lda % fp32_elements_per_16_bytes == 0) { ++heuristic_args.lda; }
123+
return heuristic_args;
120124
}
121125

122126
inline auto get_cublaslt_algorithm_id(const cublasLtMatmulHeuristicResult_t& heuristic) -> int
@@ -199,6 +203,24 @@ struct cublastlt_matmul_desc {
199203
}
200204
};
201205

206+
/** Preference descriptor for a cublasLt matmul heuristic query. */
207+
struct cublastlt_matmul_preference {
208+
cublasLtMatmulPreference_t res{nullptr};
209+
210+
inline cublastlt_matmul_preference() { RAFT_CUBLAS_TRY(cublasLtMatmulPreferenceCreate(&res)); }
211+
inline cublastlt_matmul_preference(const cublastlt_matmul_preference&) = delete;
212+
inline auto operator=(const cublastlt_matmul_preference&)
213+
-> cublastlt_matmul_preference& = delete;
214+
215+
inline ~cublastlt_matmul_preference() noexcept
216+
{
217+
RAFT_CUBLAS_TRY_NO_THROW(cublasLtMatmulPreferenceDestroy(res));
218+
}
219+
220+
// NOLINTNEXTLINE
221+
inline operator cublasLtMatmulPreference_t() const noexcept { return res; }
222+
};
223+
202224
/** Full description of matmul. */
203225
struct matmul_desc {
204226
cublastlt_matmul_desc desc;
@@ -219,46 +241,40 @@ struct matmul_desc {
219241
bool use_cublaslt_13_6_workaround = false;
220242
if constexpr (std::is_same_v<S, float> && std::is_same_v<A, float> &&
221243
std::is_same_v<B, float> && std::is_same_v<C, float>) {
222-
const auto& device_properties = resource::get_device_properties(res);
223-
use_cublaslt_13_6_workaround = needs_cublaslt_13_6_workaround(
224-
args, cublasLtGetVersion(), device_properties.major, device_properties.minor);
244+
use_cublaslt_13_6_workaround = needs_cublaslt_13_6_workaround(args, cublasLtGetVersion());
225245
}
226246

227-
constexpr int workaround_heuristic_results = 2;
228-
std::array<cublasLtMatmulHeuristicResult_t, workaround_heuristic_results> heuristic_results{};
229-
const int requested_results = use_cublaslt_13_6_workaround ? workaround_heuristic_results : 1;
230247
int algo_count;
231-
cublasLtMatmulPreference_t preference;
232-
RAFT_CUBLAS_TRY(cublasLtMatmulPreferenceCreate(&preference));
233-
RAFT_CUBLAS_TRY(cublasLtMatmulAlgoGetHeuristic(resource::get_cublaslt_handle(res),
234-
r.desc,
235-
r.a,
236-
r.b,
237-
r.c,
238-
r.c,
239-
preference,
240-
requested_results,
241-
heuristic_results.data(),
242-
&algo_count));
243-
RAFT_CUBLAS_TRY(cublasLtMatmulPreferenceDestroy(preference));
244-
245-
RAFT_EXPECTS(algo_count > 0, "cuBLASLt did not return a matmul algorithm");
246-
if (!use_cublaslt_13_6_workaround) {
247-
r.heuristics = heuristic_results.front();
248-
return r;
248+
cublastlt_matmul_preference preference;
249+
const auto query_heuristic = [&](cublasLtMatrixLayout_t a_layout,
250+
cublasLtMatrixLayout_t c_layout) {
251+
RAFT_CUBLAS_TRY(cublasLtMatmulAlgoGetHeuristic(resource::get_cublaslt_handle(res),
252+
r.desc,
253+
a_layout,
254+
r.b,
255+
c_layout,
256+
c_layout,
257+
preference,
258+
1,
259+
&r.heuristics,
260+
&algo_count));
261+
};
262+
263+
if (use_cublaslt_13_6_workaround) {
264+
const auto heuristic_args = get_cublaslt_13_6_heuristic_args(args);
265+
const auto heuristic_a = cublastlt_matrix_layout::for_matmul<A>(
266+
!(heuristic_args.trans_a), heuristic_args.m, heuristic_args.k, heuristic_args.lda);
267+
query_heuristic(heuristic_a, r.c);
268+
} else {
269+
query_heuristic(r.a, r.c);
249270
}
250271

272+
RAFT_EXPECTS(algo_count > 0, "cuBLASLt did not return a matmul algorithm");
251273
constexpr int faulty_algorithm = 68;
252-
for (int i = 0; i < algo_count; ++i) {
253-
const auto& candidate = heuristic_results[i];
254-
if (candidate.state == CUBLAS_STATUS_SUCCESS && candidate.workspaceSize == 0 &&
255-
get_cublaslt_algorithm_id(candidate) != faulty_algorithm) {
256-
r.heuristics = candidate;
257-
return r;
258-
}
274+
if (use_cublaslt_13_6_workaround) {
275+
RAFT_EXPECTS(get_cublaslt_algorithm_id(r.heuristics) != faulty_algorithm,
276+
"cuBLASLt 13.6.0 returned faulty algorithm 68 for the workaround query");
259277
}
260-
261-
RAFT_FAIL("cuBLASLt 13.6 did not return a safe algorithm for the affected large FP32 GEMM");
262278
return r;
263279
}
264280
};

cpp/tests/linalg/gemm_basic.cpp

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -166,26 +166,20 @@ TEST(Raft, GemmPointerModeDeviceDefaults) { test_gemm_pointer_mode_device(false,
166166
TEST(Raft, GemmCublasLt136WorkaroundPredicate)
167167
{
168168
constexpr std::size_t affected_version = 130600;
169-
constexpr int affected_device_major = 12;
170-
constexpr int affected_device_minor = 1;
171169
const detail::matmul_key_t below_boundary{134217727, 1, 2, 16, 1, 134217727, true, true};
172170
const detail::matmul_key_t at_boundary{134217728, 1, 2, 16, 1, 134217728, true, true};
173171
const detail::matmul_key_t above_boundary{134217729, 1, 2, 16, 1, 134217729, true, true};
174172

175173
const auto needs_workaround = [&](const auto& args) {
176-
return detail::needs_cublaslt_13_6_workaround(
177-
args, affected_version, affected_device_major, affected_device_minor);
174+
return detail::needs_cublaslt_13_6_workaround(args, affected_version);
178175
};
179176

180177
EXPECT_FALSE(needs_workaround(below_boundary));
181178
EXPECT_TRUE(needs_workaround(at_boundary));
182179
EXPECT_TRUE(needs_workaround(above_boundary));
183-
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(
184-
at_boundary, 130599, affected_device_major, affected_device_minor));
185-
EXPECT_TRUE(detail::needs_cublaslt_13_6_workaround(
186-
at_boundary, 130601, affected_device_major, affected_device_minor));
187-
EXPECT_TRUE(detail::needs_cublaslt_13_6_workaround(
188-
at_boundary, 130700, affected_device_major, affected_device_minor));
180+
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, 130599));
181+
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, 130601));
182+
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, 130700));
189183

190184
auto different_output = at_boundary;
191185
different_output.trans_b = false;
@@ -204,18 +198,26 @@ TEST(Raft, GemmCublasLt136WorkaroundPredicate)
204198
EXPECT_FALSE(needs_workaround(invalid_lda));
205199
}
206200

207-
TEST(Raft, GemmCublasLt136WorkaroundArchitectures)
201+
TEST(Raft, GemmCublasLt136WorkaroundHeuristicArgs)
208202
{
209-
constexpr std::size_t affected_version = 130600;
210-
const detail::matmul_key_t at_boundary{134217728, 1, 2, 16, 1, 134217728, true, true};
203+
const auto query_lda = [](uint64_t lda) {
204+
const detail::matmul_key_t args{134217728, 1, 2, lda, 1, 134217728, true, true};
205+
return detail::get_cublaslt_13_6_heuristic_args(args).lda;
206+
};
211207

212-
EXPECT_TRUE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 10, 0));
213-
EXPECT_TRUE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 12, 0));
214-
EXPECT_TRUE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 12, 1));
215-
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 7, 5));
216-
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 8, 0));
217-
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 8, 9));
218-
EXPECT_FALSE(detail::needs_cublaslt_13_6_workaround(at_boundary, affected_version, 9, 0));
208+
EXPECT_EQ(query_lda(12), 13);
209+
EXPECT_EQ(query_lda(15), 15);
210+
EXPECT_EQ(query_lda(16), 17);
211+
212+
const detail::matmul_key_t args{134217728, 1, 2, 16, 1, 134217728, true, true};
213+
const auto heuristic_args = detail::get_cublaslt_13_6_heuristic_args(args);
214+
EXPECT_EQ(heuristic_args.m, args.m);
215+
EXPECT_EQ(heuristic_args.n, args.n);
216+
EXPECT_EQ(heuristic_args.k, args.k);
217+
EXPECT_EQ(heuristic_args.ldb, args.ldb);
218+
EXPECT_EQ(heuristic_args.ldc, args.ldc);
219+
EXPECT_EQ(heuristic_args.trans_a, args.trans_a);
220+
EXPECT_EQ(heuristic_args.trans_b, args.trans_b);
219221
}
220222

221223
} // namespace raft::linalg

0 commit comments

Comments
 (0)