Skip to content

Running | Running the Plugin

Vlad Ureche edited this page Jun 12, 2014 · 10 revisions

This description corresponds to the version of the Miniboxing plugin in the DRT Virtual Machine. In the virtual machine, the minibooxing plugin is installed in ~/Workspace/miniboxing-plugin.

To start, please launch the Terminal form the Links directory on the desktop. The terminal allows you to access the mb-scalac and mb-scala scripts, which are the equivalents of scalac and scala with the miniboxing plugin activated.

When you start the Terminal, you should be able to fire up all of scalac (the scala compiler) and scala (the scala interpreter and executor) and their miniboxing-enabled counterparts:

acc@acc-vm:~$ scala # same for mb-scala, you won't notice the difference
Welcome to Scala version 2.11.1 (OpenJDK Client VM, Java 1.7.0_09).
Type in expressions to have them evaluated.
Type :help for more information.

scala> 
acc@acc-vm:~$ scalac # same for mb-scala, you won't notice the difference
Usage: scalac <options> <source files>
...

Before we start, please update the project, as there's always something we overlooked or could be improved:

acc@acc-vm:~$ cd Workspace/miniboxing-plugin/
acc@acc-vm:~/Workspace/miniboxing-plugin$ git pull origin wip
...
acc@acc-vm:~/Workspace/miniboxing-plugin$ sbt clean compile package
...

Now, let us create a small program to use miniboxing on:

acc@acc-vm:~$ cd ~/Workspace/miniboxing-plugin/sandbox
acc@acc-vm:~/Workspace/miniboxing-plugin/sandbox$ 

Now use your favorite editor to imput the followning code in a file named C.scala:

class C[@miniboxed T](val t: T)

After you saved the file, return to the prompt and type:

$ mb-scalac -P:minibox:log C.scala 
Specializing class C...

  // interface:
  abstract trait C[T] extends Object {
    val t(): T                                                            
    val t_J(val T_TypeTag: Byte): T @storage[Long]                        
  }

  // specialized class:
  class C_J[Tsp] extends C[Tsp] {
    def <init>(val C_J|T_TypeTag: Byte,t: Tsp @storage[Long]): C_J[Tsp]    // is a specialized implementation of constructor C
    private[this] val C_J|T_TypeTag: Byte                                  // no info
    private[this] val t: Tsp @storage[Long]                                // is a specialized implementation of value t
    val t(): Tsp                                                           // is a forwarder to the specialized member
    val t_J(val T_TypeTag: Byte): Tsp @storage[Long]                       // is a setter or getter for value t
  }

  // specialized class:
  class C_L[Tsp] extends C[Tsp] {
    def <init>(t: Tsp): C_L[Tsp]                                           // is a specialized implementation of constructor C
    private[this] val t: Tsp                                               // is a specialized implementation of value t
    val t(): Tsp                                                           // is a setter or getter for value t
    val t_J(val T_TypeTag: Byte): Tsp @storage[Long]                       // is a forwarder to the specialized member
  }

Here we see the interface C[T], which is the top class and its two miniboxed variants, C_J for primitive types and C_L for reference types. One can easily spot a difference between the two: in C_J there are two fields, t of type Tsp @storage[Long] and T_TypeTag of type Byte. These correspond to the miniboxed value. As we will see later, Tsp @storage[Long] will be later transformed into Long. On the other hand, C_L only contains a reference of type Tsp. In the attached comments, we can see exactly what each member is supposed to do. This comes from the way the specialization (and miniboxing) transformations work: they first record the metadata for the transformation and only later they perform the actual tree rewriting. But making sense of this metadata can be tricky: in each class we have two members called t. To overcome this, we can use the -uniqid option, which assigns a unique id to each symbol:

