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

Lambda inlining optimization #353

Open
wants to merge 87 commits into
base: master
Choose a base branch
from

Conversation

TimothyGeerkensGuardsquare
Copy link

@TimothyGeerkensGuardsquare TimothyGeerkensGuardsquare commented Aug 3, 2023

Added lambda inlining for Kotlin lambdas passed directly as an argument or stored in a variable then passed as an argument. It currently cannot inline lambdas that are passed to a method and then used as an argument to a method called from the first method. We currently do not inline from final fields, nor from lambdas returned from methods. So we just handle the most basic cases for now.

  • Retrace
  • Only inline lambdas based on certain rules
  • Start to end example with multiple steps in bytecode
  • Replace aconst_null with getstatic call to the lambda class
  • Add enable/disable option for the lambda inlining

Co-authored-by: Maarten Steevens <maarten.steevens@gmail.com>
Co-authored-by: Timothy Geerkens <timothy.geerkens@guardsquare.com>
@rubenpieters
Copy link
Contributor

Can you provide some timings for a few applications: how big the applications are and how much time this inlining pass takes?

@MaartenS11
Copy link

So currently the only real world application we tested it on is the klox compiler, it contains many lambda usages so it seemed like a good test. By just inlining the more basic lambdas we are currently focusing on we were able to get a ~10% performance increase. So this is without running proguard on it so only lambda inlining. Inlining the lambdas took around 30s.

Benchmark 1: java -jar result.jar wc.lox -o Test.jar
  Time (mean ± σ):      1.831 s ±  0.107 s    [User: 6.152 s, System: 0.238 s]
  Range (min … max):    1.717 s …  2.025 s    10 runs
 
Benchmark 1: java -jar test-files/klox.jar wc.lox -o Test2.jar
  Time (mean ± σ):      2.012 s ±  0.173 s    [User: 6.715 s, System: 0.238 s]
  Range (min … max):    1.822 s …  2.345 s    10 runs

We will try do run it on some more real world applications including android apps.

We ran the inliner on a real world application and found a lot of issues, this commit aims to resolve those.
/**
* Specifies whether lambdas should be inlined.
*/
public boolean lambdaInlining = true;

Choose a reason for hiding this comment

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

I'm not sure whether we would like to have this enabled by default but rather have this as an opt-in functionality. For opt-in I believe it should be false here.

base/src/main/java/proguard/ProGuard.java Show resolved Hide resolved
base/src/main/java/proguard/optimize/inline/Util.java Outdated Show resolved Hide resolved
""".trimIndent()
)

// TODO: Currently broken due to a bug in the proguard-core test utils

Choose a reason for hiding this comment

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

Is this still the case, I thought it was fixed?

Choose a reason for hiding this comment

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

It is fixed but that version of proguard core is unreleased. This pr makes use of the latest released build of proguard core which is 9.0.10.

Made LambdaUsageHandler a class
Put loadJar inside testUtil.kt and removed LambdaLocator.Util
Added comments in CastRemover
We can do some of these changes because we don't modify the code in this version
Refactored LambdaImplementationVisitor visitProgramMethod
Cleaned commented code in NullCheckRemover
Removed unused class
Removed staircase in LocalUsageRemover
Fixes from inlining lambdas in the klox compiler
added a logger in lambda locator
… descriptor

This is better because it fixes the case where a lambda is attempted to be inlined into a method that takes object as argument which is then later casted back into a lambda.
@TimothyGeerkensGuardsquare
Copy link
Author

Can you provide some timings for a few applications: how big the applications are and how much time this inlining pass takes?

Current performance on a few android applications :

ANOTHERpass :

  • Inlining pass runtime = 4078ms
  • Number of lambdas found = 24
  • APK size before proguard = 5.3MB
  • APK size after proguard = 3.2MB

OneList :

  • Inlining pass runtime = 4651ms
  • Number of lambdas found = 31
  • APK size before proguard = 5.2MB
  • APK size after proguard = 3.0MB

Antimine :

  • Inlining pass runtime = 47651ms
  • Number of lambdas found = 158
  • APK size before proguard = 10.1MB
  • APK size after proguard = 5.0MB

@MaartenS11
Copy link

MaartenS11 commented Aug 11, 2023

We noticed that proguard-core and proguard both have a version of the LambdaExpressionCollector, I guess it was moved over into proguard-core at some point but never removed here? They are also in the same package which can cause Intelij to understandably get a little confused sometimes.

@TimothyGeerkensGuardsquare TimothyGeerkensGuardsquare marked this pull request as ready for review August 11, 2023 12:25
(Location can maybe be changed not sure exactly where we should put it, putting it in the official documentation seems incorrect this is not really something a proguard user should know it's more of a developer oriented piece of documentation.)
…ferences have to be updated, just the classes involved in the inlining process

This should improve performance a bit.
This should also improve performance a bit.
…lassPool

This should again improve performance of the pass a bit.
@sonarcloud
Copy link

sonarcloud bot commented Aug 13, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 20 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

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.

5 participants