-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Sparse] Support Diag sparse format in C++ #5432
Conversation
To trigger regression tests:
|
@@ -90,6 +95,21 @@ std::shared_ptr<CSR> COOToCSC(const std::shared_ptr<COO>& coo); | |||
/** @brief Convert a CSR format to CSC format. */ | |||
std::shared_ptr<CSR> CSRToCSC(const std::shared_ptr<CSR>& csr); | |||
|
|||
/** @brief Convert a Diag format to COO format. */ | |||
std::shared_ptr<COO> DiagToCOO( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this 3 conversion? In which case, they will be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are used for operators that do not have implementation on Diag format, e.g., SpMM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the implication of the performance here if we convert diag to COO/CSR/CSV for operators?
Will it strongly decrease the spmm performance?
This solution looks good to me, but let's make sure we understand the trade-off we are making here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simply follow our current implementation to ensure no performance regression. Currently, we also convert the DiagMatrix to SparseMatrix for SpMM on the Python side.
if (A->HasDiag() && B->HasDiag()) { | ||
return SparseMatrix::FromDiagPointer( | ||
A->DiagPtr(), A->value() + B->value(), A->shape()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I will usually prefer parallel if-else branch, e.g.,
if (...) {
...
} else {
...
}
@frozenbugs what would you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation is better.
For all functions, it could have multiple early return checks, so it is highly recommended to do:
if (...) {
return ...
}
if (...) {
return ...
}
blablabla
blablabla
blablabla
return ...
dgl_sparse/src/sparse_format.cc
Outdated
const std::shared_ptr<Diag>& diag, | ||
const c10::TensorOptions& indices_options) { | ||
int64_t nnz = std::min(diag->num_rows, diag->num_cols); | ||
auto indptr = torch::arange(nnz + 1, indices_options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will using the inplace operation torch::arange_out
be cleaner? This way you can first create an array of length diag->num_rows+1
with all values being nnz
, then fill the front part with arange. This also avoids creating multiple intermediate buffers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
dgl_sparse/src/spspmm.cc
Outdated
const c10::intrusive_ptr<SparseMatrix>& lhs_mat, | ||
const c10::intrusive_ptr<SparseMatrix>& rhs_mat) { | ||
if (lhs_mat->HasDiag()) { | ||
if (rhs_mat->HasDiag()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I usually don't like nested if as it is difficult to read. I recommend change it to:
if (lhs_mat->HasDiag() && rhs_mat->HasDiag()) {
...
} else if (lhs_mat->HasDiag() && !rhs_mat->HasDiag()) {
...
} else {
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
dgl_sparse/src/spspmm.cc
Outdated
// Diag @ Sparse | ||
auto row = rhs_mat->Indices().index({0}); | ||
auto val = lhs_mat->value().index_select(0, row) * rhs_mat->value(); | ||
return SparseMatrix::ValLike(rhs_mat, val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this implementation correct? The index_select should use A.row
according to /~https://github.com/dmlc/dgl/blob/master/python/dgl/sparse/matmul.py#L172.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. rhs_mat->Indices().index({0})
returns the row coordinates of the SparseMatrix without data copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. rhs_mat->Indices()
returns the COO index. How is it different with rhs_mat->COOPointer()->indices
? Do we want to have two interfaces?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are the same. But we need Indices()
exposed to python to avoid data copy of the COO tensor.
* [Sparse] Support Diag sparse format in C++ * update * Update
* [Sparse] Support Diag sparse format in C++ * update * Update
Description
This is one of the PR for the issue #5367 .
Checklist
Please feel free to remove inapplicable items for your PR.
Changes