$ mb-scalac -P:minibox:log C.scala -uniqid
Specializing class C#7762...

  // interface:
  abstract trait C#7762[T#7774] extends Object#124 {
    val t#7861(): T#7774                                                  
    val t_J#15701(val T_TypeTag#15702: Byte#2435): T#7774 @storage#15699[Long#2321]
  }

  // specialized class:
  class C_J#15709[Tsp#15710] extends C#7762[Tsp#15710] {
    def <init>#15716(val C_J|T_TypeTag#15720: Byte#2435,t#15719: Tsp#15710 @storage#15699[Long#2321]): C_J#15709[Tsp#15710] // is a specialized implementation of constructor C#7863
    private[this] val C_J|T_TypeTag#15711: Byte#2435                       // no info
    private[this] val t#15713: Tsp#15710 @storage#15699[Long#2321]         // is a specialized implementation of value t#7862
    val t#15712(): Tsp#15710                                               // is a forwarder to the specialized member
    val t_J#15714(val T_TypeTag#15715: Byte#2435): Tsp#15710 @storage#15699[Long#2321] // is a setter or getter for value t#15713
  }

  // specialized class:
  class C_L#15729[Tsp#15730] extends C#7762[Tsp#15730] {
    def <init>#15735(t#15737: Tsp#15730): C_L#15729[Tsp#15730]             // is a specialized implementation of constructor C#7863
    private[this] val t#15732: Tsp#15730                                   // is a specialized implementation of value t#7862
    val t#15731(): Tsp#15730                                               // is a setter or getter for value t#15732
    val t_J#15733(val T_TypeTag#15734: Byte#2435): Tsp#15730 @storage#15699[Long#2321] // is a forwarder to the specialized member
  }

With -uniqid, references became more explicit: each symbol in the compiler receives a unique id which is displayed after its name. Now we can see exactly which name refers to which symbol. So far, we have the two classes and we can see both define t and t_J. In the primitive type class C_J, def t_J is a getter for the miniboxed value and def t is a forwarder which boxes. In the other class, C_L we have def t as a getter while def t_J does unboxing. We will see this pattern recurring many times in the transformation.

So far we've seen the metadata, which miniboxing displays for us when adding the -P:minibox:log argument. But we might also want to see the intermediate representation, which is build based on the metadata. To do so, we use -Xprint:minibox-inject:

$ mb-scalac -Xprint:minibox-inject C.scala 
[[syntax trees at end of            minibox-inject]] // C.scala
package <empty> {
  abstract trait C[@miniboxed T] extends Object {
    <stable> <accessor> <paramaccessor> def t(): T;
    <stable> <accessor> def t_J(T_TypeTag: Byte): T @storage[Long]
  };
  class C_J[Tsp] extends Object with C[Tsp] {
    def <init>(C_J|T_TypeTag: Byte, t: Tsp @storage[Long]): C_J[Tsp] = {
      C_J.super.<init>();
      ()
    };
    <paramaccessor> private[this] val C_J|T_TypeTag: Byte = _;
    <paramaccessor> private[this] val t: Tsp @storage[Long] = _;
    <stable> <accessor> <paramaccessor> def t(): Tsp = C_J.this.t_J(C_J.this.C_J|T_TypeTag);
    <stable> <accessor> def t_J(T_TypeTag: Byte): Tsp @storage[Long] = C_J.this.t
  };
  class C_L[Tsp] extends Object with C[Tsp] {
    def <init>(t: Tsp): C_L[Tsp] = {
      C_L.super.<init>();
      ()
    };
    <paramaccessor> private[this] val t: Tsp = _;
    <stable> <accessor> <paramaccessor> def t(): Tsp = C_L.this.t;
    <stable> <accessor> def t_J(T_TypeTag: Byte): Tsp @storage[Long] = C_L.this.t()
  }
}

As promised, the references to Tsp @storage[Long] are later transformed to Long:

