Skip to content

Batched SVD support for ROCSOLVER and CUSOLVER - #275

Open
kshyatt wants to merge 46 commits into
mainfrom
ksh/batched_svd
Open

kshyatt wants to merge 46 commits into
mainfrom
ksh/batched_svd

Conversation

@kshyatt

@kshyatt kshyatt commented Aug 21, 2026

Copy link
Copy Markdown
Member

Basically what it says on the tin. For CTMRG and other algorithms, we're getting absolutely slaughtered on GPU performance for TensorMaps with sectors because we have to spin up huge numbers of very small SVDs. I'm wrapping the batched SVDs each library provides to try to address this. Extremely open to comments but I wanted to get this rolling so I can unblock others.

@kshyatt
kshyatt requested review from Jutho and lkdvos August 21, 2026 14:52
@kshyatt

kshyatt commented Aug 21, 2026 •

Copy link
Copy Markdown
Member Author

TODOs here:

  • Finish wrapping the BisectionBatched logic for AMD
  • Add the checks for CUSOLVER gesvdj_batched (blocks may not be larger than 32 x 32)
  • Finish the tests for svd_trunc!
  • Pullback/pushforward rules

Since I'll be out for 3 weeks everyone should feel free to just push to this.

@lkdvos lkdvos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks great, I think the main thing I am wondering about is about the interface decision around wether we implement this as a set of BatchedAlg versions, or rather as a set of batched_f(...) functions. I definitely like using dispatch for switching between the strided and non-strided inputs, but I am wondering if there might be a benefit to really having a batched_svd_compact etc function.

This is also partially since that allows us to have a CPU version for this as well, so we can just offload all of this from TensorKit to here, (and possibly play with multithreading?).

Comment thread test/testsuite/decompositions/svd.jl
Comment thread test/testsuite/TestSuite.jl
Comment thread src/implementations/svd.jl Outdated
Comment thread src/implementations/svd.jl Outdated
@Jutho

Jutho commented Aug 24, 2026

Copy link
Copy Markdown
Member

I mostly agree with Lukas here, I think I would prefer

  • separate batched_f methods instead of separate algorithms (with possible options provided in the driver rather than the algorithm if we need those)
  • support for varying sizes, which we will anyway need to implement, so we can do it on the MAK side I would think

@kshyatt

kshyatt commented Sep 14, 2026

Copy link
Copy Markdown
Member Author

support for varying sizes, which we will anyway need to implement, so we can do it on the MAK side I would think

This is already implemented in a separate branch I have over at TensorKit, I can move it over here of course!

@kshyatt
kshyatt marked this pull request as draft September 14, 2026 13:21
@kshyatt

kshyatt commented Sep 14, 2026 •

Copy link
Copy Markdown
Member Author

Latest commit gets rid of the JacobiBatched and friends algos, and adds instead batched_svd_compact etc. Remaining TODOs:

  • Add tests for batches with varying sizes of input matrix (done!)
  • Add padding support in here, rather than in TensorKit (done!)
  • Add support for svd_via_adjoint! for batched arrays (done!)

@codecov

