Skip to content

Commit abc4ab4

Browse files
authored
Merge pull request #793 from yetanotherco/review/pr786-histogram-direct
Feed bitwise collectors straight into the histogram; cut reduce allocations
2 parents 1287123 + 4799da3 commit abc4ab4

3 files changed

Lines changed: 121 additions & 115 deletions

File tree

‎prover/src/tables/bitwise.rs‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,14 +517,21 @@ impl BitwiseHistogram {
517517
/// Increment the counter for one lookup.
518518
#[inline]
519519
pub fn bump(&mut self, op: BitwiseOperation) {
520+
self.bump_n(op, 1);
521+
}
522+
523+
/// Add `n` occurrences of one lookup in a single step (e.g. CPU padding rows,
524+
/// which all send identical all-zero lookups).
525+
#[inline]
526+
pub fn bump_n(&mut self, op: BitwiseOperation, n: u64) {
520527
let idx = lookup_type_index(op.lookup_type) * NUM_ROWS + row_index(op.x, op.y, op.z);
521528
// (x, y) are u8, and row_index debug-asserts z < 16, so in debug builds a
522529
// corrupt op fails loudly here. In release an out-of-domain z would NOT
523530
// panic: the flat index can land in another type's lane and silently
524531
// mis-count both cells — the proof then fails verification instead of the
525532
// prover crashing. What actually upholds the invariant is that every
526533
// `BitwiseOperation` constructor masks or debug-asserts z < 16.
527-
self.counters[idx] += 1;
534+
self.counters[idx] += n;
528535
}
529536

530537
/// Fold a slice of lookups into the histogram.

‎prover/src/tables/memw_register.rs‎

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
//!
1818
//! ## Column layout (10 columns)
1919
//!
20-
//! - `ADDRESS`: Byte (register index 0-31)
20+
//! - `ADDRESS`: Byte (register index 0-255: x0-x31, plus x254/x255)
2121
//! - `TIMESTAMP_0`: Word (low 32 bits)
2222
//! - `TIMESTAMP_1`: Word (high 32 bits)
2323
//! - `VAL_0`: Word (low 32 bits of register value)
@@ -43,7 +43,7 @@ use stark::trace::TraceTable;
4343

4444
use stark::constraints::builder::{ConstraintBuilder, ConstraintSet};
4545

46-
use super::bitwise::{BitwiseOperation, BitwiseOperationType};
46+
use super::bitwise::{BitwiseHistogram, BitwiseOperation, BitwiseOperationType};
4747
use super::memw::MemwOperation;
4848
use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, VmTable};
4949
use crate::constraints::templates::emit_is_bit;
@@ -53,7 +53,7 @@ use crate::constraints::templates::emit_is_bit;
5353
// =========================================================================
5454

5555
pub mod cols {
56-
/// Register index (0-31). CPU sends base_address = 2*reg_index.
56+
/// Register index (0-255: x0-x31, plus x254/x255). CPU sends base_address = 2*reg_index.
5757
pub const ADDRESS: usize = 0;
5858

5959
/// Timestamp low 32 bits
@@ -105,7 +105,9 @@ pub mod cols {
105105
/// enforced by `is_register_op`; the upper limb is TIMESTAMP_1 = timestamp>>32)
106106
#[derive(Debug, Clone, Copy)]
107107
pub(crate) struct RegRow {
108-
address: u64,
108+
/// Register index 0..=255 (`base_address / 2`); u16 keeps the struct at
109+
/// 32 bytes — it is the largest persisted array of the walk.
110+
address: u16,
109111
timestamp: u64,
110112
val0: u32,
111113
val1: u32,
@@ -140,8 +142,12 @@ impl RegRow {
140142
0,
141143
"register base_address must be even (got {reg_addr})"
142144
);
145+
debug_assert!(
146+
reg_addr / 2 <= u16::MAX as u64,
147+
"register index exceeds u16 (got base_address {reg_addr})"
148+
);
143149
RegRow {
144-
address: reg_addr / 2,
150+
address: (reg_addr / 2) as u16,
145151
timestamp,
146152
val0,
147153
val1,
@@ -210,7 +216,7 @@ pub(crate) fn generate_memw_register_trace_from_rows(
210216

211217
for (row_idx, r) in rows.iter().enumerate() {
212218
// ADDRESS = base_address / 2 (already divided in RegRow).
213-
table.set_u64(row_idx, cols::ADDRESS, r.address);
219+
table.set_u64(row_idx, cols::ADDRESS, r.address as u64);
214220
// Timestamp split into lo/hi 32-bit words.
215221
table.set_dword_wl(row_idx, cols::TIMESTAMP_0, r.timestamp);
216222
// Value: registers are DWordWL = 2 words.
@@ -250,13 +256,17 @@ fn memw_register_is_half_lookup(ts_lo: u32, old_ts_lo: u32) -> BitwiseOperation
250256
)
251257
}
252258

253-
/// IS_HALFWORD bitwise lookups for MEMW_R, computed directly from [`RegRow`]s via
254-
/// the shared [`memw_register_is_half_lookup`] helper (the same lookup the MEMW_R
255-
/// trace fill uses), so the multiplicities stay consistent with that table.
256-
pub(crate) fn collect_bitwise_from_memw_register(rows: &[RegRow]) -> Vec<BitwiseOperation> {
257-
rows.iter()
258-
.map(|r| memw_register_is_half_lookup((r.timestamp & 0xFFFF_FFFF) as u32, r.old_ts_lo))
259-
.collect()
259+
/// IS_HALFWORD bitwise lookups for MEMW_R, bumped straight into the histogram
260+
/// via the shared [`memw_register_is_half_lookup`] helper (the same lookup the
261+
/// MEMW_R trace fill uses), one per row. No intermediate op vector: register
262+
/// rows number in the tens of millions and the histogram is the only consumer.
263+
pub(crate) fn collect_bitwise_from_memw_register(rows: &[RegRow], hist: &mut BitwiseHistogram) {
264+
for r in rows {
265+
hist.bump(memw_register_is_half_lookup(
266+
(r.timestamp & 0xFFFF_FFFF) as u32,
267+
r.old_ts_lo,
268+
));
269+
}
260270
}
261271

262272
// =========================================================================

0 commit comments

Comments
 (0)