the only question is whether your apps compile 10% , 15% or 20% faster with the new backend

263 views
Skip to first unread message

Miguel Garcia

unread,
Mar 4, 2013, 5:42:49 PM3/4/13
to scala-i...@googlegroups.com

It's been two weeks of refactoring and bug fixing, refactoring and bug fixing, all for the joy of making scalac faster.

With no bugs in sight, it's a good time to try out the new backend / optimizer

As usual, Getting Started (that's Section 4) and more at http://magarciaepfl.github.com/scala/

In parallel with your testing and benchmarking, I'll branch afresh from master and commit the existing refactored version in a number of self-contained pieces. Please rest assured that the resulting git tip will match what branch GenRefactored2 offers now, no new features are planned until the chopping exercise is over. And the bug fixing rate should be low by now.

If you've been waiting for the new backend to consolidate, there's nothing to gain by waiting longer. Unless you want to inspect the source code in depth.

Cheers,


Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/

Adriaan Moors

unread,
Mar 4, 2013, 6:13:08 PM3/4/13
to scala-i...@googlegroups.com
Those are exciting percentages and great documentation!
I have to say though, why Java!?




--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Adriaan Moors

unread,
Mar 4, 2013, 7:10:33 PM3/4/13
to scala-i...@googlegroups.com
I gave it a spin: https://gist.github.com/adriaanm/5086852

There's about 336 test failures when run (under -neo:o4) on the test suite that's currently in master, so I expect there are still some bugs in sight.
I haven't had time to try under other optimization levels.

Erik Osheim

unread,
Mar 4, 2013, 8:23:35 PM3/4/13
to scala-i...@googlegroups.com
On Mon, Mar 04, 2013 at 02:42:49PM -0800, Miguel Garcia wrote:
> If you've been waiting for the new backend to consolidate, there's nothing
> to gain by waiting longer. Unless you want to inspect the source code in
> depth.

Hi Miguel,

Really interested to try out the new backend optimizer. For some reason
I'm hitting these compile errors:

http://plastic-idolatry.com/erik/error.log

This is using the GenRefactored2 branch (as guided by [1]) after doing
"ant all.clean" and starting fresh. I'm using Java 7 on OSX [2].

Is there anything else I could be forgetting or getting wrong?

Thanks,

-- Erik

[1] http://magarciaepfl.github.com/scala/ (section 4)

[2] java -version says:
java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)

Miguel Garcia

unread,
Mar 5, 2013, 4:53:52 AM3/5/13
to scala-i...@googlegroups.com, er...@plastic-idolatry.com
Erik,

That error output refers to types (eg SignatureChecker) and methods (eg beforeErasure) that were part of an older version of the new optimizer but not anymore.

A clean checkout just worked for me, for completeness:

git clone git://github.com/magarciaEPFL/scala.git GR
cd GR
git checkout -b GenRefactored2 origin/GenRefactored2
ant all.clean && ant


To compiler another compiler version (as in your example) using the new backend / optimizer it's the same steps as usual (briefly, first compile scala runtime, then the library, then reflect and compiler).

However feedback on programs other than the compiler is specially welcome! :)

Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/

Miguel Garcia

unread,
Mar 5, 2013, 5:10:54 AM3/5/13
to scala-i...@googlegroups.com
Let's look at some examples of tests in GenRefactored2 that are different from master. Basically, differences stem from the fact that by default master runs GenASM, while currently the other branch runs GenBCode.

  instrumented/inline-in-constructors
               An example with -optimise in .flags about which the new optimizer will complain, on the grounds that -neo:GenASM hasn't been indicated.
               The test suite at GenRefactored2 lets those tests run with the optimizer they were designed for, via -neo:GenASM -closurify:traditional

  neg/case-collision.check
               Different because with the old optimizer emits three warnings while the new one emits only one warning

All of the other differences have in common that the test suite in master, as is, in fact tests the output of a specific optimizer implementation. Take for example method driver() in test/files/run/t7181.scala which results in:

    881  instructions, when compiled with -neo:o2 -closurify:delegating
    1004 instructions, when compiled with -neo:GenASM -closurify:traditional -optimise

In spite of -optimise running the dead-code phase, it doesn't manage to remove all the JUMPs that the pattern matcher left around, and a partest.BytecodeTest tuned towards the current dead-code elimination might well reject the new optimizer output.

Additionally, ICode tests have been removed because the new optimizer won't produces any ICode.

