Avoid unnecessary Vec allocations in greedy proof selection#254
Avoid unnecessary Vec allocations in greedy proof selection#254pablodeymo merged 1 commit intomainfrom
Conversation
In extend_proofs_greedily, the greedy set-cover loop was allocating a Vec<u64> for every candidate proof on each iteration just to compare coverage counts via .len(). Only the winning proof's coverage Vec is actually needed (to extend the covered set and count attestations). Now the selection pass computes only a count (no allocation) for each candidate, and collects the actual Vec only for the winner. This avoids N-1 unnecessary allocations per iteration, where N is the number of remaining proofs.
🤖 Kimi Code ReviewReview: Optimization in Verdict: Correct optimization, no issues. This is a straightforward performance improvement that reduces allocations without changing consensus logic. Analysis:
No changes required. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Codex Code ReviewNo findings. The change in store.rs looks like a safe micro-optimization: it preserves the greedy selection logic in Residual risk is low. I could not run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Claude Code ReviewReview: Avoid unnecessary Vec allocations in greedy proof selectionSummaryA focused, single-function optimization to CorrectnessThe critical invariant is that Edge cases are all handled correctly:
Performance
The tradeoff is favorable at any N ≥ 2, and particularly so on the block-production hot path where proof sets can grow with validator count. One alternative worth noting: it isn't possible to fold the collection into the selection pass to avoid the second iterator scan, because that would require a simultaneous mutable borrow of Style / Idioms
Minor Observation
These two uses are unavoidable given that VerdictApprove. The change is correct, focused, and a clear improvement. No behavioral change, no new allocations introduced, and the hot-path allocation count drops from O(N) to O(1) per greedy iteration. Tests and lint passing confirms no regressions. Automated review by Claude (Anthropic) · sonnet · custom prompt |
Greptile SummaryThis PR optimizes the greedy set-cover loop inside Key changes:
Correctness: The optimization is sound. Confidence Score: 5/5Safe to merge — pure performance optimization with no behavioral change and correct two-pass semantics. The change is mechanically correct: No files require special attention.
|
| Filename | Overview |
|---|---|
| crates/blockchain/src/store.rs | Splits greedy proof selection into a count-only pass (no allocation) and a collect pass for the winner only, eliminating N-1 unnecessary Vec allocations per loop iteration with no behavioral change. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Start: remaining_indices non-empty] --> B[Selection pass: for each remaining idx, call participant_indices + filter + count]
B --> C{best is Some?}
C -- No --> Z[Break]
C -- Yes --> D{best_count == 0?}
D -- Yes --> Z
D -- No --> E[Collection pass: call participant_indices + filter + collect for best_idx only]
E --> F[Push AggregatedAttestation]
E --> G[Push selected_proof]
E --> H[Emit metrics with new_covered.len]
F & G & H --> I[covered.extend new_covered]
I --> J[remaining_indices.remove best_idx]
J --> A
Reviews (1): Last reviewed commit: "Avoid unnecessary Vec allocations in gre..." | Re-trigger Greptile
Motivation
The
extend_proofs_greedilyfunction (introduced in #251) uses a greedy set-cover algorithm to select aggregated signature proofs that maximize validator coverage. On each iteration of the selection loop, it was allocating aVec<u64>for every remaining candidate proof just to compare coverage counts via.len(). Only the winning proof's coverage Vec is actually needed -- to extend the covered set and record the attestation count.With N remaining proofs per iteration, this means N-1 unnecessary heap allocations that are immediately discarded. Since this runs on the block production hot path (every 4 seconds), reducing throwaway allocations is worthwhile.
Description
Split the greedy selection into two phases:
.count()(no allocation) for each candidate proof to find the one covering the most uncovered validators.Vec<u64>of newly covered validator IDs only for the winning proof.This trades one extra iteration over the best proof's participant indices for avoiding N-1
Vecallocations per loop iteration.How to Test
make test-- all existing tests pass (no behavioral change)make lint-- clippy clean