Skip to content

Commit d773a32

Browse files
committed
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`).
1 parent 9e45ad1 commit d773a32

File tree

1 file changed

+14
-1
lines changed

1 file changed

+14
-1
lines changed

sbt-bridge/src/xsbt/CompilerClassLoader.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package xsbt
22

33
import java.net.{URL, URLClassLoader}
44

5+
import scala.collection.mutable
6+
57
/** A classloader to run the compiler
68
*
79
* 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)
4244
}
4345

4446
object CompilerClassLoader {
47+
/** Cache the result of `fixBridgeLoader`.
48+
*
49+
* Reusing ClassLoaders is important for warm performance since otherwise the
50+
* JIT code cache for the compiler will be discarded between every call to
51+
* the sbt `compile` task.
52+
*/
53+
private[this] val fixedLoaderCache = new mutable.WeakHashMap[ClassLoader, ClassLoader]
54+
4555
/** Fix the compiler bridge ClassLoader
4656
*
4757
* Soundtrack: https://www.youtube.com/watch?v=imamcajBEJs
@@ -70,7 +80,10 @@ object CompilerClassLoader {
7080
* @param bridgeLoader The classloader that sbt uses to load the compiler bridge
7181
* @return A fixed classloader that works with dotty
7282
*/
73-
def fixBridgeLoader(bridgeLoader: ClassLoader) = bridgeLoader match {
83+
def fixBridgeLoader(bridgeLoader: ClassLoader): ClassLoader =
84+
fixedLoaderCache.getOrElseUpdate(bridgeLoader, computeFixedLoader(bridgeLoader))
85+
86+
private[this] def computeFixedLoader(bridgeLoader: ClassLoader) = bridgeLoader match {
7487
case bridgeLoader: URLClassLoader =>
7588
val dualLoader = bridgeLoader.getParent
7689
val dualLoaderClass = dualLoader.getClass

0 commit comments

Comments
 (0)