This page will explain how to use the
@miniboxed anntotation. Miniboxing is a source-to-source transformation that speeds up generic classes and methods when used with primitive value types, such as
Before you start using the plugin, please read this one page. It will explain how to use the
@miniboxed annotation, so your program will be optimized properly. Although we don’t recommend it, you can skip through to the example sbt project if you don’t plan to use miniboxing in your project right now.
Here are a few quick facts about the
- The miniboxing transformation is activated by annotating type parameters with
@miniboxed, for both classes and methods:
- Miniboxing can speed up monomorphic code:
In this case, the miniboxing transformation will encounter an instantiation of
C with type argument
Int, and will rewrite the
new instance creation and the
foo method call to use direct value types, thus speeding up execution and avoiding boxing.
- Miniboxing can’t speed up generic code if the type parameters are not annotated with
Since the type parameter
T of method
bar_bad is not marked as
@miniboxed, the erasure transformation will kick in instead of miniboxing. Although the
C class is transformed by miniboxing, erasure won’t perform any program rewriting to improve performance. Therefore, regardless of whether
C is optimized by miniboxing or not, the performance for
bar_bad will stay the same. This is intended, since miniboxed code needs to be compatible with erasure-generated code, at the cost of lower performance.
- To recover performance for the previous case, you can annotate the method parameter as
Since the type parameter
bar_awesome is marked as
@miniboxed, the method will have two versions: an optimized version for value types and the compatible, erasure-based, slow version of the method.
While reading the quick facts, you might have wondered whether you can explicitly instantiate the optimized variants of classes or call optimized methods created by miniboxing. Unfortunately the answer is no, you can’t, as this would compromise the type safety of the language. Therefore, when using the miniboxing plugin, you will have to pay special attention to making sure the hot methods in your code and the commonly used data structures are rewritten by the compiler to use the optimized variants. Luckily, this is not hard at all, you only need to remember three cases you may find yourself in.
But before we go on to the cases, we need to explain the concept of an optimized trace. Let’s imagine you’re writing a generic k-means algorithm, and the main iteration calls several methods. Ideally, you want the iteration itself and the methods it calls to be the miniboxing-optimized versions. Knowing the optimized methods have a special marking in their name, how could you check your algorithm is using them? One way to do this would be to throw an exception in one of the methods and check the last 2 stack frames – if the methods have the special miniboxed mark, they are the optimized ones. We call this an optimized trace.
Optimized traces are series of miniboxing-optimized methods that call each other. Let’s see an example stack trace, knowing
_J is one of the markings for miniboxing-optimized methods:
In the stack trace above, all our algrithm is miniboxing-optimized, since
sum_J all have the miniboxing prefix
_J, so our algorithm should run efficiently. It is not necessary to have all the program optimized, just the critical parts that execute for a long time. So your task, as a programmer, is to make sure the critical traces in your program only contain miniboxing-marked methods.
That being said, here are the three types of calls that make up the trace:
- optimized trace initiators, which start an optimized trace: method calls where the type argument is a value type and the type parameter is annotated with
- optimized trace inhibitors, which end an optimized trace: calls to methods whose type parameter is not marked as
- optimized trace propagators, which continue an optimized trace, but only if called from an optimized trace
An Example, Finally!
Knowing these three cases enables you to make sure the entire trace is optimized. Let’s take an example:
As before, the
_J suffix is an indication that the miniboxing-optimized version of the method was executed. We can say the optimized trace was initiated by using a
@miniboxed-annotated method with a value type.
Now, let’s test if we don’t initiate the optimized trace:
As expected, no more
_Js, so we’re using the erasure-based versions! That’s because
String is not a value type, so the optimized trace was not initiated at all. This shows that
baz are just propagators: if they are called as part of an optimized trace, they are optimized, but if the optimized trace is not initialized somewhere, the erasure-based slow versions are invoked.
Now, let’s see an inhibitor: let us remove the
@miniboxed annotation from
bar’s type parameter
T, in a second object,
The result shouldn’t surprise you: the optimized trace was inhibited, since method
bar does not have an optimized version at all (e.g. the bytecode for object
Test2 doesn’t contain
bar_n_J at all). But the thing to look out for is that erasure-based versions of the code will always call the erasure-based versions of other methods. This is why, although
foo has an optimized version, it won’t be called.
Classes as Data Structures
We will now focus more on classes. Classes encapsulate both data and code, but, in order to simplify things, we will first consider only data. As you probably figured out already, miniboxing transformed methods have two versions: a generic, erasure-compatible one, and an optimized, value type-carrying one. Pretty much the same happens to a class: it will be transformed into two classes, a generic, erasure-compatible one, and an optimized one:
A very rough approximation of the transformed code for
class D is:
The reason classes are duplicated is because they store data: since references and value types are incompatible on the Java Virtual Machine, we have to duplicate the class, such that the object layout is adapted for the type it is carrying.
The optimized version of the class is created by the instantiation:
new D[U](...). The generic version,
D_L is used, except for two specific cases, when the instantation is rewritten to create the optimized version
- if the type argument
Uis known and is a value type:
new D[Int](3), which is rewritten to
- if in the class or method containing type parameter
Uis marked with
@miniboxed, then the optimized version of that class or method will use the optimized version of the class:
This corresponds to some extent to the previous 3 cases:
- optimized trace initiators always create the optimized instance of the class
- optimized trace propagators create the optimized instance only in their optimized version (
- optimized trace inhibitors always create the generic instance of the class
Classes as Optimized Trace Initiators
Finally, we come to the last point: the code encapsulated in classes. We won’t go into details on how this is achieved, but calling a method on an optimized instance of a class (
z_opt in this case) behaves like an initiator, while calling a method on a generic instance of a class (
z_gen) behaves as an inhibitor:
For an alert reader, this section probably raised many more questions than it answered. The miniboxing encoding page explains the internals of miniboxing, and will answer all your questions, both on the theory and the implementation. Still, as a programmer using miniboxing, what you already know is enough to make sure your performance-critical code is optimized, and you can jump to the example project page.
Skipping one step ahead, the
-P:minibox:log flag passed to the Scala compiler will output all details on how classes are transformed and how their members are affected:
Comments are always welcome! But to make the best use of them, please consider this:
- If you have questions or feedback regarding the content of this page, please leave us a comment!
- If you have general questions about the miniboxing plugin, please ask on the mailing List.
- If you found a bug, please let us know on the github issue tracker.
Thanks! Looking forward to your messages!
comments powered by Disqus