Expand description
Parallel sparse × dense kernels.
§Why there are two kernels
A compressed matrix can only be walked along its outer dimension. For a CSR matrix that is rows, so:
A · Dwrites output rowifrom sparse rowi. Threads own disjoint output rows, so this needs no scratch and no reduction —gather_mul.Aᵀ · Dreads sparse rowiand scatters into output rowsjfor every columnjpresent in that row. Threads collide, so accumulation is needed —scatter_mul.
transpose_view() does not escape this: it relabels a CSR matrix as a CSC view of
the transpose, but the traversable dimension is unchanged. What it does buy is
that a CSC-stored matrix gets the disjoint kernel for Aᵀ · D for free, so callers
holding CSC pay nothing for the transposed direction.
§Scratch budgeting
The 1.x code allocated one full n × k buffer per chunk, with chunk count
driven by matrix size — 64 chunks on a 200k-row matrix, so ~922 MiB of scratch for
a single 30000 × 60 product. Here the accumulator count is the thread count, and
if threads × n × k still exceeds DEFAULT_SCRATCH_BUDGET the dense columns are
processed in blocks so the bound always holds.
Constants§
- DEFAULT_
SCRATCH_ BUDGET - Upper bound on transient scratch for scatter-direction products, in bytes.
Functions§
- gather_
mul out = lhs · rhswherelhsis CSR. Write-disjoint: no scratch, no reduction.- gather_
mul_ vec y = lhs · xwherelhsis CSR. Write-disjoint.- scatter_
mul out = lhsᵀ · rhswherelhsis CSR. Scatter direction: uses one accumulator per thread, blocking over the columns ofrhsto keep scratch underbudget.- scatter_
mul_ vec y = lhsᵀ · xwherelhsis CSR. Scatter direction, one accumulator per thread.