codecov Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.29442% with 283 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
ext/MatrixAlgebraKitAMDGPUExt/yarocsolver.jl 49.86% 189 Missing ⚠️
src/implementations/batched_svd.jl 72.03% 73 Missing ⚠️
...ixAlgebraKitAMDGPUExt/MatrixAlgebraKitAMDGPUExt.jl 52.00% 12 Missing ⚠️
...MatrixAlgebraKitCUDAExt/MatrixAlgebraKitCUDAExt.jl 40.00% 3 Missing ⚠️
src/batches.jl 93.87% 3 Missing ⚠️
src/interface/batched_svd.jl 0.00% 2 Missing ⚠️
ext/MatrixAlgebraKitCUDAExt/yacusolver.jl 97.43% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/MatrixAlgebraKit.jl 100.00% <ø> (ø)
src/common/gauge.jl 100.00% <100.00%> (ø)
src/implementations/svd.jl 95.74% <100.00%> (-0.02%) ⬇️
ext/MatrixAlgebraKitCUDAExt/yacusolver.jl 96.38% <97.43%> (+0.15%) ⬆️
src/interface/batched_svd.jl 0.00% <0.00%> (ø)
...MatrixAlgebraKitCUDAExt/MatrixAlgebraKitCUDAExt.jl 77.61% <40.00%> (-3.35%) ⬇️
src/batches.jl 93.87% <93.87%> (ø)
...ixAlgebraKitAMDGPUExt/MatrixAlgebraKitAMDGPUExt.jl 56.96% <52.00%> (-8.62%) ⬇️
src/implementations/batched_svd.jl 72.03% <72.03%> (ø)
ext/MatrixAlgebraKitAMDGPUExt/yarocsolver.jl 64.03% <49.86%> (-24.61%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kshyatt

kshyatt commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

One remaining thing to do here is restore the specific path that targets the AMDGPU ROCVector{<:ROCMatrix} path, otherwise I think things are looking ok. stack not only isn't supported on GPU, it doesn't work for the ragged case (unless we pad separately for each input, which I think is inefficient?).

@kshyatt
kshyatt marked this pull request as ready for review September 17, 2026 11:16
@kshyatt

kshyatt commented Sep 18, 2026

Copy link
Copy Markdown
Member Author

Moving the bisection (batched and non) logic out of here to make this easier to understand

@kshyatt
kshyatt force-pushed the ksh/batched_svd branch 2 times, most recently from 863db9f to 1bcbe28 Compare September 24, 2026 05:35

@lkdvos lkdvos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct that we are still assuming all arrays to be of the same size for the batched svd? I know this is more in line with what vendors provide, but I don't immediately see how that will be useful for TensorKit, in general we have completely different sizes for the blocks?

Comment thread ext/MatrixAlgebraKitAMDGPUExt/MatrixAlgebraKitAMDGPUExt.jl Outdated
Comment thread ext/MatrixAlgebraKitCUDAExt/MatrixAlgebraKitCUDAExt.jl Outdated
Comment thread ext/MatrixAlgebraKitCUDAExt/yacusolver.jl Outdated
@kshyatt

kshyatt commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

We now do support ragged block sizes but the vendors only accept uniform batches, so the padding support is now present here. For lots of small blocks I think this still gives a performance benefit because the latency of launching a lot of small, individual SVDs is extremely bad.

Comment thread ext/MatrixAlgebraKitAMDGPUExt/MatrixAlgebraKitAMDGPUExt.jl
Comment thread ext/MatrixAlgebraKitAMDGPUExt/yarocsolver.jl Outdated
Comment thread src/implementations/batched_svd.jl Outdated
Comment thread src/implementations/batched_svd.jl Outdated
@Jutho

Jutho commented Sep 25, 2026

Copy link
Copy Markdown
Member

Regarding the equal versus unequal sizes. Do we know of any library providing support for any operation (matrix multiplication or some decomposition) in batched mode with unequal sizes? If not, we can make it a MatrixAlgebraKit policy to also only provide batched_ methods for equal sizes, and it is op to TensorKit.jl for how to deal with that for the tensor blocks.

I guess many different strategies will be worth investigating. I can imagine something where say, 90%, of the blocks is promoted to their largest common size by padding, but then the 10% largest blocks are still just ran serially in order to avoid having to promote everything to unreasonable size.

Comment thread src/implementations/batched_svd.jl Outdated
@kshyatt

kshyatt commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

Regarding the equal versus unequal sizes. Do we know of any library providing support for any operation (matrix multiplication or some decomposition) in batched mode with unequal sizes? If not, we can make it a MatrixAlgebraKit policy to also only provide batched_ methods for equal sizes, and it is op to TensorKit.jl for how to deal with that for the tensor blocks.

I don't know of any. I guess it's a bit frustrating to have been told to move the ragged handling in here only to now have to move it back out again, but if that's what everyone prefers we can do it.

@Jutho

Jutho commented Sep 25, 2026

Copy link
Copy Markdown
Member

Oh sorry, I had overlooked this and probably didn't fully associate the word ragged with this. In that case, there is no need to remove this. Even if we want to follow more advanced strategies where only part of the blocks are treated via batched_svd, we can still use (ragged) batched_svd here for those blocks.

@kshyatt

kshyatt commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

That sounds good to me. I don't know if "ragged" is the generally accepted term (is there even one?) but we can also revisit this in the future, of course.

@lkdvos

lkdvos commented Sep 25, 2026

Copy link
Copy Markdown
Member

I do have to say that I don't think I fully agree that just because there are no libraries that provide ragged batched implementations that we shouldn't try having an interface for this anyways, because no matter what we can't really get around the problem that we will need this. In any case we will have to put the implementation either here or in TensorKit, and my feeling is that it makes sense to put such a "kernel" in MatrixAlgebraKit since this is really just matrix algebra. This being said, since there are definitely different things that can be done in a truly uniform batched case it could make sense to have these as separate functions, and in that case this PR can be focusing on the uniform case? If I recall correctly, MKL has something like this for matmul as well, where there is batched and grouped batched versions to have uniform sizes within a single group and varying between groups

Comment thread src/batches.jl
rest = Int[]
end
return batches, rest
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand the logic with pad == true here. I am fine without padding, batches contains lists of indices of matrices with same size, and their corresponding size. rest contains all other matrices (sizes that don't occur frequently enough, or sizes that are too large).

Padding will try to put everything in rest into a single batch, by padding them to the same (maximal) size. If that fails (because e.g. the maximal size is too big), it just gives up.

I would rather think that, with padding, we want to create a single batch of those matrices that are smaller than batch_size_limit, and then only keep those with a larger size in rest.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, of course, goes into the whole discussion of batching strategies.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not sure this is optimal but my thinking was to kick off the first batch ASAP while we pad out and construct the rest. But maybe we can delay the full investigation of the best strategy for a later PR?

Comment thread ext/MatrixAlgebraKitCUDAExt/yacusolver.jl Outdated
Comment on lines +320 to +322
# these MUST be "full" sized
Ṽ = similar(Vᴴ, (n, n, batch_size))
Ũ = similar(U, (m, m, batch_size))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another undocumented CUSOLVER "feature" ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🫠 It's a journey of discovery

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants