Replaced cusparse wrappers with simple unique_ptrs and more RAII #1342
Replaced cusparse wrappers with simple unique_ptrs and more RAII #1342Bubullzz wants to merge 2 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughRefactors cuSPARSE descriptor lifetime handling: replaces custom wrapper types with owning RAII unique_ptr aliases and non-owning descriptor views, adds factory helpers (make_csr/make_dnvec/make_dnmat and SpMVOp factories), and updates constructors and all cuSPARSE call sites across barrier and PDLP to use .get()/view parameters. ChangescuSPARSE Descriptor RAII Migration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cpp/src/pdlp/cusparse_view.cu (1)
240-242:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMissing
has_value()check before dereferencing optional.
funcat line 242 is dereferenced without checking if the dlsym lookup succeeded.Proposed fix
void cusparse_spmvop_run(cusparseHandle_t handle, cusparseSpMVOpPlan_t plan, const void* alpha, const void* beta, cusparse_dn_vec_descr_view vecX, cusparse_dn_vec_descr_view vecY, cusparse_dn_vec_descr_view vecZ, cudaStream_t stream) { static const auto func = dynamic_load_runtime::function<cusparseSpMVOp_sig>("cusparseSpMVOp"); + cuopt_expects(func.has_value(), "cusparseSpMVOp symbol not found at runtime"); RAFT_CUSPARSE_TRY(cusparseSetStream(handle, stream)); RAFT_CUSPARSE_TRY((*func)(handle, plan, alpha, beta, vecX, vecY, vecZ)); }🤖 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/src/pdlp/cusparse_view.cu` around lines 240 - 242, The code dereferences the optional dynamic_load_runtime::function<cusparseSpMVOp_sig> named func without checking it; before calling (*func)(handle, plan, alpha, beta, vecX, vecY, vecZ) add a check like if (!func.has_value()) and handle the failure (log or return an error/throw) with a clear message including the symbol name "cusparseSpMVOp"; keep the existing RAFT_CUSPARSE_TRY usage for actual cuSPARSE calls and ensure the early error path prevents the dereference of func and returns/propagates an appropriate error.
🧹 Nitpick comments (2)
cpp/src/pdlp/cusparse_view.hpp (1)
38-57: 💤 Low valueConsider
_tsuffix for deleter types per project naming conventions.The coding guidelines specify types/structs should use
snake_case_twith_tsuffix (e.g.,cusparse_sp_mat_deleter_t). However, the current naming follows common STL deleter patterns, so this is a stylistic choice.🤖 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/src/pdlp/cusparse_view.hpp` around lines 38 - 57, The structs cusparse_sp_mat_deleter, cusparse_dn_vec_deleter, and cusparse_dn_mat_deleter do not follow the project naming convention requiring a snake_case_t suffix; rename them to cusparse_sp_mat_deleter_t, cusparse_dn_vec_deleter_t, and cusparse_dn_mat_deleter_t respectively, update all uses/typedefs/usings in this compilation unit and any headers that reference these types (e.g., unique_ptr deleter specializations or variable declarations), and ensure the operator() implementations remain unchanged and still call RAFT_CUSPARSE_TRY_NO_THROW on cusparseDestroySpMat/cusparseDestroyDnVec/cusparseDestroyDnMat.cpp/src/pdlp/cusparse_view.cu (1)
147-149: 💤 Low valueInconsistent error-checking macro.
Line 147 uses
CUSPARSE_CHECKwhile other cuSPARSE calls in this file useRAFT_CUSPARSE_TRY. Consider usingRAFT_CUSPARSE_TRYfor consistency.🤖 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/src/pdlp/cusparse_view.cu` around lines 147 - 149, Replace the inconsistent CUSPARSE_CHECK call with the RAFT_CUSPARSE_TRY macro to match the rest of the file: change the call to cusparseSetStream(...) so it is wrapped with RAFT_CUSPARSE_TRY rather than CUSPARSE_CHECK, keeping the same arguments and preserving the subsequent RAFT_CUSPARSE_TRY(cusparseSpMM_preprocess(...)) call; ensure you reference and use the RAFT_CUSPARSE_TRY macro for both cusparseSetStream and cusparseSpMM_preprocess to maintain consistent error handling.
🤖 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/src/pdlp/cusparse_view.cu`:
- Around line 213-218: The code dereferences the optional dynamic loader result
fn (dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>) without
checking presence; update the factory that creates the cusparseSpMVOp_descr (the
block that calls (*fn)(...)) to first test fn.has_value() (or if(!fn) branch)
and handle the missing symbol by returning an empty cusparse_spmvop_descr_uptr
(or otherwise propagate a clear error) instead of dereferencing; ensure the
handling is applied where cusparseSpMVOp_createDescr is invoked so callers of
is_cusparse_runtime_spmvop_supported() are not assumed sufficient.
- Around line 221-228: The dynamic loader result `fn` in make_spmvop_plan is
dereferenced without checking it exists; update make_spmvop_plan to test
dynamic_load_runtime::function<...> fn for presence (e.g., if (!fn) throw or
return an error) before calling (*fn)(...), mirroring the fix used in
make_spmvop_descr so that cusparseSpMVOp_createPlan is only invoked when the
symbol lookup succeeded and RAFT_CUSPARSE_TRY is reached with a valid function
pointer.
---
Outside diff comments:
In `@cpp/src/pdlp/cusparse_view.cu`:
- Around line 240-242: The code dereferences the optional
dynamic_load_runtime::function<cusparseSpMVOp_sig> named func without checking
it; before calling (*func)(handle, plan, alpha, beta, vecX, vecY, vecZ) add a
check like if (!func.has_value()) and handle the failure (log or return an
error/throw) with a clear message including the symbol name "cusparseSpMVOp";
keep the existing RAFT_CUSPARSE_TRY usage for actual cuSPARSE calls and ensure
the early error path prevents the dereference of func and returns/propagates an
appropriate error.
---
Nitpick comments:
In `@cpp/src/pdlp/cusparse_view.cu`:
- Around line 147-149: Replace the inconsistent CUSPARSE_CHECK call with the
RAFT_CUSPARSE_TRY macro to match the rest of the file: change the call to
cusparseSetStream(...) so it is wrapped with RAFT_CUSPARSE_TRY rather than
CUSPARSE_CHECK, keeping the same arguments and preserving the subsequent
RAFT_CUSPARSE_TRY(cusparseSpMM_preprocess(...)) call; ensure you reference and
use the RAFT_CUSPARSE_TRY macro for both cusparseSetStream and
cusparseSpMM_preprocess to maintain consistent error handling.
In `@cpp/src/pdlp/cusparse_view.hpp`:
- Around line 38-57: The structs cusparse_sp_mat_deleter,
cusparse_dn_vec_deleter, and cusparse_dn_mat_deleter do not follow the project
naming convention requiring a snake_case_t suffix; rename them to
cusparse_sp_mat_deleter_t, cusparse_dn_vec_deleter_t, and
cusparse_dn_mat_deleter_t respectively, update all uses/typedefs/usings in this
compilation unit and any headers that reference these types (e.g., unique_ptr
deleter specializations or variable declarations), and ensure the operator()
implementations remain unchanged and still call RAFT_CUSPARSE_TRY_NO_THROW on
cusparseDestroySpMat/cusparseDestroyDnVec/cusparseDestroyDnMat.
🪄 Autofix (Beta)
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: 43105512-0a29-4bb5-b0f5-b5cd32da0e79
📒 Files selected for processing (5)
cpp/src/barrier/barrier.cucpp/src/barrier/cusparse_info.hppcpp/src/barrier/cusparse_view.cucpp/src/pdlp/cusparse_view.cucpp/src/pdlp/cusparse_view.hpp
| static const auto fn = | ||
| dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>("cusparseSpMVOp_createDescr"); | ||
| cusparseSpMVOpDescr_t descr{nullptr}; | ||
| RAFT_CUSPARSE_TRY( | ||
| dlsym_create(handle, &descr_, opA, matA, vecX, vecY, vecZ, computeType, buffer.data())); | ||
| need_destruction_ = true; | ||
| } | ||
|
|
||
| cusparse_spmvop_descr_wrapper_t::operator cusparseSpMVOpDescr_t() const { return descr_; } | ||
|
|
||
| cusparse_spmvop_plan_wrapper_t::cusparse_spmvop_plan_wrapper_t() | ||
| : plan_(nullptr), need_destruction_(false) | ||
| { | ||
| } | ||
|
|
||
| cusparse_spmvop_plan_wrapper_t::~cusparse_spmvop_plan_wrapper_t() | ||
| { | ||
| if (!need_destruction_) { return; } | ||
| RAFT_CUSPARSE_TRY_NO_THROW(dlsym_destroy(plan_)); | ||
| } | ||
|
|
||
| cusparse_spmvop_plan_wrapper_t::cusparse_spmvop_plan_wrapper_t( | ||
| const cusparse_spmvop_plan_wrapper_t& other) | ||
| : plan_(other.plan_), need_destruction_(false) | ||
| { | ||
| } | ||
|
|
||
| cusparse_spmvop_plan_wrapper_t& cusparse_spmvop_plan_wrapper_t::operator=( | ||
| cusparse_spmvop_plan_wrapper_t&& other) | ||
| { | ||
| if (need_destruction_) { RAFT_CUSPARSE_TRY(dlsym_destroy(plan_)); } | ||
| plan_ = other.plan_; | ||
| need_destruction_ = other.need_destruction_; | ||
| other.need_destruction_ = false; | ||
| return *this; | ||
| (*fn)(handle, &descr, opA, matA, vecX, vecY, vecZ, computeType, buffer.data())); | ||
| return cusparse_spmvop_descr_uptr{descr}; |
There was a problem hiding this comment.
Missing has_value() check before dereferencing optional.
fn is a std::optional that could be nullopt if the dlsym lookup fails. Dereferencing it at line 217 without checking has_value() would cause undefined behavior. While is_cusparse_runtime_spmvop_supported() probes for cusparseSpMVOp, it doesn't verify that cusparseSpMVOp_createDescr exists.
Proposed fix
cusparse_spmvop_descr_uptr make_spmvop_descr(cusparseHandle_t handle,
cusparseOperation_t opA,
cusparse_sp_mat_descr_view matA,
cusparse_dn_vec_descr_view vecX,
cusparse_dn_vec_descr_view vecY,
cusparse_dn_vec_descr_view vecZ,
cudaDataType computeType,
rmm::device_uvector<uint8_t>& buffer)
{
static const auto fn =
dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>("cusparseSpMVOp_createDescr");
+ cuopt_expects(fn.has_value(), "cusparseSpMVOp_createDescr symbol not found at runtime");
cusparseSpMVOpDescr_t descr{nullptr};
RAFT_CUSPARSE_TRY(
(*fn)(handle, &descr, opA, matA, vecX, vecY, vecZ, computeType, buffer.data()));
return cusparse_spmvop_descr_uptr{descr};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| static const auto fn = | |
| dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>("cusparseSpMVOp_createDescr"); | |
| cusparseSpMVOpDescr_t descr{nullptr}; | |
| RAFT_CUSPARSE_TRY( | |
| dlsym_create(handle, &descr_, opA, matA, vecX, vecY, vecZ, computeType, buffer.data())); | |
| need_destruction_ = true; | |
| } | |
| cusparse_spmvop_descr_wrapper_t::operator cusparseSpMVOpDescr_t() const { return descr_; } | |
| cusparse_spmvop_plan_wrapper_t::cusparse_spmvop_plan_wrapper_t() | |
| : plan_(nullptr), need_destruction_(false) | |
| { | |
| } | |
| cusparse_spmvop_plan_wrapper_t::~cusparse_spmvop_plan_wrapper_t() | |
| { | |
| if (!need_destruction_) { return; } | |
| RAFT_CUSPARSE_TRY_NO_THROW(dlsym_destroy(plan_)); | |
| } | |
| cusparse_spmvop_plan_wrapper_t::cusparse_spmvop_plan_wrapper_t( | |
| const cusparse_spmvop_plan_wrapper_t& other) | |
| : plan_(other.plan_), need_destruction_(false) | |
| { | |
| } | |
| cusparse_spmvop_plan_wrapper_t& cusparse_spmvop_plan_wrapper_t::operator=( | |
| cusparse_spmvop_plan_wrapper_t&& other) | |
| { | |
| if (need_destruction_) { RAFT_CUSPARSE_TRY(dlsym_destroy(plan_)); } | |
| plan_ = other.plan_; | |
| need_destruction_ = other.need_destruction_; | |
| other.need_destruction_ = false; | |
| return *this; | |
| (*fn)(handle, &descr, opA, matA, vecX, vecY, vecZ, computeType, buffer.data())); | |
| return cusparse_spmvop_descr_uptr{descr}; | |
| static const auto fn = | |
| dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>("cusparseSpMVOp_createDescr"); | |
| cuopt_expects(fn.has_value(), "cusparseSpMVOp_createDescr symbol not found at runtime"); | |
| cusparseSpMVOpDescr_t descr{nullptr}; | |
| RAFT_CUSPARSE_TRY( | |
| (*fn)(handle, &descr, opA, matA, vecX, vecY, vecZ, computeType, buffer.data())); | |
| return cusparse_spmvop_descr_uptr{descr}; |
🤖 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/src/pdlp/cusparse_view.cu` around lines 213 - 218, The code dereferences
the optional dynamic loader result fn
(dynamic_load_runtime::function<cusparseSpMVOp_createDescr_sig>) without
checking presence; update the factory that creates the cusparseSpMVOp_descr (the
block that calls (*fn)(...)) to first test fn.has_value() (or if(!fn) branch)
and handle the missing symbol by returning an empty cusparse_spmvop_descr_uptr
(or otherwise propagate a clear error) instead of dereferencing; ensure the
handling is applied where cusparseSpMVOp_createDescr is invoked so callers of
is_cusparse_runtime_spmvop_supported() are not assumed sufficient.
| cusparse_spmvop_plan_uptr make_spmvop_plan(cusparseHandle_t handle, cusparseSpMVOpDescr_t descr) | ||
| { | ||
| if (need_destruction_) { RAFT_CUSPARSE_TRY(dlsym_destroy(plan_)); } | ||
| static const auto fn = | ||
| dynamic_load_runtime::function<cusparseSpMVOp_createPlan_sig>("cusparseSpMVOp_createPlan"); | ||
| cusparseSpMVOpPlan_t plan{nullptr}; | ||
| // cuOpt does not supply user-provided LTO IR; pass nullptr/0 so cuSPARSE JITs internally. | ||
| RAFT_CUSPARSE_TRY(dlsym_create(handle, descr, &plan_, /*ltoIRBuf=*/nullptr, /*ltoIRSize=*/0)); | ||
| need_destruction_ = true; | ||
| RAFT_CUSPARSE_TRY((*fn)(handle, descr, &plan, /*ltoIRBuf=*/nullptr, /*ltoIRSize=*/0)); | ||
| return cusparse_spmvop_plan_uptr{plan}; |
There was a problem hiding this comment.
Missing has_value() check before dereferencing optional.
Same issue as make_spmvop_descr — line 227 dereferences fn without verifying the dlsym lookup succeeded.
Proposed fix
cusparse_spmvop_plan_uptr make_spmvop_plan(cusparseHandle_t handle, cusparseSpMVOpDescr_t descr)
{
static const auto fn =
dynamic_load_runtime::function<cusparseSpMVOp_createPlan_sig>("cusparseSpMVOp_createPlan");
+ cuopt_expects(fn.has_value(), "cusparseSpMVOp_createPlan symbol not found at runtime");
cusparseSpMVOpPlan_t plan{nullptr};
// cuOpt does not supply user-provided LTO IR; pass nullptr/0 so cuSPARSE JITs internally.
RAFT_CUSPARSE_TRY((*fn)(handle, descr, &plan, /*ltoIRBuf=*/nullptr, /*ltoIRSize=*/0));
return cusparse_spmvop_plan_uptr{plan};
}🤖 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/src/pdlp/cusparse_view.cu` around lines 221 - 228, The dynamic loader
result `fn` in make_spmvop_plan is dereferenced without checking it exists;
update make_spmvop_plan to test dynamic_load_runtime::function<...> fn for
presence (e.g., if (!fn) throw or return an error) before calling (*fn)(...),
mirroring the fix used in make_spmvop_descr so that cusparseSpMVOp_createPlan is
only invoked when the symbol lookup succeeded and RAFT_CUSPARSE_TRY is reached
with a valid function pointer.
I love the clean up effort! Leaving the review for the experts. |
The idea is to replace the big handmade wrappers for cusparse objects with more robust std::unique_ptr
Trying to clean the code a bit as a side task while waiting for other jobs to finish. This is a subset of the full PR to get feedback from the team before going forward.