Now that I'm starting with a new branch with a clean history I'll strive for:
  (a) adding tests specific to the new optimizer
  (b) "sharing" as many as possible tests between old and new backends.

In that new branch GenASM will run by default. The setup chosen for GenRefactored2 had the benefit that it allowed re-using (most of) the test suite.


Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/

Miguel Garcia

unread,
Mar 5, 2013, 5:26:01 AM3/5/13
to scala-i...@googlegroups.com
BTW in test-neo4 you're using -neo:<zero><four> , it should be -neo:o4


Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/



On Tuesday, March 5, 2013 1:10:33 AM UTC+1, Adriaan Moors wrote:

Miguel Garcia

unread,
Mar 5, 2013, 5:30:33 AM3/5/13
to scala-i...@googlegroups.com

Most intra-method optimizations are direct extensions of existing ASM tooling, and thus are written in Java. The Scala-specific optimizations are written in Scala, making by far the bulk of the optimizer.

Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/

Miguel Garcia

unread,
Mar 5, 2013, 9:44:32 AM3/5/13
to scala-i...@googlegroups.com

Regarding test.stability , it works fine (I've tried with -neo:o2 and -neo:o4 but I guess the same applies in other cases). Just run all.clean before quick and strap.

In detail, e.g. for Level 2:

ant all.clean build-neo2 strap-neo2 test.stability

<target name="build-neo2"
  description="Builds the -neo:o2 optimised Scala compiler and library. Executables are in 'build/pack/bin'.">
  <antcall target="build">
    <param name="scalac.args.optimise" value="-neo:o2"/>
  </antcall>
</target>
 
<target name="strap-neo2"
  description="Builds the -neo:o2 optimised Scala compiler and library. Executables are in 'build/pack/bin'.">
  <antcall target="strap.done">
    <param name="scalac.args.optimise" value="-neo:o2"/>
  </antcall>
</target>

Paul Phillips

unread,
Mar 5, 2013, 10:21:53 AM3/5/13
to scala-i...@googlegroups.com
My experience with a compiler built under -neo:o4 is that it can't build a number of "special" files which reside under src/library without crashing: all the primitives, Null, Nothing, Manifest, maybe others.

% ./build/pack/bin/scalac -d /tmp ./src/library/scala/Byte.scala 
java.lang.AssertionError: assertion failed: 
     while compiling: ./src/library/scala/Byte.scala
        during phase: jvm
     library version: version 2.11.0-20130304-201648-ee412cbbdb
    compiler version: version 2.11.0-20130304-201648-ee412cbbdb
  reconstructed args: -d /tmp

  last tree to typer: term MaxValue 
              symbol: value MaxValue in object Byte (flags: final <triedcooking> private[this])
   symbol definition: final private[this] val MaxValue: Byte(127)
                 tpe: <notype>
       symbol owners: value MaxValue -> object Byte -> package scala
      context owners: object Byte -> package scala

Miguel Garcia

unread,
Mar 5, 2013, 11:09:45 AM3/5/13
to scala-i...@googlegroups.com

When compiling library sources, specifying -sourcepath src/library avoids that crash (which I'm assuming is "the right thing to do")

  ./build/pack/bin/scalac -neo:o4 -closurify:delegating src/library/scala/Byte.scala -d /tmp -sourcepath src/library

Same with other optimization levels, and without optimization at all.

Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/

Josh Suereth

unread,
Mar 5, 2013, 10:23:16 PM3/5/13
to scala-internals
That's true.   -sourcepath is still a requirement of bootstrapping, although it'd be nice if we had a way around it....


Paul Phillips

unread,
Mar 5, 2013, 10:27:52 PM3/5/13
to scala-i...@googlegroups.com
A requirement in what sense? You can compile the library with the existing backend without sourcepath. What is different?

Grzegorz Kossakowski

unread,
Mar 5, 2013, 11:55:31 PM3/5/13
to scala-i...@googlegroups.com
On 5 March 2013 19:27, Paul Phillips <pa...@improving.org> wrote:
A requirement in what sense? You can compile the library with the existing backend without sourcepath. What is different?

The last time I checked you can't build it if you don't have older version of the library on your classpath.

--
Grzegorz Kossakowski
Scalac hacker at Typesafe
twitter: @gkossakowski

Simon Ochsenreither

unread,
Mar 6, 2013, 4:59:10 PM3/6/13
to scala-i...@googlegroups.com

ant all.clean build-neo2 strap-neo2 test.stability

I just bootstrapped Miguel's branch on Avian and I can report that it worked without issues. Timings: https://groups.google.com/d/msg/scala-language/HW4FvHVLo8k/oPn9Yg80PvYJ

I guess -neo:o4 -closurify:MH -target:jvm-1.7 will get more interesting. :-)

Simon Ochsenreither

unread,
Mar 6, 2013, 5:33:19 PM3/6/13
to scala-i...@googlegroups.com

I guess -neo:o4 -closurify:MH -target:jvm-1.7 will get more interesting. :-)

