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

Avoid unnecessary linked list traversals in MethodLinker. #133

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

LlamaLad7
Copy link

@LlamaLad7 LlamaLad7 commented Jan 3, 2025

The chains of method links can get surprisingly large and a huge amount of time can be wasted repeatedly traversing them.
We can cut down most of this time by flattening the chain each time we traverse it, pointing every element straight to the end.
The chains themselves are assumed to be an implementation detail of this class, in which case it's fine to flatten them at any time.
Some profiling from building one of my projects with ProGuard:
Before:
image
After:
image

I'd imagine that there are ways to avoid linked lists at all in this linking process, but this change is minimally invasive to the external API and the speedup is very large.

The chains themselves are assumed to be an implementation detail of this class, in which case it's fine to flatten them at any time.
@rubenpieters
Copy link
Contributor

Hey @LlamaLad7 ! Thanks for the PR, your approach looks like a nice improvement. We will be looking at the suggestion further, but we will need to do some additional testing first to ensure the change is safe to apply also in our internal projects using ProGuardCore.
We would also be interested in reproducing the results you mentioned, do you mind sharing which project you tested the runtime improvements on?

@LlamaLad7
Copy link
Author

LlamaLad7 commented Jan 8, 2025

@rubenpieters Unfortunately the project is proprietary, so I can't share it. We are using obfuscation, hence the work in MethodLinker, but apart from that I think the only significant thing is the size of our build, since we have ~25,000 program classes and ~75,000 library classes. ProGuard deals with a project of this scale surprisingly well in most aspects, but about 50% of our build time is spent just traversing these chains.

I've made a slightly contrived example here (https://github.com/LlamaLad7/slow-proguard-example) with a lot of methods in a large inheritance chain. MethodLinker spends about 15 seconds traversing the chains without my change, and about 1 second with my change. Hopefully this helps.

@rubenpieters
Copy link
Contributor

@LlamaLad7 The reproducing project you shared is indeed helpful in reproducing the effects you describe, so thanks for that! We are planning on having a further look on the impact of this in our internal projects, as we haven't gotten to it yet. Mainly to ensure we don't introduce any unexpected side effects as mentioned before.

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