$ mb-scalac -Xprint:minibox-commit C.scala 
[[syntax trees at end of            minibox-commit]] // C.scala
package <empty> {
  abstract trait C[@miniboxed T] extends Object {
    <stable> <accessor> <paramaccessor> def t(): T;
    <stable> <accessor> def t_J(T_TypeTag: Byte): Long
  };
  class C_J[Tsp] extends Object with C[Tsp] {
    def <init>(C_J|T_TypeTag: Byte, t: Long): C_J[Tsp] = {
      C_J.super.<init>();
      ()
    };
    <paramaccessor> private[this] val C_J|T_TypeTag: Byte = _;
    <paramaccessor> private[this] val t: Long = _;
    <stable> <accessor> <paramaccessor> def t(): Tsp = MiniboxConversions.this.minibox2box[Tsp](C_J.this.t_J(C_J.this.C_J|T_TypeTag), C_J.this.C_J|T_TypeTag);
    <stable> <accessor> def t_J(T_TypeTag: Byte): Long = C_J.this.t
  };
  class C_L[Tsp] extends Object with C[Tsp] {
    def <init>(t: Tsp): C_L[Tsp] = {
      C_L.super.<init>();
      ()
    };
    <paramaccessor> private[this] val t: Tsp = _;
    <stable> <accessor> <paramaccessor> def t(): Tsp = C_L.this.t;
    <stable> <accessor> def t_J(T_TypeTag: Byte): Long = MiniboxConversions.this.box2minibox_tt[Tsp](C_L.this.t(), T_TypeTag)
  }
}

This will be dicussed in detail in a more advanced tutorial.

You probably noticed the calls to MiniboxConversions.this.box2minibox and its reverse, MiniboxConversions.this.minibox2box. These convert from one representation to the other. The compiler flags you may be interested in are:

  -Xshow-phases                show the compiler phases (notice how mb-scalac includes minibox while scalac does not)
  -Xprint:<phase>              print trees at the end of <phase>
  -uniqid                      prepend symbol id to the symbol name, to distinguish between symbols
  -P:minibox:log               log miniboxing signature transformations
  -P:minibox:hijack            hijack the @specialized(...) notation for miniboxing
  -P:minibox:two-way           generate variants for long and double instead of just long

Finally, let's run a program that is transformed using miniboxing:

class MBme[@miniboxed U, @miniboxed V] {
  def clazz = this.getClass.toString
}
  
object Test {
  def main(args: Array[String]): Unit = {
    println(new MBme[Int, Int].clazz)
    println(new MBme[Int, String].clazz)
    println(new MBme[String, Int].clazz)
    println(new MBme[String, String].clazz)
  }
}

Compile the file with:

  mb-scalac MBme.scala

And run the Test class with:

  mb-scala Test

Expected output:

class MBme_JJ
class MBme_JL
class MBme_LJ
class MBme_LL

Checking the Compiled Code Size

In the Miniboxing OOPSLA13 paper we included bytecode size benchmarks, so you can check the miniboxed class size. This will be done with the miniboxed class:

$ cd ~/Workspace/miniboxing-plugin/sandbox
$ mb-scalac -optimize ../tests/benchmarks/src/miniboxing/benchmarks/minibox/MBResizableArray.scala 
$ find ./miniboxing/benchmarks/minibox/MBResizableArray* -name "*.class" -ls | awk '{total += $7} END {print total}'
12044

$ scalac -optimize ../tests/benchmarks/src/miniboxing/benchmarks/generic/ResizableArray.scala 
$ find ./miniboxing/benchmarks/generic/ResizableArray* -name "*.class" -ls | awk '{total += $7} END {print total}'
4505

$ mb-scalac -optimize ../tests/benchmarks/src/miniboxing/benchmarks/minibox/MBList.scala 
$ find ./miniboxing/benchmarks/minibox/MBList* -name "*.class" -ls | awk '{total += $7} END {print total}'
9083

$ scalac -optimize ../tests/benchmarks/src/miniboxing/benchmarks/generic/List.scala 
$ find ./miniboxing/benchmarks/generic/List* -name "*.class" -ls | awk '{total += $7} END {print total}'
3146

In our case, these reported 12044 bytes for MBResizableArray and 9083 for MBList, which are better than the sizes reported in the paper, 24.5 Kbytes for MBResizableArray and 11.5 Kbytes for MBList.

Next Steps

You can continue with the following resources: