-
Notifications
You must be signed in to change notification settings - Fork 8
Add matrix_opt into oneMKL_SYCL backend #39
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
Conversation
|
@BenBrock any thoughts about integrating the matrix_opt into our solutions now that we have CSC and transpose objects merged to main branch ? I seem to recall there were some open questions we had discussed a while ago:
|
BenBrock
left a comment
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.
Hi @spencerpatty—I've merged this PR with main to get it up-to-date and then made a couple of small tweaks. (Actually some of the changes are to pre-existing code to make it more consistent.)
The main change is switching to get_matrix_handle, which will handle (no pun intended) the cases where you do and don't have a matrix_opt.
I think the design as-is is pretty reasonable.
The one thing I might consider changing is how we store the matrix handles inside of operation_state. Currently, we store a nullptr handle if there is a matrix_opt and a non-nullptr handle if there isn't. We could update this to store a wrapper object inside the operation_state. The wrapper object could then keep track of whether it ought to delete itself or not, which would simplify things considerably.
Still, I think this is fine as-is. I'm approving this and will send you a review request right back to review my changes/the current state.
| O* c_rowptr = (O*) info.state_.c_rowptr; | ||
|
|
||
| I* c_rowptr = (I*) info.state_.c_rowptr; | ||
| auto a_handle = __mkl::get_matrix_handle(q, a, info.state_.a_handle); |
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.
This is the main update I made—instead of calling get_ultimate_base, I re-worked get_matrix_handle to work directly on a stack of views. It will iterate through and grab the matrix_opt if available, or else create a new view.
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.
aha, I see. And it is an MKL specific api (instead of a general one) since we are returning an MKL specific object... this is good. Others can add their handlers as well for their objects.
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.
so in here, I guess we don't ever interact with the ultimate_base of a or b, but rather we always get an mkl matrix handle from a or info.state including extracting the xyz_view and creating a matrix_handle from it, in thus get_matrix_handle ...
| index<>{__backend::shape(c)[0], __backend::shape(c)[1]}, nnz, | ||
| __mkl::operation_state_t{a_handle, b_handle, c_handle, nullptr, descr, | ||
| (void*) c_rowptr, q}}; | ||
| __mkl::operation_state_t{__detail::has_matrix_opt(a) ? nullptr : a_handle, |
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 function has_matrix_opt will return whether or not a stack of views has a matrix_opt, which you can then use to handle those two cases.
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.
nice :)
|
@spencerpatty GitHub won't allow me to request a review, so please take a look and let me know what you think. |
| if constexpr (has_base<T>) { | ||
| if constexpr (is_matrix_opt_view_v<T>) { | ||
| return t; | ||
| } else { | ||
| return t; | ||
| return get_ultimate_base(t.base()); | ||
| } | ||
| } else { | ||
| return t; | ||
| } |
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.
so get_ultimate_base still returns not the ultimate base but either a matrix_opt or the bottom xyz_view now ? do we want to keep that as is ? or should we return it back to ignoring matrix_opt_view_v ?
|
|
||
| template <typename M> | ||
| requires(__detail::is_matrix_instantiation_of_mdspan_v<M>) | ||
| requires(__detail::is_matrix_mdspan_v<M>) |
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.
nice, I like this shortened names
| concept matrix = __detail::is_csr_view_v<M> || __detail::is_csc_view_v<M> || | ||
| __detail::is_matrix_mdspan_v<M> || __detail::matrix<M>; |
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 am guessing the "is_xyz_view_v" look through the matrix_opt to see the xyz_view underneath so a matrix_opt would also be classified properly here ...
| template <tensor T> | ||
| bool has_matrix_opt(T&& t) { | ||
| if constexpr (is_matrix_opt_v<T>) { | ||
| return true; | ||
| } else if constexpr (has_base<T>) { | ||
| return has_matrix_opt(t.base()); | ||
| } else { | ||
| return false; | ||
| } | ||
| } | ||
|
|
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.
nice checker
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.
aha, get_ultimate_base was returned to it's previous state and we use has_matrix_opt() and the custom __mkl::get_matrix_handle() to get matrix handle now, instead of having ultimate_base return it . I like this much more. Good changes!
|
|
||
| return m.matrix_handle_; | ||
| } else if constexpr (__detail::has_base<M>) { | ||
| return get_matrix_handle(q, m.base(), handle); |
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.
oh, so line 29 and 30 will iterate through the stack and keep looking until we either find a matrix_handle, or we run out of bases and check either handle or create_matrix_handle on ultimate base of m ...
| auto base() { | ||
| return matrix_; | ||
| } | ||
|
|
||
| auto base() const { | ||
| return matrix_; | ||
| } |
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 both const (accessor) and nonconst(mutator) functions for base() ? can we really guarantee that the accessor won't modify matrix_ ?
| if (!__detail::has_matrix_opt(a)) { | ||
| oneapi::mkl::sparse::release_matrix_handle(q, &a_handle).wait(); | ||
| } |
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.
aha, this is good to enable a_handle to persist in matrix_opt ... good
| if (!__detail::has_matrix_opt(a)) { | ||
| oneapi::mkl::sparse::release_matrix_handle(q, &a_handle).wait(); | ||
| } |
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.
so we turned on matrix_opt for spmv/spmm and spgemm ... but it looks like there is still some work to add it to triangular_solve. @BenBrock do you have opinion about whether we do that in a separate PR or in this one ?
spencerpatty
left a comment
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.
As this was originally my PR, I am not able to approve it, but I have reviewed the changes that @BenBrock made and approve of the PR now for merge. (There may be some thigns listed below that we want to add to this PR, or it is stable right now, so is fine to merge)
Summary: We add in the matrix_opt handling to spmv/spmm/spgemm, though it is not in TRSV yet. We added a new has_matrix_opt() function and a custom _mkl::get_matrix_handle() in these functions to isolate the interaction between matrix M and getting a vendor matrix object for calling the vendor APIs.
TODOs:
- add matrix_opt support to triangular_solve
- Add use of matrix_opt into examples or testing
|
@spencerpatty Good point about testing and I want to implement some more comprehensive testing that will test with every possible combination of arguments (raw view, scaled, matrix_opt, scaled matrix_opt, etc.), but I think that will have to wait. We already have one SpMM test that uses matrix_opt, so I'll add an SpGEMM one and merge for now. |
BenBrock
left a comment
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.
Approving to merge based on @spencerpatty's comments.
I am concerned about the operation_state_t complexity with regards to SpGEMM C matrix_handle_t is stored in operation_state_t that keeps the continuity between compute/fill. There is a matrix_opt restriction on C right now, but it seems it would be important to let C be a matrix_opt, that way it isn't stored in the operation_state_t indefinitely... lifetime is complicated