From d773a32c9731dd5b105ac718900f8217fa75bcb2 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 14 Apr 2017 02:23:59 +0200 Subject: Make warm compilation speed with sbt 2x faster Previously, every call to `compile` in sbt with dotty took about the same time because we created a new ClassLoader everytime and thus thrased the JIT code cache, by reusing ClassLoaders we can make `compile` about 2x faster. You can reproduce this by running: > dotty-compiler-bootstrapped/compile This takes ~50 seconds on my machine. Then clean using: > ;dotty-compiler-bootstrapped/clean;dotty-compiler-update And run `dotty-compiler-bootstrapped/compile` again, this takes ~25 seconds for me. I get very similar timings from scalac (replacing `dotty-compiler-bootstrapped` by `dotty-compiler`). --- sbt-bridge/src/xsbt/CompilerClassLoader.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sbt-bridge/src/xsbt/CompilerClassLoader.scala b/sbt-bridge/src/xsbt/CompilerClassLoader.scala index 3cb3f344f..c0bdcb5e9 100644 --- a/sbt-bridge/src/xsbt/CompilerClassLoader.scala +++ b/sbt-bridge/src/xsbt/CompilerClassLoader.scala @@ -2,6 +2,8 @@ package xsbt import java.net.{URL, URLClassLoader} +import scala.collection.mutable + /** A classloader to run the compiler * * A CompilerClassLoader is constructed from a list of `urls` that need to be on @@ -42,6 +44,14 @@ class CompilerClassLoader(urls: Array[URL], sbtLoader: ClassLoader) } object CompilerClassLoader { + /** Cache the result of `fixBridgeLoader`. + * + * Reusing ClassLoaders is important for warm performance since otherwise the + * JIT code cache for the compiler will be discarded between every call to + * the sbt `compile` task. + */ + private[this] val fixedLoaderCache = new mutable.WeakHashMap[ClassLoader, ClassLoader] + /** Fix the compiler bridge ClassLoader * * Soundtrack: https://www.youtube.com/watch?v=imamcajBEJs @@ -70,7 +80,10 @@ object CompilerClassLoader { * @param bridgeLoader The classloader that sbt uses to load the compiler bridge * @return A fixed classloader that works with dotty */ - def fixBridgeLoader(bridgeLoader: ClassLoader) = bridgeLoader match { + def fixBridgeLoader(bridgeLoader: ClassLoader): ClassLoader = + fixedLoaderCache.getOrElseUpdate(bridgeLoader, computeFixedLoader(bridgeLoader)) + + private[this] def computeFixedLoader(bridgeLoader: ClassLoader) = bridgeLoader match { case bridgeLoader: URLClassLoader => val dualLoader = bridgeLoader.getParent val dualLoaderClass = dualLoader.getClass -- cgit v1.2.3