Native workflow¤
JAXMg accepts ordinary JAX arrays sharded over a two-dimensional device mesh. However, cuSOLVERMp requires column-major local buffers distributed in a 2D block-cyclic layout. The native backend bridges these layouts inside one fused C++/CUDA FFI call:
JAX block-sharded arrays
|
| Python validation and local tile-capacity padding
v
fused native FFI call
|
| 1. local row-major -> column-major conversion
| 2. top-left / edge-padding alignment
| 3. 2D block-cyclic redistribution
v
cuSOLVERMp solver
|
| reverse 3 -> reverse 2 -> reverse 1
v
JAX-facing result
Python calculates static metadata such as the process grid, rank map, logical matrix size, tile size, and padded local capacity. C++/CUDA owns the data movement, cuSOLVERMp descriptors and workspace, solver calls, and reverse redistribution. Matrix data remains GPU-resident throughout this workflow, and the in-place transformations reuse bounded native scratch storage to minimize memory overhead.
Matrix-valued solver outputs follow the reverse path shown above. The eigenvalues-only SYEVD mode returns the replicated eigenvalue array directly after solver execution.
Scratch allocation¤
The redistribution path uses one native scratch allocation per FFI call. Its size is determined by the largest tile slab used by the 2D block-cyclic stage:
For square solver tiles, \(T_r=T_c=T_A\). The layout-conversion and edge-padding stages reuse this allocation and process data in bounded batches. They do not allocate a second full local matrix.
Memory-distribution stages¤
Stage 1: local memory layout conversion
JAX presents each local matrix shard in row-major physical memory. cuSOLVERMp expects column-major local memory. CUDA kernels apply an in-place rectangular permutation using bounded scratch; the logical matrix is not transposed. This stage is local to each GPU and requires no communicator traffic.
Stage 2: top-left and edge-padding alignment
Python provides tile-aligned local capacity. Native horizontal compaction moves real column slabs toward the global left edge, then vertical compaction moves real row slabs toward the global top edge. Padding is consolidated on the global right and bottom edges.
Moves in one horizontal wave are independent across process rows. Moves in one vertical wave are independent across process columns.
Stage 3: 2D block-cyclic redistribution
Whole tile slabs are redistributed in two separable phases. The column-owner phase assigns each tile to the correct process column; the row-owner phase then assigns each tile to the correct process row.
Same-rank moves use local CUDA operations. Cross-rank moves use the
NCCL-backed communicator borrowed from XLA. The same communicator supplies
the ncclComm_t used by cuSOLVERMp.
Solver workflows¤
The redistribution stages are shared by all public routines:
potrscallscusolverMpPotrffollowed bycusolverMpPotrs. When requested, it reads the distributed factor diagonal and performs a one-scalar NCCL all-reduce to returnlog(det(A)).lu_solvecallscusolverMpGetrffollowed bycusolverMpGetrsand manages the distributed pivot allocation.syevdcallscusolverMpSyevdand materializes distributed eigenvalues plus eigenvectors when requested.
Each Python process owns one GPU and contributes one rank to the XLA/NCCL and cuSOLVERMp communicators. See Distributed execution for the required launch model.
Detailed implementation¤
Memory distribution gives the complete diagrams, address mappings, dependency waves, parallelism, reverse path, and source-code map.