@@ -27,7 +27,19 @@ function MRISubspaceRecon.FFTNormalOp(Λ::CuArray{Tc}; cmaps=(1,), eltype_x=elty
2727 fftplan = plan_fft! (kL1, 1 : length (img_shape))
2828 ifftplan = plan_ifft! (kL2, 1 : length (img_shape))
2929
30- A = MRISubspaceRecon. _FFTNormalOp (img_shape, Ncoeff, fftplan, ifftplan, Λ, kmask_indcs, kL1, kL2, cmaps)
30+ # Pre-compute kernel launch configuration
31+ Nk = size (Λ, 3 )
32+ kL1_rs = reshape (kL1, :, Ncoeff)
33+ kL2_rs = reshape (kL2, :, Ncoeff)
34+ kernel = @cuda launch= false kernel_mul_cartesian! (kL2_rs, Λ, kL1_rs, kmask_indcs)
35+ config = launch_configuration (kernel. fun)
36+
37+ threads_x = min (config. threads ÷ Ncoeff, Nk)
38+ threads_y = min (config. threads ÷ threads_x, Ncoeff)
39+ threads = (threads_x, threads_y)
40+ blocks = cld .((Nk, Ncoeff), threads)
41+
42+ A = MRISubspaceRecon. _FFTNormalOp (img_shape, Ncoeff, fftplan, ifftplan, Λ, kmask_indcs, kL1, kL2, cmaps, threads, blocks)
3143
3244 return LinearOperator (
3345 eltype_x,
@@ -43,7 +55,7 @@ function MRISubspaceRecon.FFTNormalOp(Λ::CuArray{Tc}; cmaps=(1,), eltype_x=elty
4355end
4456
4557# # ##########################################################################
46- # Kernel Calculation
58+ # Internal use
4759# ############################################################################
4860
4961function calculate_kernel_cartesian (img_shape, trj:: CuArray{<:Integer,3} , U; sample_mask= CUDA. ones (Bool, size (trj)[2 : end ]), verbose= false )
@@ -59,12 +71,12 @@ function calculate_kernel_cartesian(img_shape, trj::CuArray{<:Integer,3}, U; sam
5971 verbose && println (" calculating Cartesian kernel on GPU..." )
6072 t = @elapsed CUDA. @sync begin
6173 # Configure kernel launch
62- kernel = @cuda launch= false _kernel_cartesian_complex ! (Λ_real, trj, U, sample_mask, img_shape, Ncoeff, Nsamp, Nt, Nrep)
74+ kernel = @cuda launch= false kernel_cartesian_complex ! (Λ_real, trj, U, sample_mask, img_shape, Ncoeff, Nsamp, Nt, Nrep)
6375 config = launch_configuration (kernel. fun)
6476 threads = min (config. threads, Nsamp)
6577 blocks = cld (Nsamp, threads)
6678
67- @cuda threads= threads blocks= blocks _kernel_cartesian_complex ! (Λ_real, trj, U, sample_mask, img_shape, Ncoeff, Nsamp, Nt, Nrep)
79+ @cuda threads= threads blocks= blocks kernel_cartesian_complex ! (Λ_real, trj, U, sample_mask, img_shape, Ncoeff, Nsamp, Nt, Nrep)
6880 end
6981 verbose && println (" time to compute kernel: t = $t s" )
7082
@@ -74,7 +86,7 @@ function calculate_kernel_cartesian(img_shape, trj::CuArray{<:Integer,3}, U; sam
7486 return Λ
7587end
7688
77- function _kernel_cartesian_complex ! (Λ_real, trj, U, sample_mask, img_shape:: NTuple{N,Int} , Ncoeff, Nsamp, Nt, Nrep) where N
89+ function kernel_cartesian_complex ! (Λ_real, trj, U, sample_mask, img_shape:: NTuple{N,Int} , Ncoeff, Nsamp, Nt, Nrep) where N
7890 is = (blockIdx (). x - 1 ) * blockDim (). x + threadIdx (). x
7991
8092 if is <= Nsamp
@@ -91,7 +103,7 @@ function _kernel_cartesian_complex!(Λ_real, trj, U, sample_mask, img_shape::NTu
91103 for irep in 1 : Nrep
92104 val += conj (U[it, ic1, irep]) * U[it, ic2, irep]
93105 end
94- # CUDA atomics don't support complex directly, so split into real/imag
106+ # CUDA atomics does not support complex directly, so split into real/imag
95107 CUDA. @atomic Λ_real[1 , ic1, ic2, k_idx... ] += real (val)
96108 CUDA. @atomic Λ_real[2 , ic1, ic2, k_idx... ] += imag (val)
97109 end
@@ -126,9 +138,7 @@ function LinearAlgebra.mul!(x::CuArray, S::MRISubspaceRecon._FFTNormalOp, b, α,
126138 kL2_rs = reshape (S. kL2, :, S. Ncoeff)
127139 fill! (S. kL2, 0 )
128140
129- # Batched matrix multiplication at each masked k-space location
130- # Each kmask_indcs[i] indexes a row in kL1_rs/kL2_rs, and Λ[:,:,i] is the kernel matrix
131- _batched_kernel_mul! (kL2_rs, S. Λ, kL1_rs, S. kmask_indcs)
141+ @cuda threads= S. threads blocks= S. blocks kernel_mul_cartesian! (kL2_rs, S. Λ, kL1_rs, S. kmask_indcs)
132142
133143 # Inverse FFT and accumulate
134144 S. ifftplan * S. kL2
@@ -137,23 +147,7 @@ function LinearAlgebra.mul!(x::CuArray, S::MRISubspaceRecon._FFTNormalOp, b, α,
137147 return x
138148end
139149
140- # Batched kernel multiplication on GPU
141- function _batched_kernel_mul! (kL2_rs:: CuArray , Λ:: CuArray , kL1_rs:: CuArray , kmask_indcs)
142- Ncoeff = size (Λ, 1 )
143- Nk = size (Λ, 3 )
144-
145- kernel = @cuda launch= false _kernel_mul_cartesian! (kL2_rs, Λ, kL1_rs, kmask_indcs)
146- config = launch_configuration (kernel. fun)
147-
148- threads_x = min (config. threads ÷ Ncoeff, Nk)
149- threads_y = min (config. threads ÷ threads_x, Ncoeff)
150- threads = (threads_x, threads_y)
151- blocks = cld .((Nk, Ncoeff), threads)
152-
153- @cuda threads= threads blocks= blocks _kernel_mul_cartesian! (kL2_rs, Λ, kL1_rs, kmask_indcs)
154- end
155-
156- function _kernel_mul_cartesian! (kL2_rs, Λ, kL1_rs, kmask_indcs)
150+ function kernel_mul_cartesian! (kL2_rs, Λ, kL1_rs, kmask_indcs)
157151 ik = (blockIdx (). x - 1 ) * blockDim (). x + threadIdx (). x
158152 ic1 = (blockIdx (). y - 1 ) * blockDim (). y + threadIdx (). y
159153
0 commit comments