perf(crypto): stream field-element bytes into hashers and transcript#773
perf(crypto): stream field-element bytes into hashers and transcript#773Oppen wants to merge 3 commits into
Conversation
ea14eeb to
2e68711
Compare
Eliminates the per-element heap allocation in Merkle leaf hashing and Fiat-Shamir transcript appends. AsBytes gains a stream_bytes sink method, overridden zero-alloc for Goldilocks base and degree-3 extension; the Merkle backends and DefaultTranscript use it instead of as_bytes()/ to_bytes_be(). Adds hash_data_parts + verify_merkle_path_from_hash so row-pair openings hash two slices directly instead of concatenating them into a throwaway Vec first; verify_composition_poly_opening and FieldElementVectorBackend::hash_data now delegate to their row-pair/parts siblings instead of duplicating the same hashing loop. Recursion guest: single-query 89.7M -> 73.7M cycles, multi-query 2.21B -> 1.82B cycles.
stream_bytes was calling the base-field override three times, one per limb, each landing as its own Digest::update on the guest — three BlockBuffer::digest_blocks + memcpy calls instead of one. Disassembly of the guest ELF showed dyn dispatch itself was fully devirtualized by the #[inline(always)] chain, so the actual cost was the call count, not indirection. Reuse the existing write_bytes_be override (already zero-alloc, byte-identical layout) into a stack buffer and sink once. Recursion guest: single-query 73.9M -> 71.1M cycles, multi-query 1.82B -> 1.78B cycles.
aa8d48c to
ddb97be
Compare
|
/ai-review |
|
/bench |
Codex Code ReviewNo findings. I reviewed only the PR diff and did not identify safety/security, correctness, significant performance, or dead-code issues in the changed code. Tests were not run, per the static-review instructions. |
|
Review: perf(crypto): stream field-element bytes into hashers and transcript Reviewed the diff focusing on the byte-identity invariant, since the whole refactor rests on
Leaf-hash equivalence in the verifier also checks out:
No memory-safety, panic, or unbounded-allocation concerns — the changes remove allocations rather than add them, and the stack buffers are fixed-size and fully written before use. Minor (Low / cosmetic):
Nothing blocking. Clean, byte-preserving perf refactor. |
Benchmark — ethrex 20 transfers (median of 3)Table parallelism: auto (cores / 3)
Commit: ddb97be · Baseline: cached · Runner: self-hosted bench |
AI ReviewPR #773 · 8 changed files Findings
Status column reflects the verdict from the verifier: deepseek-verifier (openrouter/deepseek/deepseek-v4-pro). AI-001: Auxiliary `&` reborrow in `verify_composition_poly_opening` call
Claim The new body of Evidence In Suggested fix Replace Reviewer Lanes
Verification Lanes
Native Codex and Claude reviews run separately and post their own comments. They are not included in this structured provenance report. Raw lane outputs, candidates, final issues, and model metrics are uploaded as workflow artifacts. |
Summary
AsBytesgains astream_bytessink method, overridden zero-alloc for the Goldilocks base field and its degree-3 extension, replacingas_bytes()/to_bytes_be()returning a freshVec<u8>per element per hash.FieldElementVectorBackend::hash_data_parts+verify_merkle_path_from_hashso row-pair openings (verify_opening_pair,verify_composition_poly_opening,verify_fri_layer_openings) hash two slices directly instead of concatenating them into a throwawayVecfirst;verify_composition_poly_openingandhash_datanow delegate to their row-pair/parts siblings instead of duplicating the hashing loop.stream_byteswas calling the base-field override three times (one per limb), landing as three separateDigest::updatecalls on the guest instead of one. Disassembly confirmed thedyn FnMutsink itself was fully devirtualized (no indirect-call cost), so the actual waste was call count. Now reuses the existing zero-allocwrite_bytes_beoverride into a stack buffer and sinks once.Test plan
cargo test --workspace --exclude math-cuda(492 prover + 137 stark tests, all green) after each commitmake test-ethrexcargo test -p lambda-vm-prover --lib test_recursion_execute_1query -- --ignored --nocapture(in-VM verify accepts, guest commitsvk_digest ‖ output, byte-identical across every change)make test-profile-recursion-single/-multicycle counts confirmed against baseline after each commitllvm-objdump) confirmingstream_bytesdevirtualizes and the extension-field batching removes redundantBlockBuffer::digest_blocks/memcpycalls