Skip to content

Commit 5658d72

Browse files
authored
Increase cuSparse version requierment for SpMVOp from 12.7 to 12.8 (#1576)
# Problem When running (and compiling) cuOpt with an env that has cuSparse 12.7, solving a problem creates a memory leak. # Cause The experimental API cuSparse_SpMVOp causes memory leaks in version 12.7 on the SpMVOpCreateDescr call. This problem is solved in cuSparse 12.8. # Solution This PR increases the cuSparse version requierment for SpMVOp from 12.7 to 12.8. The memory leak is gone, and it results in simpler code because there is no need to do dlsym dispatching depending on the version. Disclaimer : cuSparse version **!=** cuda version. - cuSparse 12.7 is associated with cuda 13.2 - cuSparse 12.8 is associated with cuda 13.3 Authors: - Bulle Mostovoi (https://github.com/Bubullzz) Approvers: - Nicolas L. Guidotti (https://github.com/nguidotti) - Nicolas Blin (https://github.com/Kh4ster) URL: #1576
1 parent cd8ddad commit 5658d72

3 files changed

Lines changed: 63 additions & 130 deletions

File tree

cpp/src/pdlp/cusparse_view.cu

Lines changed: 53 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -270,51 +270,30 @@ void my_cusparsespmm_preprocess(cusparseHandle_t handle,
270270
}
271271
#endif
272272

273-
#if CUOPT_CUSPARSE_VER_12_7_UP
274-
// SpMVOp symbols. Resolved at runtime via dlsym, because the runtime cuSPARSE version
275-
// might not match the headers used at compile time. cuSPARSE 12.7 corresponds to CUDA
276-
// Toolkit 13.2; cuSPARSE 12.8 corresponds to CUDA Toolkit 13.3. We can go back to
277-
// direct linking once CUDA 14 is adopted.
278-
using cusparseSpMVOp_destroyDescr_sig = cusparse_sig<cusparseSpMVOpDescr_t>;
279-
using cusparseSpMVOp_destroyPlan_sig = cusparse_sig<cusparseSpMVOpPlan_t>;
280-
using cusparseSpMVOp_bufferSize_12_7_sig = cusparse_sig<cusparseHandle_t,
281-
cusparseOperation_t,
282-
cusparseSpMatDescr_t,
283-
cusparseDnVecDescr_t,
284-
cusparseDnVecDescr_t,
285-
cusparseDnVecDescr_t,
286-
cudaDataType,
287-
size_t*>;
288-
using cusparseSpMVOp_createDescr_12_7_sig = cusparse_sig<cusparseHandle_t,
289-
cusparseSpMVOpDescr_t*,
290-
cusparseOperation_t,
291-
cusparseSpMatDescr_t,
292-
cusparseDnVecDescr_t,
293-
cusparseDnVecDescr_t,
294-
cusparseDnVecDescr_t,
295-
cudaDataType,
296-
void*>;
297273
#if CUOPT_CUSPARSE_VER_12_8_UP
298-
using cusparseSpMVOp_bufferSize_12_8_sig = cusparse_sig<cusparseHandle_t,
299-
cusparseOperation_t,
300-
cusparseConstSpMatDescr_t,
301-
cusparseConstDnVecDescr_t,
302-
cusparseDnVecDescr_t,
303-
cusparseDnVecDescr_t,
304-
cudaDataType,
305-
cusparseSpMVOpAlg_t,
306-
size_t*>;
307-
using cusparseSpMVOp_createDescr_12_8_sig = cusparse_sig<cusparseHandle_t,
308-
cusparseSpMVOpDescr_t*,
309-
cusparseOperation_t,
310-
cusparseConstSpMatDescr_t,
311-
cusparseConstDnVecDescr_t,
312-
cusparseDnVecDescr_t,
313-
cusparseDnVecDescr_t,
314-
cudaDataType,
315-
cusparseSpMVOpAlg_t,
316-
void*>;
317-
#endif // CUOPT_CUSPARSE_VER_12_8_UP
274+
// SpMVOp symbols. Resolved at runtime via dlsym, because the runtime minor version might not match
275+
// the compiled minor version. We can go back to direct linking once CUDA 14 is adopted
276+
using cusparseSpMVOp_destroyDescr_sig = cusparse_sig<cusparseSpMVOpDescr_t>;
277+
using cusparseSpMVOp_destroyPlan_sig = cusparse_sig<cusparseSpMVOpPlan_t>;
278+
using cusparseSpMVOp_bufferSize_sig = cusparse_sig<cusparseHandle_t,
279+
cusparseOperation_t,
280+
cusparseConstSpMatDescr_t,
281+
cusparseConstDnVecDescr_t,
282+
cusparseDnVecDescr_t,
283+
cusparseDnVecDescr_t,
284+
cudaDataType,
285+
cusparseSpMVOpAlg_t,
286+
size_t*>;
287+
using cusparseSpMVOp_createDescr_sig = cusparse_sig<cusparseHandle_t,
288+
cusparseSpMVOpDescr_t*,
289+
cusparseOperation_t,
290+
cusparseConstSpMatDescr_t,
291+
cusparseConstDnVecDescr_t,
292+
cusparseDnVecDescr_t,
293+
cusparseDnVecDescr_t,
294+
cudaDataType,
295+
cusparseSpMVOpAlg_t,
296+
void*>;
318297
using cusparseSpMVOp_createPlan_sig =
319298
cusparse_sig<cusparseHandle_t, cusparseSpMVOpDescr_t, cusparseSpMVOpPlan_t*, char*, size_t>;
320299
using cusparseSpMVOp_sig = cusparse_sig<cusparseHandle_t,
@@ -325,43 +304,6 @@ using cusparseSpMVOp_sig = cusparse_sig<cusparseHandle_t,
325304
cusparseDnVecDescr_t,
326305
cusparseDnVecDescr_t>;
327306

328-
namespace {
329-
330-
bool is_cusparse_runtime_12_8_or_newer()
331-
{
332-
// cuSPARSE 12.8 is the version shipped with CUDA Toolkit 13.3.
333-
int major = 0, minor = 0;
334-
auto status = cusparseGetProperty(libraryPropertyType_t::MAJOR_VERSION, &major);
335-
if (status != CUSPARSE_STATUS_SUCCESS) { return false; }
336-
status = cusparseGetProperty(libraryPropertyType_t::MINOR_VERSION, &minor);
337-
if (status != CUSPARSE_STATUS_SUCCESS) { return false; }
338-
return (major > 12) || (major == 12 && minor >= 8);
339-
}
340-
341-
cusparseStatus_t cusparse_spmvop_buffer_size(cusparseHandle_t handle,
342-
cusparseOperation_t opA,
343-
cusparseSpMatDescr_t matA,
344-
cusparseDnVecDescr_t vecX,
345-
cusparseDnVecDescr_t vecY,
346-
cusparseDnVecDescr_t vecZ,
347-
cudaDataType computeType,
348-
size_t* bufferSize)
349-
{
350-
#if CUOPT_CUSPARSE_VER_12_8_UP
351-
if (is_cusparse_runtime_12_8_or_newer()) {
352-
static const auto fn = dynamic_load_runtime::function<cusparseSpMVOp_bufferSize_12_8_sig>(
353-
"cusparseSpMVOp_bufferSize");
354-
return (*fn)(
355-
handle, opA, matA, vecX, vecY, vecZ, computeType, CUSPARSE_SPMVOP_ALG_DEFAULT, bufferSize);
356-
}
357-
#endif // CUOPT_CUSPARSE_VER_12_8_UP
358-
static const auto fn =
359-
dynamic_load_runtime::function<cusparseSpMVOp_bufferSize_12_7_sig>("cusparseSpMVOp_bufferSize");
360-
return (*fn)(handle, opA, matA, vecX, vecY, vecZ, computeType, bufferSize);
361-
}
362-
363-
} // namespace
364-
365307
cusparseStatus_t cusparse_spmvop_descr_wrapper_t::dlsym_create(cusparseHandle_t handle,
366308
cusparseSpMVOpDescr_t* descr,
367309
cusparseOperation_t opA,
@@ -372,17 +314,10 @@ cusparseStatus_t cusparse_spmvop_descr_wrapper_t::dlsym_create(cusparseHandle_t
372314
cudaDataType computeType,
373315
void* buffer)
374316
{
375-
#if CUOPT_CUSPARSE_VER_12_8_UP
376-
if (is_cusparse_runtime_12_8_or_newer()) {
377-
static const auto fn = dynamic_load_runtime::function<cusparseSpMVOp_createDescr_12_8_sig>(
378-
"cusparseSpMVOp_createDescr");
379-
return (*fn)(
380-
handle, descr, opA, matA, vecX, vecY, vecZ, computeType, CUSPARSE_SPMVOP_ALG_DEFAULT, buffer);
381-
}
382-
#endif // CUOPT_CUSPARSE_VER_12_8_UP
383-
static const auto fn = dynamic_load_runtime::function<cusparseSpMVOp_createDescr_12_7_sig>(
384-
"cusparseSpMVOp_createDescr");
385-
return (*fn)(handle, descr, opA, matA, vecX, vecY, vecZ, computeType, buffer);
317+
static const auto fn =
318+
dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>("cusparseSpMVOp_createDescr");
319+
return (*fn)(
320+
handle, descr, opA, matA, vecX, vecY, vecZ, computeType, CUSPARSE_SPMVOP_ALG_DEFAULT, buffer);
386321
}
387322

388323
cusparseStatus_t cusparse_spmvop_descr_wrapper_t::dlsym_destroy(cusparseSpMVOpDescr_t descr)
@@ -504,7 +439,7 @@ void cusparse_spmvop_run(cusparseHandle_t handle,
504439
RAFT_CUSPARSE_TRY(cusparseSetStream(handle, stream));
505440
RAFT_CUSPARSE_TRY((*func)(handle, plan, alpha, beta, vecX, vecY, vecZ));
506441
}
507-
#endif // CUOPT_CUSPARSE_VER_12_7_UP
442+
#endif // CUOPT_CUSPARSE_VER_12_8_UP
508443

509444
// This cstr is used in pdhg, step size strategy and in cuPDLPx infeasible detection
510445
// A_T is owned by the scaled problem
@@ -1428,38 +1363,36 @@ bool is_cusparse_runtime_mixed_precision_supported()
14281363

14291364
bool is_cusparse_runtime_spmvop_supported()
14301365
{
1431-
#if CUOPT_CUSPARSE_VER_12_7_UP
1432-
#if !CUOPT_CUSPARSE_VER_12_8_UP
1433-
// Headers older than cuSPARSE 12.8 (CUDA Toolkit 13.3) cannot name the newer SpMVOp
1434-
// descriptor types, so do not call the older 12.7 signature against a 12.8+ runtime.
1435-
if (is_cusparse_runtime_12_8_or_newer()) { return false; }
1436-
#endif // !CUOPT_CUSPARSE_VER_12_8_UP
1437-
// Probe the runtime to ensure cusparseSpMVOp is supported.
1366+
#if CUOPT_CUSPARSE_VER_12_8_UP
1367+
// Probe the runtimme to ensure cusparseSpMVOp is supported
14381368
static const bool supported =
14391369
dynamic_load_runtime::function<cusparseSpMVOp_sig>("cusparseSpMVOp").has_value();
14401370
return supported;
14411371
#else
14421372
return false;
1443-
#endif // CUOPT_CUSPARSE_VER_12_7_UP
1373+
#endif // CUOPT_CUSPARSE_VER_12_8_UP
14441374
}
14451375

14461376
// Creates SpMVOp plans. Must be called after scale_problem() so plans use the scaled matrix.
14471377
template <typename i_t, typename f_t>
14481378
void cusparse_view_t<i_t, f_t>::create_spmv_op_plans(bool is_reflected)
14491379
{
1450-
#if CUOPT_CUSPARSE_VER_12_7_UP
1380+
#if CUOPT_CUSPARSE_VER_12_8_UP
14511381
if (!is_cusparse_runtime_spmvop_supported() || !(std::is_same_v<f_t, double>)) { return; }
1382+
static const auto buffer_size =
1383+
dynamic_load_runtime::function<cusparseSpMVOp_bufferSize_sig>("cusparseSpMVOp_bufferSize");
14521384
CUSPARSE_CHECK(cusparseSetStream(handle_ptr_->get_cusparse_handle(), handle_ptr_->get_stream()));
14531385
// Prepare buffers for At_y SpMVOp
14541386
size_t buffer_size_transpose = 0;
1455-
RAFT_CUSPARSE_TRY(cusparse_spmvop_buffer_size(handle_ptr_->get_cusparse_handle(),
1456-
CUSPARSE_OPERATION_NON_TRANSPOSE,
1457-
A_T,
1458-
dual_solution,
1459-
current_AtY,
1460-
current_AtY,
1461-
CUDA_R_64F,
1462-
&buffer_size_transpose));
1387+
RAFT_CUSPARSE_TRY((*buffer_size)(handle_ptr_->get_cusparse_handle(),
1388+
CUSPARSE_OPERATION_NON_TRANSPOSE,
1389+
A_T,
1390+
dual_solution,
1391+
current_AtY,
1392+
current_AtY,
1393+
CUDA_R_64F,
1394+
CUSPARSE_SPMVOP_ALG_DEFAULT,
1395+
&buffer_size_transpose));
14631396
buffer_transpose_spmvop.resize(buffer_size_transpose, handle_ptr_->get_stream());
14641397

14651398
spmv_op_descr_A_t_.create(handle_ptr_->get_cusparse_handle(),
@@ -1476,14 +1409,15 @@ void cusparse_view_t<i_t, f_t>::create_spmv_op_plans(bool is_reflected)
14761409
// Only prepare buffers for A_x if we are using reflected_halpern
14771410
if (is_reflected) {
14781411
size_t buffer_size_non_transpose = 0;
1479-
RAFT_CUSPARSE_TRY(cusparse_spmvop_buffer_size(handle_ptr_->get_cusparse_handle(),
1480-
CUSPARSE_OPERATION_NON_TRANSPOSE,
1481-
A,
1482-
reflected_primal_solution,
1483-
dual_gradient,
1484-
dual_gradient,
1485-
CUDA_R_64F,
1486-
&buffer_size_non_transpose));
1412+
RAFT_CUSPARSE_TRY((*buffer_size)(handle_ptr_->get_cusparse_handle(),
1413+
CUSPARSE_OPERATION_NON_TRANSPOSE,
1414+
A,
1415+
reflected_primal_solution,
1416+
dual_gradient,
1417+
dual_gradient,
1418+
CUDA_R_64F,
1419+
CUSPARSE_SPMVOP_ALG_DEFAULT,
1420+
&buffer_size_non_transpose));
14871421
buffer_non_transpose_spmvop.resize(buffer_size_non_transpose, handle_ptr_->get_stream());
14881422

14891423
spmv_op_descr_A_.create(handle_ptr_->get_cusparse_handle(),
@@ -1497,7 +1431,7 @@ void cusparse_view_t<i_t, f_t>::create_spmv_op_plans(bool is_reflected)
14971431

14981432
spmv_op_plan_A_.create(handle_ptr_->get_cusparse_handle(), spmv_op_descr_A_);
14991433
}
1500-
#endif // CUOPT_CUSPARSE_VER_12_7_UP
1434+
#endif // CUOPT_CUSPARSE_VER_12_8_UP
15011435
}
15021436

15031437
#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT

cpp/src/pdlp/cusparse_view.hpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
#include <cusparse_v2.h>
2222

23-
// cuSPARSE 12.7 ships with CUDA Toolkit 13.2
24-
#define CUOPT_CUSPARSE_VER_12_7_UP (CUSPARSE_VERSION >= 12700)
2523
// cuSPARSE 12.8 ships with CUDA Toolkit 13.3
2624
#define CUOPT_CUSPARSE_VER_12_8_UP (CUSPARSE_VERSION >= 12800)
2725

@@ -84,7 +82,7 @@ class cusparse_dn_mat_descr_wrapper_t {
8482
bool need_destruction_;
8583
};
8684

87-
#if CUOPT_CUSPARSE_VER_12_7_UP
85+
#if CUOPT_CUSPARSE_VER_12_8_UP
8886
// RAII wrapper around cusparse SpMVOp objects. All the buffers are owned by the cusparse_view_t.
8987
class cusparse_spmvop_descr_wrapper_t {
9088
public:
@@ -152,7 +150,7 @@ class cusparse_spmvop_plan_wrapper_t {
152150
cusparseSpMVOpPlan_t plan_;
153151
bool need_destruction_;
154152
};
155-
#endif // CUOPT_CUSPARSE_VER_12_7_UP
153+
#endif // CUOPT_CUSPARSE_VER_12_8_UP
156154

157155
template <typename i_t, typename f_t>
158156
class cusparse_view_t {
@@ -251,13 +249,13 @@ class cusparse_view_t {
251249
rmm::device_uvector<uint8_t> buffer_non_transpose_spmvop{0, handle_ptr_->get_stream()};
252250
rmm::device_uvector<uint8_t> buffer_transpose_spmvop{0, handle_ptr_->get_stream()};
253251

254-
#if CUOPT_CUSPARSE_VER_12_7_UP
252+
#if CUOPT_CUSPARSE_VER_12_8_UP
255253
// SpMVOp descriptors and plans for A and A_T (descr before plan so dtor destroys plan first)
256254
cusparse_spmvop_descr_wrapper_t spmv_op_descr_A_;
257255
cusparse_spmvop_plan_wrapper_t spmv_op_plan_A_;
258256
cusparse_spmvop_descr_wrapper_t spmv_op_descr_A_t_;
259257
cusparse_spmvop_plan_wrapper_t spmv_op_plan_A_t_;
260-
#endif // CUOPT_CUSPARSE_VER_12_7_UP
258+
#endif // CUOPT_CUSPARSE_VER_12_8_UP
261259
// reuse buffers for cusparse spmm
262260
rmm::device_uvector<uint8_t> buffer_transpose_batch;
263261
rmm::device_uvector<uint8_t> buffer_non_transpose_batch;
@@ -356,10 +354,11 @@ void my_cusparsespmm_preprocess(cusparseHandle_t handle,
356354

357355
bool is_cusparse_runtime_mixed_precision_supported();
358356

359-
// False if the cuSPARSE headers/runtime do not support SpMVOp symbols. True otherwise.
357+
// False if cuSparse version < 12.8 or runtime cuSPARSE does not export SpMVOp symbols. True
358+
// otherwise.
360359
bool is_cusparse_runtime_spmvop_supported();
361360

362-
#if CUOPT_CUSPARSE_VER_12_7_UP
361+
#if CUOPT_CUSPARSE_VER_12_8_UP
363362
// Dispatches to the runtime cusparseSpMVOp via dlsym so callers (e.g., pdhg.cu) never
364363
// reference the symbol statically. Caller must have verified
365364
// is_cusparse_runtime_spmvop_supported().
@@ -371,6 +370,6 @@ void cusparse_spmvop_run(cusparseHandle_t handle,
371370
cusparseDnVecDescr_t vecY,
372371
cusparseDnVecDescr_t vecZ,
373372
cudaStream_t stream);
374-
#endif // CUOPT_CUSPARSE_VER_12_7_UP
373+
#endif // CUOPT_CUSPARSE_VER_12_8_UP
375374

376375
} // namespace cuopt::mathematical_optimization::pdlp

cpp/src/pdlp/pdhg.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ void pdhg_solver_t<i_t, f_t>::compute_next_dual_solution(rmm::device_uvector<f_t
447447
template <typename i_t, typename f_t>
448448
void pdhg_solver_t<i_t, f_t>::spmvop_At_y()
449449
{
450-
#if CUDA_VER_13_2_UP
450+
#if CUOPT_CUSPARSE_VER_12_8_UP
451451
if (is_cusparse_runtime_spmvop_supported()) {
452452
cusparse_spmvop_run(handle_ptr_->get_cusparse_handle(),
453453
cusparse_view_.spmv_op_plan_A_t_,
@@ -475,7 +475,7 @@ void pdhg_solver_t<i_t, f_t>::spmvop_At_y()
475475
template <typename i_t, typename f_t>
476476
void pdhg_solver_t<i_t, f_t>::spmvop_A_x()
477477
{
478-
#if CUDA_VER_13_2_UP
478+
#if CUOPT_CUSPARSE_VER_12_8_UP
479479
if (is_cusparse_runtime_spmvop_supported()) {
480480
cusparse_spmvop_run(handle_ptr_->get_cusparse_handle(),
481481
cusparse_view_.spmv_op_plan_A_,

0 commit comments

Comments
 (0)