Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

utils/mmr: safety & performance rework #274

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft

Conversation

Zk2u
Copy link
Contributor

@Zk2u Zk2u commented Sep 6, 2024

Description

For my first day I thought I'd tackle something small that didn't need much contextual knowledge so I've been reworking the MMR port that @voidash worked on - mostly cleaning up but optimizing where possible.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature/Enhancement (non-breaking change which adds functionality or enhances an existing one)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor

Checklist

  • I have performed a self-review of my code.
  • I have commented my code where necessary.
  • I have updated the documentation if needed.
  • My changes do not introduce new warnings.
  • I have added tests that prove my changes are effective or that my feature works.
  • New and existing tests pass with my changes.

Related Issues

None

also changed some variable names and added some comments, ensured test worked with new compact model
@Zk2u Zk2u requested a review from delbonis September 6, 2024 16:01
@Zk2u Zk2u self-assigned this Sep 6, 2024
@Zk2u Zk2u added enhancement New feature or request refactor Reworking code to improve structure or performance labels Sep 6, 2024
Copy link

codecov bot commented Sep 6, 2024

Codecov Report

Attention: Patch coverage is 89.06250% with 7 lines in your changes missing coverage. Please review.

Project coverage is 58.17%. Comparing base (f83eac5) to head (8fcb8ff).

Files with missing lines Patch % Lines
crates/util/mmr/src/lib.rs 89.06% 7 Missing ⚠️
@@            Coverage Diff             @@
##           master     #274      +/-   ##
==========================================
- Coverage   58.23%   58.17%   -0.06%     
==========================================
  Files         160      160              
  Lines       15304    15309       +5     
==========================================
- Hits         8912     8906       -6     
- Misses       6392     6403      +11     
Files with missing lines Coverage Δ
crates/util/mmr/src/lib.rs 94.58% <89.06%> (-1.61%) ⬇️

... and 1 file with indirect coverage changes

@Zk2u Zk2u requested review from bewakes and voidash September 6, 2024 16:14
@Zk2u Zk2u changed the title utils/mmr: rework, optimisations/cleanup utils/mmr: rework of CompactMmr Sep 6, 2024
@Zk2u
Copy link
Contributor Author

Zk2u commented Sep 6, 2024

Edit: this may be a bigger PR than expected as the original MMR implementation was alloc free but this ported version doesn't have any bound checks on the number of elements (i.e if you allocate for 13 peaks, you're limited to 2^(13-1) = 4,096 elements and adding another leaf after this will (I believe) result in a panic in 103-111). We should either be returning errors or changing the peaks to be a vector so we can allocate further

Copy link
Member

@storopoli storopoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments.
If we were not so pressed for deliverables I would definitely try to nerd-snipe whomever would be interested in adding some cargo bench stuff throughout the express codebase...

crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
let num_to_add = required - peaks.len();
peaks.reserve_exact(num_to_add);
// note we add in a loop so we don't need to make 2 allocs
// (the ZEROs will be stack allocated... i think)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// (the ZEROs will be stack allocated... i think)
// (the ZEROs might be be stack allocated...)

Unpersonal.

Also I don't understand the note.
On one sense, yes ZERO is not only const but also Sized, hence it can be trivially stack-allocated by the compiler.
However, the peaks is a Vec and MAYBE the compiler can totally infer the total size and any possible interactions at compile time, but there might be no such guarantees at run time.
Hence, I would not be surprised if peaks is heap-allocated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK Vecs' sizes cannot be inferred at build time, they're purely allocated at runtime.

What I mean is that instead of a vec![ZERO; num_to_add] then extending peaks (which correct, is heap allocated, all Vecs are) which would incur another allocation - once when initializing the vector then a second allocation to peaks when extending from the slice - we can avoid the initialization entirely by using a loop:

(0..num_to_add).for_each(|_| peaks.push(ZERO))

Where each iteration of the loop puts a ZERO on the stack, then memcpy's it to the end of peaks. This avoids the second allocation required if we were to initialize another vector then extend from it.

crates/util/mmr/src/lib.rs Outdated Show resolved Hide resolved
@Zk2u
Copy link
Contributor Author

Zk2u commented Sep 6, 2024

Thanks for comments I'll reply in a sec. Well, I'm not sure how useful I'll be contributing to the devnet in the short time until deadline so if you want me to focus on that I probably can while I learn more about the high/mid level architecture.

@Zk2u Zk2u changed the title utils/mmr: rework of CompactMmr utils/mmr: safety & performance rework Sep 6, 2024
Copy link
Contributor

@delbonis delbonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly this will just automatically increase the capacity now if we try to add another entry while at capacity?

.collect(),
element_count: self.element_count,
peaks: match min_peaks {
0 => vec![ZERO],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to tweak the representation on the decode side so an empty compact MMR has no peaks at all and this can just be an empty buffer.

Comment on lines +41 to +46
/// Returns the minimum peaks needed to store an MMR of `element_count` elements
#[inline]
fn min_peaks(element_count: u64) -> usize {
match element_count {
0 => 0,
c => c.ilog2() as usize + 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should split this out into a toplevel function so we don't have to call it with Self::.

Comment on lines 12 to 14
/// Compact representation of the MMR that should be borsh serializable easily.
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, Arbitrary)]
pub struct CompactMmr {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be really cool is to write manual borsh serialization so that we don't have to store the length of the peaks list and can infer it from the element count (since it's redundant, it's just popcnt).

@Zk2u
Copy link
Contributor Author

Zk2u commented Sep 9, 2024

This is nowhere near finished and I'm pausing work on the PR until it's needed/I have free time to do it. I think I'll rework the implementation entirely. I'll request another review if i pick this back up/anyone needs it actively.

@storopoli
Copy link
Member

Not needed for devnet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request refactor Reworking code to improve structure or performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants