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

[suggestion]: MerkleTree optimization #2396

Closed
s8sato opened this issue Jun 25, 2022 · 8 comments
Closed

[suggestion]: MerkleTree optimization #2396

s8sato opened this issue Jun 25, 2022 · 8 comments
Assignees
Labels
iroha2-dev The re-implementation of a BFT hyperledger in RUST Optimization Something isn't working as well as it should

Comments

@s8sato
Copy link
Contributor

s8sato commented Jun 25, 2022

Explore tradeoffs between time and space efficiency (skipping hash calculations v.s. having no wrappers).

Currently, a node is represented as Option<HashOf<T>>.
This can benefit from skipping empty (None) nodes in the hash calculation from the leaves to the root of the tree, while increases the memory footprint by the amount of the wrapper.

HashOf<T> may be an alternative for the node representation.
In that case, an empty node can be represented as Hash::zeroed().typed().
This is advantageous in terms of memory footprint, while increases the hash calculation cost since there is no distinction in types between empty and non-empty nodes

@s8sato s8sato added iroha2-dev The re-implementation of a BFT hyperledger in RUST Optimization Something isn't working as well as it should labels Jun 25, 2022
@Erigara Erigara self-assigned this Dec 12, 2022
@appetrosyan
Copy link
Contributor

appetrosyan commented Dec 12, 2022

There's a few points. This is a consequence of years of experience in optimising software, so I'd err on the side of caution.

This is advantageous in terms of memory footprint, while increases the hash calculation cost since there is no distinction in types between empty and non-empty nodes

Only at the language level. In reality most CPUs ignore the branch conditions execute the likely branch, and only backtrack to the condition once the branch misprediction takes place.

If we don't use monadics, and instead operate on zeroed, we might do what the compiler would have done anyway. Except now, it's explicit.

This can benefit from skipping empty (None) nodes in the hash calculation from the leaves to the root of the tree, while increases the memory footprint by the amount of the wrapper.

Also no.

I'd argue that if Hash::zeroed()::typed was made an invalid Hash, and we refactored everywhere to use Option<HashOf<T>>, we'd actually have the exact same assembly thanks to the niche optimisation. The internal representations of HashOf<T> where zeroed is valid and Option<HashOf<T>> where HashOf::<T>::zeroed() is invalid, is guaranteed by the compiler.

In other words we can have the cake and eat it too in terms of memory layout savings.

A similar thing is done to the built-in type NonZeroUsize which is used to represent pointers.

@appetrosyan
Copy link
Contributor

I think that we should explore the approach of making HashOf be non-zero, marking that value as a niche, and refactoring every place where we produce HashOf::<T>::zeroed to accept Option<HashOf<T>>.

@appetrosyan appetrosyan changed the title MerkleTree optimization [suggestion]: MerkleTree optimization Dec 12, 2022
@Erigara
Copy link
Contributor

Erigara commented Dec 12, 2022

i and @mversic recently have discussed that having niche value will be nice for ffi too (here).

@Erigara
Copy link
Contributor

Erigara commented Dec 12, 2022

However i'm not sure how we can make HashOf non-zero, since it's backed by [u8;32].

@appetrosyan
Copy link
Contributor

That’s the clever bit. if it were u8;16 you could just pack that into a single u128. That trick doesn’t quite work with u8: 32 but there must be a way.

@appetrosyan
Copy link
Contributor

Actually, we could pack it into two u128s, and mark the less significant bits as nonzero. We then set the LSB to 1 for all valid hashes. If we want to be pedantic, this sacrifices one bit for all other hashes.

@Erigara
Copy link
Contributor

Erigara commented Dec 12, 2022

We should be careful as u128/i128 is not fully supported in FFI (#2622)

@Erigara
Copy link
Contributor

Erigara commented Dec 16, 2022

With merge of #3012 there is no need to remove Option since Option<Hash> is niche optimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
iroha2-dev The re-implementation of a BFT hyperledger in RUST Optimization Something isn't working as well as it should
Projects
Archived in project
Development

No branches or pull requests

3 participants