I'm currently compiling Scala with these settings and I get almost the same timings as with -neo:o2. Is this expected, or is Ant somehow managing to ignore the settings?
(The jar file sizes seem to be identical, too.)

<target name="build-neo4"
  description="Builds the -neo:o4 -closurify:MH -target:jvm-1.7 optimised Scala compiler and library. Executables are in 'build/pack/bin'.">
  <antcall target="build">
    <param name="scalac.args.optimise" value="-neo:o4 -closurify:MH -target:jvm-1.7"/>
  </antcall>
</target>
 
<target name="strap-neo4"
  description="Builds the -neo:o4 -closurify:MH -target:jvm-1.7 optimised Scala compiler and library. Executables are in 'build/pack/bin'.">
  <antcall target="strap.done">
    <param name="scalac.args.optimise" value="-neo:o4 -closurify:MH -target:jvm-1.7"/>
  </antcall>
</target>


Thanks and bye,

Simon

Miguel Garcia

unread,
Mar 7, 2013, 5:06:07 AM3/7/13
to scala-i...@googlegroups.com
Simon,

Great benchmarks so far, just a remark about -closurify:MH (ie method handles instead of anon-closure-classes) Support for that will land in the experimental backend next week (more details below). Currently an error message should appear upon choosing that option.

Regarding Avian, I've got mixed feelings about the compiler speedup there. On the one hand, the experimental backend conciously avoids GC overhead (which should play well on Avian), on the other hand the bytecode emitter achives most of its speedup via task-parallelism (I'm not familiar enough with the Avian JVM to know about its multi-threading performance).

There are Ant tasks for non-optimized and optimized builds, ie build and build-opt , which interplay with <param name="scalac.args.optimise" value="...">

Now that you ask about MethodHandles, their implementation is around the corner. But first I'm re-creating a clean commit history after branching anew from master. The plan for MHs looks like:

  (a) the backend already materializes (so called "Late") anon-closure-classes, along with their instantiation where a anon-closure appears in Scala text
  (b) that "materialization" consists in building a subclass of scala.runtime.AbstractFunctionX (where X is the function arity)
  (c) for methos handles, one can have one subclass for each arity, of the form:

final class scala.runtime.MHBasedFunctionX(target: MethodHandle) extends AbstractFunctionX {

  override def apply$mc...(args) {
    target.invoke(args)
  }

  and so on for all apply() methods defined by the superclass,
  not necessary to override andThen, compose

}

For the class above to verify, each "target.invoke" should have a method descriptor matching the apply() it's in. That can be emitted via ASM, it's only those 22 subclasses that need to use that trick.

Instead of an instantiation, LDC method-handle-constant followed by bindTo of closure-state. Similar to what genLateClosure() does now.

As a first approximation, some of the code idioms that will be used in the resulting implementation can be seen in the following Java program:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class Main {

    private static MethodHandle mhA;
    private static MethodHandle mhB;

    public static double deleA(String s, boolean b) {
        return 1.0;
    }

    public static int[] deleB(double d, int[] iarr) {
        return iarr;
    }

    private static void start() throws NoSuchMethodException, IllegalAccessException {
        MethodHandles.Lookup lookup = MethodHandles.lookup();

        MethodType mtA = MethodType.methodType(double.class, String.class, boolean.class);
        mhA = lookup.findStatic(Main.class, "deleA", mtA);

        MethodType mtB = MethodType.methodType(int[].class, double.class, int[].class);
        mhB = lookup.findStatic(Main.class, "deleB", mtB);
    }

    public static void main(String[] args) throws Throwable {
        start();
        double resA = (double) applyA("abc", true);
        assert resA == 1.0;
        int[]  iarr = {1, 2, 3};
        int[]  resB = (int[])  applyB(1.1d, iarr);
        assert iarr.equals(resB);
    }

    public static Object applyA(Object s, Object b) throws Throwable {
        return mhA.invoke(s, b);
    }

    public static Object applyB(Object d, Object iarr) throws Throwable {
        return mhB.invoke(d, iarr);
    }


}



