From 3c508ce52db7984b4636f935546e2b3a20a7ab61 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 22 Sep 2016 17:24:30 +1000 Subject: [PATCH] SD-232 Recycle classloaders to be anti-hostile to JIT. The compiler interface subclasses `scala.tools.nsc.Global`, and loading this new subclass before each `compile` task forces HotSpot JIT to deoptimize larges swathes of compiled code. It's a bit like SBT has rigged the dice to always descend the longest ladder in a game of Snakes and Ladders. The slowdown seems to be larger with Scala 2.12. There are a number of variables at play, but I think the main factor here is that we now rely on JIT to devirtualize calls to final methods in traits whereas we used to emit static calls. JIT does a good job at this, so long as classloading doesn't undo that good work. This commit extends the existing `ClassLoaderCache` to encompass the classloader that includes the compiler interface JAR. I've resorted to adding a var to `AnalyzingCompiler` to inject the dependency to get the cache to the spot I need it without binary incompatible changes to the intervening method signatures. --- main/src/main/scala/sbt/Defaults.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index a275d0d5b..94a72e521 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -307,8 +307,13 @@ object Defaults extends BuildCommon { if (plugin) scalaBase / ("sbt-" + sbtv) else scalaBase } - def compilersSetting = compilers := Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value, - bootIvyConfiguration.value, fileToStore.value, scalaCompilerBridgeSource.value)(appConfiguration.value, streams.value.log) + def compilersSetting = compilers := { + val compilers = Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value, + bootIvyConfiguration.value, fileToStore.value, scalaCompilerBridgeSource.value)(appConfiguration.value, streams.value.log) + if (!java.lang.Boolean.getBoolean("sbt.disable.interface.classloader.cache")) + compilers.scalac.setClassLoaderCache(state.value.classLoaderCache) + compilers + } lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq( compile := compileTask.value,