Skip to content

Commit f310ec1

Browse files
Sébastien LoiselSébastien Loisel
authored andcommitted
Unify CPU/GPU code paths with rowptr_target/colval_target
Replace conditional GPU array checks with always-populated target arrays: - For CPU: rowptr_target === rowptr, colval_target === colval (same objects) - For GPU: rowptr_target/colval_target are GPU copies This eliminates runtime conditionals in SpMV and other operations, providing a single unified code path for both CPU and GPU backends. Changes: - Rename rowptr_gpu/colval_gpu to rowptr_target/colval_target - Add _to_target_backend() helper function - Update all SparseMatrixMPI constructor sites (~20 locations) - Update SpMV to use target arrays directly without conditionals - Update Metal extension mtl()/cpu() functions
1 parent c7bf2ed commit f310ec1

5 files changed

Lines changed: 218 additions & 123 deletions

File tree

ext/LinearAlgebraMPIMetalExt.jl

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ const MtlSparseMatrixMPI{T,Ti} = LinearAlgebraMPI.SparseMatrixMPI{T,Ti,MtlVector
4141
mtl(A::LinearAlgebraMPI.SparseMatrixMPI)
4242
4343
Convert a CPU SparseMatrixMPI to a Metal GPU SparseMatrixMPI.
44-
Only the nonzero values (`nzval`) are moved to GPU. Structural arrays
45-
(`rowptr`, `colval`, partitions) remain on CPU.
44+
The `nzval` and target structure arrays are moved to GPU.
45+
The CPU structure arrays (`rowptr`, `colval`, partitions) remain on CPU for MPI.
4646
"""
4747
function LinearAlgebraMPI.mtl(A::LinearAlgebraMPI.SparseMatrixMPI{T,Ti,Vector{T}}) where {T,Ti}
4848
nzval_gpu = MtlVector(A.nzval)
49+
# Convert structure arrays to GPU (used by unified SpMV kernel)
50+
rowptr_target = MtlVector(A.rowptr)
51+
colval_target = MtlVector(A.colval)
4952
return LinearAlgebraMPI.SparseMatrixMPI{T,Ti,MtlVector{T}}(
5053
A.structural_hash,
5154
A.row_partition,
@@ -57,7 +60,9 @@ function LinearAlgebraMPI.mtl(A::LinearAlgebraMPI.SparseMatrixMPI{T,Ti,Vector{T}
5760
A.nrows_local,
5861
A.ncols_compressed,
5962
nothing, # Invalidate cached_transpose (would need to convert too)
60-
A.cached_symmetric
63+
A.cached_symmetric,
64+
rowptr_target,
65+
colval_target
6166
)
6267
end
6368

@@ -68,6 +73,7 @@ Convert a Metal GPU SparseMatrixMPI to a CPU SparseMatrixMPI.
6873
"""
6974
function LinearAlgebraMPI.cpu(A::LinearAlgebraMPI.SparseMatrixMPI{T,Ti,<:MtlVector}) where {T,Ti}
7075
nzval_cpu = Array(A.nzval)
76+
# For CPU, rowptr_target and colval_target are the same as rowptr and colval
7177
return LinearAlgebraMPI.SparseMatrixMPI{T,Ti,Vector{T}}(
7278
A.structural_hash,
7379
A.row_partition,
@@ -79,7 +85,9 @@ function LinearAlgebraMPI.cpu(A::LinearAlgebraMPI.SparseMatrixMPI{T,Ti,<:MtlVect
7985
A.nrows_local,
8086
A.ncols_compressed,
8187
nothing, # Invalidate cached_transpose
82-
A.cached_symmetric
88+
A.cached_symmetric,
89+
A.rowptr, # rowptr_target (same as rowptr for CPU)
90+
A.colval # colval_target (same as colval for CPU)
8391
)
8492
end
8593

@@ -140,4 +148,24 @@ function LinearAlgebraMPI._create_output_like(v::LinearAlgebraMPI.VectorMPI{T,<:
140148
return LinearAlgebraMPI.mtl(v)
141149
end
142150

151+
# ============================================================================
152+
# MatrixPlan Index Array Support
153+
# ============================================================================
154+
155+
"""
156+
_index_array_type(::Type{MtlVector{T}}, ::Type{Ti}) where {T,Ti}
157+
158+
Map MtlVector{T} value array type to MtlVector{Ti} index array type.
159+
Used by MatrixPlan to store symbolic index arrays on GPU.
160+
"""
161+
LinearAlgebraMPI._index_array_type(::Type{MtlVector{T}}, ::Type{Ti}) where {T,Ti} = MtlVector{Ti}
162+
163+
"""
164+
_to_target_backend(v::Vector{Ti}, ::Type{MtlVector{T}}) where {Ti,T}
165+
166+
Convert a CPU index vector to Metal GPU.
167+
Used by SparseMatrixMPI constructors to create GPU structure arrays.
168+
"""
169+
LinearAlgebraMPI._to_target_backend(v::Vector{Ti}, ::Type{<:MtlVector}) where {Ti} = MtlVector(v)
170+
143171
end # module

src/LinearAlgebraMPI.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ function SparseArrays.SparseMatrixCSC(A::SparseMatrixCSR{Tv,Ti}) where {Tv,Ti}
109109
end
110110

111111
# Cache for memoized MatrixPlans
112-
# Key: (A_hash, B_hash, T, Ti) - use full 256-bit hashes
113-
const _plan_cache = Dict{Tuple{Blake3Hash,Blake3Hash,DataType,DataType},Any}()
112+
# Key: (A_hash, B_hash, T, Ti, AV) - use full 256-bit hashes, includes array type for GPU support
113+
const _plan_cache = Dict{Tuple{Blake3Hash,Blake3Hash,DataType,DataType,Type},Any}()
114114

115115
# Cache for memoized VectorPlans (for A * x)
116116
# Key: (A_hash, x_hash, T, AV) - includes array type for GPU support
@@ -141,8 +141,8 @@ end
141141
const _diag_structure_cache = Dict{Blake3Hash,DiagStructureCache}()
142142

143143
# Cache for memoized AdditionPlans (for A + B and A - B)
144-
# Key: (A_hash, B_hash, T, Ti) - use full 256-bit hashes
145-
const _addition_plan_cache = Dict{Tuple{Blake3Hash,Blake3Hash,DataType,DataType},Any}()
144+
# Key: (A_hash, B_hash, T, Ti, AV) - use full 256-bit hashes, includes array type for GPU support
145+
const _addition_plan_cache = Dict{Tuple{Blake3Hash,Blake3Hash,DataType,DataType,Type},Any}()
146146

147147
# Cache for memoized IdentityAdditionPlans (for A + λI)
148148
# Key: (A_hash, T, Ti) - use full 256-bit hash

src/indexing.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,9 +1049,10 @@ function Base.getindex(A::SparseMatrixMPI{T,Ti}, row_rng::UnitRange{Int}, col_rn
10491049
# SparseMatrixCSC(ncols, nrows, colptr, rowval, nzval) - transposed storage
10501050
empty_AT = SparseMatrixCSC(new_ncols, my_local_rows, ones(Int, my_local_rows + 1), Int[], T[])
10511051
hash = compute_structural_hash(new_row_partition, Int[], empty_AT, comm)
1052+
# For CPU, rowptr_target and colval_target are the same as rowptr and colval
10521053
return SparseMatrixMPI{T,Ti,Vector{T}}(hash, new_row_partition, new_col_partition, Int[],
10531054
empty_AT.colptr, empty_AT.rowval, empty_AT.nzval,
1054-
my_local_rows, 0, nothing, nothing)
1055+
my_local_rows, 0, nothing, nothing, empty_AT.colptr, empty_AT.rowval)
10551056
end
10561057

10571058
# Compute new row partition (local computation, no communication)
@@ -1161,9 +1162,10 @@ function Base.getindex(A::SparseMatrixMPI{T,Ti}, row_rng::UnitRange{Int}, col_rn
11611162
hash = compute_structural_hash(new_row_partition, final_col_indices, new_AT.colptr, new_AT.rowval, comm)
11621163

11631164
# Use explicit CSR arrays for the new struct format
1165+
# For CPU, rowptr_target and colval_target are the same as rowptr and colval
11641166
return SparseMatrixMPI{T,Ti,Vector{T}}(hash, new_row_partition, new_col_partition, final_col_indices,
11651167
new_AT.colptr, new_AT.rowval, new_AT.nzval, local_nrows,
1166-
length(final_col_indices), nothing, nothing)
1168+
length(final_col_indices), nothing, nothing, new_AT.colptr, new_AT.rowval)
11671169
end
11681170

11691171
# Convenience: A[row_rng, :] - all columns

src/mumps_factorization.jl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,18 +371,23 @@ function _convert_to_mumps_compatible(A::SparseMatrixMPI{T,Ti,AV}) where {T,Ti,A
371371

372372
# Create new SparseMatrixMPI with converted type (CPU Vector)
373373
# Structural arrays (rowptr, colval, col_indices) stay the same
374+
new_rowptr = copy(A.rowptr)
375+
new_colval = copy(A.colval)
376+
# For CPU, rowptr_target and colval_target are the same as rowptr and colval
374377
return SparseMatrixMPI{Tinternal,Ti,Vector{Tinternal}}(
375378
A.structural_hash,
376379
copy(A.row_partition),
377380
copy(A.col_partition),
378381
copy(A.col_indices),
379-
copy(A.rowptr),
380-
copy(A.colval),
382+
new_rowptr,
383+
new_colval,
381384
nzval_converted,
382385
A.nrows_local,
383386
A.ncols_compressed,
384387
nothing, # Don't copy cached transpose
385-
A.cached_symmetric
388+
A.cached_symmetric,
389+
new_rowptr, # rowptr_target (same as rowptr for CPU)
390+
new_colval # colval_target (same as colval for CPU)
386391
)
387392
end
388393

0 commit comments

Comments
 (0)