-
-
Notifications
You must be signed in to change notification settings - Fork 3
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
Add basic optimizations #249
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could move the optimizations into compiler::mir::optimize
to make it clear that these operate on the MIR
Co-authored-by: Jonas Wanke <contact@jonas-wanke.com>
Co-Authored-By: Jonas Wanke <contact@wanke.dev>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could move the optimizations into compiler::mir::optimize
to make it clear that these operate on the MIR.
I viewed all files of the first 100 commits (until “Add optimize_opinionated method” (c12b19b
))
Co-Authored-By: Jonas Wanke <contact@wanke.dev>
Co-Authored-By: Jonas Wanke <contact@wanke.dev>
Regarding moving the |
Co-Authored-By: Jonas Wanke <contact@wanke.dev>
Depends on #246
This PR adds optimizations.
The problem
Before,
use
expressions lead to exponential code blowup: In the Core package, we had ause "equality"
import in several files. During runtime, this results in multiple instantiations of the equality module. Especially for taller hierarchies of modules, this can easily lead to an exponential amount of code being executed.We thought about only instantiating modules the first time they are imported (and only returning a reference the other times), but this could lead to other weird behavior. For example, if a package Foo exports something mutable (for example, something containing a channel), this could happen:
Essentially, Bar could
use "Foo"
and send something in the channel in top-level scope, Baz coulduse "Foo"
and receive from that channel to see if Bar was imported before. This contradicts our intuition about how a functional language should behave.The solution
We could instead perform a handful of optimizations such as common-subtree-elimination statically, before executing code. Take the following code:
Each time a module is first encountered, its imports are inlined and it is optimized. After optimizations, the modules would look like this:
Note that in each optimized module, every constant only exists once. Because most things that libraries define are constants (aka strings, ints, and (most importantly) functions with known captured arguments) as opposed to non-constants such as channels, this dramatically reduces the amount of code to be executed.
The implementation
To achieve this, I introduced a new intermediate representation, the MIR (Mid-Level Intermediate Representation), between the HIR and LIR. This representation is modeled so that it makes writing optimization passes easy. Also, some HIR expressions such as needs and compiler error nodes are desugared. Responsibilities of calls are explicitly tracked.
These are the implemented optimizations:
use
expressions.use
expressions with known targets by inlining the optimized MIR of the module.Take this code example:
It results in the following MIR (comments added by me):
Note: The
moduleStarts
andmoduleEnds
expressions can be optimized away, but I thought the PR is big enough already (sorry about that btw).Performance
In release mode, the optimization takes 6.3 seconds. That's definitely faster than
use "Core"
took before, but it's still a long way from performant. A quick look at the flamegraph revealsvisit_with_visible
dominates the runtime, which makes sense. In particular, to communicate all visible expressions to the visitor, I think all expressions are cloned. I'll have a look at that in another PR.Still, the running of the "Hello, world!" program itself is now really fast. After maybe some performance tuning, AOT compilation might be interesting next.