regards,

Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/

Simon Ochsenreither

unread,
Mar 7, 2013, 6:12:27 AM3/7/13
to scala-i...@googlegroups.com
Hi Miguel,


Great benchmarks so far, just a remark about -closurify:MH (ie method handles instead of anon-closure-classes) Support for that will land in the experimental backend next week (more details below). Currently an error message should appear upon choosing that option.

Ok, then I'm pretty sure I somehow managed to make Ant ignore all those options. Is there a tool to debug Ant configurations? :-/


Regarding Avian, I've got mixed feelings about the compiler speedup there. On the one hand, the experimental backend conciously avoids GC overhead (which should play well on Avian), on the other hand the bytecode emitter achives most of its speedup via task-parallelism (I'm not familiar enough with the Avian JVM to know about its multi-threading performance).

There are not much differences between Scala trunk and your branch (with default options). The huge speedup between 2013-02-01 and 2013-03-06 are the results of GC improvements and fixes in Avian which addressed issues uncovered by running Scala's test suite on Avian.


There are Ant tasks for non-optimized and optimized builds, ie build and build-opt , which interplay with <param name="scalac.args.optimise" value="...">

Ah, ok. This could be the culprit.


Now that you ask about MethodHandles, their implementation is around the corner. But first I'm re-creating a clean commit history after branching anew from master.

Sounds great, I'll test that on Avian as it gets available!
I'm not sure how much of the MethodHandles infrastructure is implemented in the library vs. the run-time, that will certainly get interesting.

I'm currently in the process of completely abandoning Hotspot for my work, so it would be pretty annoying if Scala 2.11 would suddenly ship with backends which only support Oracle VMs.
But if one of the other alternative closure strategies keeps working, that will certainly be good enough, too. I guess I'll need to do further testing here.

Thanks and bye,

Simon

Johannes Rudolph

unread,
Mar 7, 2013, 6:17:52 AM3/7/13
to scala-i...@googlegroups.com
On Thu, Mar 7, 2013 at 12:12 PM, Simon Ochsenreither
<simon.och...@gmail.com> wrote:
> I'm currently in the process of completely abandoning Hotspot for my work

Out of interest, why?


--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Ismael Juma

unread,
Mar 7, 2013, 6:23:50 AM3/7/13
to scala-i...@googlegroups.com
On Thu, Mar 7, 2013 at 11:17 AM, Johannes Rudolph <johannes...@googlemail.com> wrote:
On Thu, Mar 7, 2013 at 12:12 PM, Simon Ochsenreither
<simon.och...@gmail.com> wrote:
> I'm currently in the process of completely abandoning Hotspot for my work

Out of interest, why?

And what is the replacement?

Ismael

Simon Ochsenreither

unread,
Mar 7, 2013, 7:40:04 AM3/7/13
to scala-i...@googlegroups.com

Out of interest, why?

I'm sick and tired of Oracle's inability to have working business relationships, its failure to ship anything remotely worthwhile in the JVM space, its crackdown on alternative implementations and its bullshit excuses about the whole situation.

I have been waiting for years for a JVM with a feature set fit for the 20th century.

There is absolutely no sane explanation why we still don't have tail calls, native deployment options, acceptable memory usage, big arrays, runtime specialization, working lambdas, a better class file format, continuations, coroutines ...

 
And what is the replacement?

Avian. It ships with proper tail calls and continuations TODAY.
Additionally, it can generate tiny stand-alone executables, can be deployed on iOS, Android and Windows (Phone) 8, supports embedding and can use different class libraries including its own, OpenJDK's and Android's.

What's more important, the actual implementation is readable and improvements happen fast. The authors have a sane focus on actually solving issues (e. g. generating less garbage, causing less work at runtime: value types, thread-local heaps, etc.) instead of trying to paper over it with ridiculous complex JIT and GC implementations, resulting in increasingly tiny single-digit improvements while eating up more and more memory.

As soon as the Scala tests run fast enough on Avian, it will probably be my last day on Oracle's platform. I don't intend to be there when Oracle decides to backstab Scala/Typesafe as it did with SpringSource, RedHat, Google, Harmony, LibreOffice, Jenkins, OpenSolaris, MySQL, ...

Bye,

Simon
Reply all
Reply to author
Forward
0 new messages