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

fix: remove one way branching in bytecode trie #775

Merged
merged 6 commits into from
Jan 28, 2025
Merged

Conversation

agostbiro
Copy link
Member

@agostbiro agostbiro commented Jan 24, 2025

This PR fixes stack overflow on drop in the BytecodeTrie by eliminating one-way branching which produces very deep tries for large contracts. Care has been taken to preserve the implementation details of the previous implementation which the stack trace heuristics rely on.

I moved the BytecodeTrie to a separate module in 7bcb871. The changes to eliminate one-way branching can be reviewed by reviewing 76840d8.

I also added a benchmark for constructing the ContractDecoder, but maybe it'd be better to put this in the tools crate, as running the benchmark requires some manual setup.

Eliminating one way branching resulted in a 5x speed up for constructing the ContractDecoder for the forge-std test suite.

We are benchmarking the ContractDecoder construction instead of the BytecodeTrie construction as the former takes care of constructing the data that goes in the trie and I didn't want to spend time on recreating that logic separately. This does mean however that the benchmark also measures parsing and normalizing the contract metadata, I suspect which now takes the majority of the time.

@agostbiro agostbiro self-assigned this Jan 24, 2025
Copy link

changeset-bot bot commented Jan 24, 2025

⚠️ No Changeset found

Latest commit: bc378e0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@agostbiro agostbiro had a problem deploying to github-action-benchmark January 24, 2025 18:48 — with GitHub Actions Error
@agostbiro agostbiro marked this pull request as draft January 24, 2025 18:48
@agostbiro agostbiro added the no changeset needed This PR doesn't require a changeset label Jan 24, 2025
@agostbiro agostbiro temporarily deployed to github-action-benchmark January 24, 2025 19:02 — with GitHub Actions Inactive
@agostbiro agostbiro had a problem deploying to github-action-benchmark January 27, 2025 18:58 — with GitHub Actions Failure
@agostbiro agostbiro requested a review from a team January 27, 2025 19:08
@agostbiro
Copy link
Member Author

Do we want a changelog item for this? It's not a user reported issue.

@agostbiro agostbiro marked this pull request as ready for review January 27, 2025 19:09
Copy link
Member

@Wodann Wodann left a comment

Choose a reason for hiding this comment

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

I reviewed the commit mentioned in the description of the PR. Overall LGTM. Just one question and one comment

return Some(TrieSearch::LongestPrefixNode(cursor));
// Otherwise the cursor's range is greater than the key's length which means the
// key is a prefix of the match.
None
Copy link
Member

Choose a reason for hiding this comment

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

Is this the case where the key is "foo" and a stored entry is "foobar"?

Wouldn't we still be interested in that case? Or is there a separate function for that?

Copy link
Member Author

@agostbiro agostbiro Jan 28, 2025

Choose a reason for hiding this comment

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

Is this the case where the key is "foo" and a stored entry is "foobar"?

Yes, we'd return None in this case.

Wouldn't we still be interested in that case? Or is there a separate function for that?

Yes, normally this is an interesting result for trie lookups, but for the purposes of the contract identifier, it's a no match, so we're following the previous implementation here by returning None.

Comment on lines 185 to 187
let mut descendants = Vec::with_capacity(node_to_split.descendants.len() + 1);
descendants.extend(node_to_split.descendants.iter().cloned());
descendants.push(new_item.clone());
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this variable is used in this function

Copy link
Member Author

Choose a reason for hiding this comment

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

Uhh that's something I broke when fixing the last bug. Thanks for catching it! Fixed + tested in
bc378e0

}

pub fn criterion_benchmark(c: &mut Criterion) {
let Some(build_info_config) = load_build_info_config().expect("loads build info config") else {
Copy link
Member

Choose a reason for hiding this comment

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

You might be able to avoid the overhead of loading using this API of criterion:

https://bheisler.github.io/criterion.rs/book/user_guide/benchmarking_with_inputs.html

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, could you explain this a bit more? I don't follow.

@agostbiro agostbiro had a problem deploying to github-action-benchmark January 28, 2025 08:11 — with GitHub Actions Failure
@agostbiro
Copy link
Member Author

The seaport benchmark regression is a false positive due to benchmark variability.

@agostbiro agostbiro had a problem deploying to github-action-benchmark January 28, 2025 10:25 — with GitHub Actions Failure
@agostbiro agostbiro removed the no changeset needed This PR doesn't require a changeset label Jan 28, 2025
@agostbiro agostbiro added this pull request to the merge queue Jan 28, 2025
Merged via the queue into main with commit 2b9b805 Jan 28, 2025
37 of 38 checks passed
@agostbiro agostbiro deleted the fix/bytecode-trie branch January 28, 2025 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants