-
Notifications
You must be signed in to change notification settings - Fork 14
Eliminate unnecessary module loads #16
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
Comments
When inlining from a module, we end up with a null check for the module:
gives
enabling copy propagation eliminates the local holding the module:
We can eliminate the null check if the module constructor has a basic shape. A module load can only be
Example with superclass constructor
Example with early init:
We can look at the constructor bytecode for the module and its super classes. If they all don't have side-effects, the module load cannot be null. However, this is a cross-class optimization, so we have to scope it according to the current optimizer level ( In the same way we can identify modules that don't have any side-effects and eliminate unused loads. NOTE: the idea of storing the module purity in the |
Some modules have fields, so their initializers are not pure. Maybe: special case some of ours (Predef for example, which has
|
Some WIP code for identifying pure modules. Still need to check super classes (the version here just supports Object), and make it depend on the optimizer level (corss-classpath or not)
|
once |
Test case: ArrowAssoc
|
FTR, here are the parts of the Scala.js optimizer where we do that:
|
/link #112 |
A related benchmark that suggests that module load / null checks might be eliminating the benefit of implicit value classes: isaacl/lila-jmh-benchmarks@c8e767b |
I started playing with @lrytz's code above. WIP in retronym:ticket/sd16, with a first step of using a hard-coded list of pure modules, and also adding a new inliner heuristic to inline methods of the form
https://github.com/retronym/sd16-diff It makes a modest 2% reduction in the sum size of classfiles in
I can't measure a difference in compiler performance. Perhaps there are microbenchmarks that would benefit more, like the one I linked above. It might also improve performance in the interpreter or C1-compiled code, but that's just a guess. |
@lrytz Do you think there is a useful / low-risk patch in here for 2.12.3? I'm thinking we should just retarget this to 2.13.x, and backport the most useful improvements under new opt-in optimizer flags if they are profitable. |
Agree about 2.13.x, especially since they are cleanups that don't seem to affect steady state performance. I don't see how your patch causes the diff in your screenshot, we'd have to check that in detail for sure. I agree special-casing a few modules is a pragmatic approach, as we probably cannot ever analyze Predef to be pure (with all its field initializers). We can do that independently of an analysis for other module classes. |
... or should we just close this as out of scope for Scala 2? |
Not sure, Scala 2 has a lot of time to develop :-) |
I think I suggested that because I had approximated that this had behavioural and/or binary impacts that couldn't be made within a 2.13 minor release. But if that's not the case then fixing this might prove to have an amazing benefit. |
Given that we're talking about the inliner, binary compatibility is maybe less of a concern |
After inlining the module instance field loads remain - they have to, the static initializer of the module class may have side effects:
generates
The first
GETSTATIC
is required for the side effect. The second is not. The null checks are not required, module instances are always non-null, but that will be handled by scala-opt/scala#8.Idea: for module classes we could add a bit to the
ScalaInlineInfo
attribute which tells whether the module constructor is known to have no side-effects (the module has only defs, for example).The text was updated successfully, but these errors were encountered: