Skip to content

Commit 7974e45

Browse files
jotabulaciosdiegokingstonMauroToscano
authored
perf/row-major trace LDE for GPU path (#715)
* add row major batched lde fft primitives * Make LDETraceTable row-major * Wire prover to row-major batched LDE * read trace row major * Move the batched-FFT and row-major-LDE unit tests into corresponding file * fix disk-spill EmptyCommitment in row-major LDE * Parallelize trace build and speed up op-dedup bookkeeping * Skip the identity multiply by alpha_powers[0] in LogUp fingerprints * Remove dead FFT module and gate legacy twiddles * Harden parallel row-major bit-reverse permute * Guard columns_to_row_major; clarify hasher doc * Deduplicate commit_rows_bit_reversed and bit_reverse_vec * add gpu tests * Add GPU/CPU Merkle root parity test * Add GPU/CPU Merkle root parity tests for base and ext3 aux trace * Add GPU/CPU barycentric OOD parity tests * Fix ext3 pre-strided layout in barycentric parity test * Fix instruments double-billing GPU fused pipeline in R1 * Add test verifying GPU and CPU proofs both pass verification * Clean up verbose comments in parity tests * GPU R1 GPU: eliminate extract_columns + columns_to_row_major via on-device transpose * Revert "GPU R1 GPU: eliminate extract_columns + columns_to_row_major via on-device transpose" This reverts commit 38f5600. * GPU R1: row-major NTT kernel — no transpose, coalesced column access * Fix GPU R1 row-major: transpose buf to col-major for device handle * Fix keccak row-major launch config: use 128-thread block, not 1024 * GPU R1 aux: row-major ext3 NTT reusing base-field kernels with m*3 * Clean up GPU row-major LDE: extract transpose helper, fix zero-pad alloc, trim stale comments * Clean up GPU row-major LDE: extract helper, fix alloc, trim comments * Fix gpu_lde_threshold OnceLock: re-read env var in test builds * Fix cross-stream race: synchronize after transpose before returning handle * Add parity tests for new row-major GPU pipeline * Remove dead batched-keep GPU LDE functions * fix lint * Add debug_assert for Fp3 Vec::from_raw_parts invariant * Revert unrelated FxHashMap op-dedup change (out of scope for #715) The FxHasher/FxHashMap op-dedup micro-optimization is unrelated to the row-major GPU LDE rework and was only applied to 4 of 6 dedup tables. Revert the table maps to std HashMap and drop the hasher; it can land as its own focused PR. * Remove redundant gpu_and_cpu_proofs_both_verify test The GPU full path is covered by the normal prove/verify suite built with --features cuda (plus gpu_path_fires_end_to_end), the CPU path by the non-cuda suite, and GPU/CPU equivalence by the merkle/barycentric parity tests. Its force-CPU leg also never ran on CPU: gpu_lde_threshold() only re-read the env var under cfg(test), but from the prover integration crate stark compiles without cfg(test), so the OnceLock cached the first value. Simplify gpu_lde_threshold() to a single cached impl now that the per-call re-read has no consumer. * Fix stale docs and remove dead code keccak.cu: move keccak256_leaves_base_row_major out of keccak_merkle_level's doc block so the child-pair->parent doc rejoins its kernel. prover.rs: delete columns_to_row_major, which has no callers after the row-major GPU path stopped materializing GPU-expanded columns. * Consolidate row-major LDE pipeline; guard keccak num_rows Extract coset_lde_row_major_inner shared by the base and ext3 _keep entry points (they differed only by m vs m*3 and the handle type), removing ~110 lines of drift-prone duplication. Add debug_assert!(num_rows >= 2) to launch_keccak_base_row_major: the kernel shifts by (64 - log_num_rows), UB at num_rows==1, matching the guard in launch_keccak_base. * Fix stale R2 composition-LDE assertion in gpu_path_fires_end_to_end The assert checked gpu_parts_lde_calls() > 0 with a comment claiming branch/shift tables are degree-3 — both false: fib_iterative_1M tables all have number_of_parts <= 2, and the common degree-2 case fires the fused two-halves path (gpu_extend_halves_calls), counted separately from the parts>2 path (gpu_parts_lde_calls) since #700. Assert on the sum so either composition-LDE path satisfies it. Validated on RTX 5090 / CUDA 13.1: make test-math-cuda 78/78, make test-cuda-integration green, proof verifies. --------- Co-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com> Co-authored-by: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> Co-authored-by: MauroFab <maurotoscano2@gmail.com>
1 parent 38feb15 commit 7974e45

11 files changed

Lines changed: 1363 additions & 174 deletions

File tree

crypto/math-cuda/kernels/keccak.cu

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,37 @@ extern "C" __global__ void keccak_merkle_level(
347347

348348
finalize_keccak256(st, rate_pos, nodes + (parent_begin + tid) * 32);
349349
}
350+
351+
// ---------------------------------------------------------------------------
352+
// Row-major base leaf hashing.
353+
//
354+
// Input layout: data[row * m + col] for `num_rows` rows and `m` columns.
355+
// For leaf `tid`, reads the bit-reversed row `br(tid)` — a contiguous slice
356+
// of `m` elements starting at data[br * m]. Coalesced when multiple threads
357+
// in the same warp process consecutive `tid` values (they read non-overlapping
358+
// rows, each a contiguous block of m u64s in order).
359+
// ---------------------------------------------------------------------------
360+
extern "C" __global__ void keccak256_leaves_base_row_major(
361+
const uint64_t *data,
362+
uint64_t m,
363+
uint64_t num_rows,
364+
uint64_t log_num_rows,
365+
uint8_t *hashed_leaves_out)
366+
{
367+
uint64_t tid = (uint64_t)blockIdx.x * blockDim.x + threadIdx.x;
368+
if (tid >= num_rows) return;
369+
uint64_t br = __brevll(tid) >> (64 - log_num_rows);
370+
const uint64_t *row = data + br * m;
371+
372+
uint64_t st[25];
373+
#pragma unroll
374+
for (int i = 0; i < 25; ++i) st[i] = 0;
375+
376+
uint32_t rate_pos = 0;
377+
for (uint64_t c = 0; c < m; ++c) {
378+
uint64_t canon = goldilocks::canonical(row[c]);
379+
uint64_t lane = bswap64(canon);
380+
absorb_lane(st, rate_pos, lane);
381+
}
382+
finalize_keccak256(st, rate_pos, hashed_leaves_out + tid * 32);
383+
}

crypto/math-cuda/kernels/ntt.cu

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,129 @@ extern "C" __global__ void ntt_dit_8_levels(uint64_t *x,
285285
// Store back to the remapped row.
286286
x[row] = tile[threadIdx.x];
287287
}
288+
289+
// ============================================================================
290+
// ROW-MAJOR BATCHED KERNELS
291+
//
292+
// Data layout: data[row * m + col] for n rows and m columns.
293+
// threadIdx.x = column index → consecutive threads access consecutive columns
294+
// of the same row → coalesced global memory access.
295+
// Twiddle factors depend only on the butterfly position, not the column →
296+
// one twiddle load is broadcast across the entire warp.
297+
// ============================================================================
298+
299+
// Bit-reverse permute rows: swap row `row` with row `br(row)`.
300+
// Grid: gridDim.x = ceil(m / 256), gridDim.y = min(n, 65535).
301+
// Grid-stride loop over rows so a capped gridDim.y covers all n rows.
302+
extern "C" __global__ void bit_reverse_row_major(uint64_t *data,
303+
uint64_t n,
304+
uint64_t log_n,
305+
uint64_t m)
306+
{
307+
uint64_t col = (uint64_t)blockIdx.x * blockDim.x + threadIdx.x;
308+
if (col >= m) return;
309+
for (uint64_t row = blockIdx.y; row < n; row += gridDim.y) {
310+
uint64_t rev = __brevll(row) >> (64 - log_n);
311+
if (row < rev) {
312+
uint64_t tmp = data[row * m + col];
313+
data[row * m + col] = data[rev * m + col];
314+
data[rev * m + col] = tmp;
315+
}
316+
}
317+
}
318+
319+
// One DIT butterfly level on row-major data.
320+
// Grid: gridDim.x = ceil(m / blockDim.x), gridDim.y = min(ceil(n/2 / blockDim.y), 65535).
321+
// blockDim.x covers columns (coalescing), blockDim.y covers butterfly pairs.
322+
// Grid-stride loop over butterfly-pair tiles so capped gridDim.y covers all n/2 pairs.
323+
extern "C" __global__ void ntt_dit_level_row_major(uint64_t *data,
324+
const uint64_t *tw,
325+
uint64_t n,
326+
uint64_t log_n,
327+
uint64_t level,
328+
uint64_t m)
329+
{
330+
uint64_t col = (uint64_t)blockIdx.x * blockDim.x + threadIdx.x;
331+
uint64_t n_half = n >> 1;
332+
if (col >= m) return;
333+
334+
uint64_t half = 1ULL << level;
335+
uint64_t block_size = half << 1;
336+
337+
for (uint64_t bfly_base = blockIdx.y * blockDim.y;
338+
bfly_base < n_half;
339+
bfly_base += (uint64_t)gridDim.y * blockDim.y) {
340+
uint64_t butterfly = bfly_base + threadIdx.y;
341+
if (butterfly >= n_half) break;
342+
343+
uint64_t block_idx = butterfly >> level;
344+
uint64_t k = butterfly & (half - 1);
345+
uint64_t i0 = block_idx * block_size + k;
346+
uint64_t i1 = i0 + half;
347+
348+
// Same twiddle for all columns at this butterfly position (broadcast).
349+
uint64_t w = tw[k << (log_n - level - 1)];
350+
351+
uint64_t u = data[i0 * m + col];
352+
uint64_t v = mul(w, data[i1 * m + col]);
353+
data[i0 * m + col] = add(u, v);
354+
data[i1 * m + col] = sub(u, v);
355+
}
356+
}
357+
358+
// Pointwise multiply row-major: data[row * m + col] *= weights[row].
359+
// One weight per row, broadcast across all m columns.
360+
// Grid: gridDim.x = ceil(m / 256), gridDim.y = min(n, 65535).
361+
// Grid-stride loop over rows.
362+
extern "C" __global__ void pointwise_mul_row_major(uint64_t *data,
363+
const uint64_t *weights,
364+
uint64_t n,
365+
uint64_t m)
366+
{
367+
uint64_t col = (uint64_t)blockIdx.x * blockDim.x + threadIdx.x;
368+
if (col >= m) return;
369+
for (uint64_t row = blockIdx.y; row < n; row += gridDim.y)
370+
data[row * m + col] = mul(data[row * m + col], weights[row]);
371+
}
372+
373+
// ── Row-major → column-major transpose (for GpuLdeBase handle) ───────────────
374+
//
375+
// Converts the row-major LDE output to the column-major layout that downstream
376+
// GPU kernels (DEEP, barycentric) require for the device handle.
377+
//
378+
// src[r * cols + c] → dst[c * out_stride + r]
379+
//
380+
// Grid: gridDim.x = ceil(cols/32), gridDim.y = min(ceil(rows/32), 65535).
381+
// Grid-strides over row tiles so all rows are covered when rows > 65535*32.
382+
383+
#define MTILE 32
384+
#define MTILE_P (MTILE + 1)
385+
386+
extern "C" __global__ void matrix_transpose_strided(
387+
const uint64_t *__restrict__ src,
388+
uint64_t *__restrict__ dst,
389+
uint32_t rows,
390+
uint32_t cols,
391+
uint64_t out_stride)
392+
{
393+
__shared__ uint64_t tile[MTILE][MTILE_P];
394+
395+
for (uint32_t row_base = blockIdx.y * MTILE; row_base < rows;
396+
row_base += gridDim.y * MTILE) {
397+
uint32_t x = blockIdx.x * MTILE + threadIdx.x;
398+
uint32_t y = row_base + threadIdx.y;
399+
400+
if (x < cols && y < rows)
401+
tile[threadIdx.y][threadIdx.x] = src[(uint64_t)y * cols + x];
402+
403+
__syncthreads();
404+
405+
uint32_t tx = row_base + threadIdx.x;
406+
uint32_t ty = blockIdx.x * MTILE + threadIdx.y;
407+
408+
if (tx < rows && ty < cols)
409+
dst[(uint64_t)ty * out_stride + tx] = tile[threadIdx.x][threadIdx.y];
410+
411+
__syncthreads();
412+
}
413+
}

crypto/math-cuda/src/device.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,14 @@ pub struct Backend {
140140
pub ntt_dit_8_levels_batched: CudaFunction,
141141
pub pointwise_mul_batched: CudaFunction,
142142
pub scalar_mul_batched: CudaFunction,
143+
// row-major NTT kernels
144+
pub bit_reverse_row_major: CudaFunction,
145+
pub ntt_dit_level_row_major: CudaFunction,
146+
pub pointwise_mul_row_major: CudaFunction,
147+
pub matrix_transpose_strided: CudaFunction,
143148

144149
// keccak.ptx
150+
pub keccak256_leaves_base_row_major: CudaFunction,
145151
pub keccak256_leaves_base_batched: CudaFunction,
146152
pub keccak256_leaves_ext3_batched: CudaFunction,
147153
pub keccak_comp_poly_leaves_ext3: CudaFunction,
@@ -237,6 +243,12 @@ impl Backend {
237243
ntt_dit_8_levels_batched: ntt.load_function("ntt_dit_8_levels_batched")?,
238244
pointwise_mul_batched: ntt.load_function("pointwise_mul_batched")?,
239245
scalar_mul_batched: ntt.load_function("scalar_mul_batched")?,
246+
bit_reverse_row_major: ntt.load_function("bit_reverse_row_major")?,
247+
ntt_dit_level_row_major: ntt.load_function("ntt_dit_level_row_major")?,
248+
pointwise_mul_row_major: ntt.load_function("pointwise_mul_row_major")?,
249+
matrix_transpose_strided: ntt.load_function("matrix_transpose_strided")?,
250+
keccak256_leaves_base_row_major: keccak
251+
.load_function("keccak256_leaves_base_row_major")?,
240252
keccak256_leaves_base_batched: keccak.load_function("keccak256_leaves_base_batched")?,
241253
keccak256_leaves_ext3_batched: keccak.load_function("keccak256_leaves_ext3_batched")?,
242254
keccak_comp_poly_leaves_ext3: keccak.load_function("keccak_comp_poly_leaves_ext3")?,

0 commit comments

Comments
 (0)