Re-use metabuild scala instance layer

At some point I noticed that projects with no scala sources in the build
loaded significantly faster than projects that had even a single scala
file -- no matter how simple that file was. This didn't really make
sense to me because *.sbt files _do_ have to be compiled. I finally
realized that classloading was a likely bottle neck because *.sbt
files are compiled on the sbt classpath while *.scala files are compiled
with a different classloader generated by the classloader cache. It then
occurred to me that we could pre-fill the classloader cache with the
scala layer of the sbt metabuild classloader.

I found that compared to 1.3.0-M5, a project with a simple scala file in
the project directory loaded about 2 seconds faster after this change.
Even if there are no scala sources in the build.sbt, there is a similar
performance improvement for running "sbt compile", which I found exited
2-3 seconds faster after this change.
This commit is contained in:
Ethan Atkins 2019-06-06 20:05:31 -07:00
parent af0cfc9740
commit f1698d2bf2
1 changed files with 32 additions and 9 deletions

View File

@ -8,20 +8,14 @@
package sbt
import java.io.File
import java.net.{ URL, URLClassLoader }
import java.util.concurrent.Callable
import sbt.internal.classpath.ClassLoaderCache
import sbt.internal.inc.classpath.{ ClassLoaderCache => IncClassLoaderCache }
import sbt.util.Logger
import sbt.internal.util.{
AttributeKey,
AttributeMap,
ErrorHandling,
ExitHook,
ExitHooks,
GlobalLogging
}
import sbt.internal.util.complete.{ HistoryCommands, Parser }
import sbt.internal.util._
import sbt.util.Logger
/**
* Data structure representing all command execution information.
@ -202,6 +196,10 @@ trait StateOps extends Any {
}
object State {
private class UncloseableURLLoader(cp: Seq[File], parent: ClassLoader)
extends URLClassLoader(Array.empty, parent) {
override def getURLs: Array[URL] = cp.map(_.toURI.toURL).toArray
}
/** Indicates where command execution should resume after a failure.*/
val FailureWall = BasicCommandStrings.FailureWall
@ -344,6 +342,31 @@ object State {
def initializeClassLoaderCache: State = {
s.get(BasicKeys.extendedClassLoaderCache).foreach(_.close())
val cache = newClassLoaderCache
s.configuration.provider.scalaProvider.loader match {
case null => // This can happen in scripted
case fullScalaLoader =>
val jars = s.configuration.provider.scalaProvider.jars
val (library, rest) = jars.partition(_.getName == "scala-library.jar")
library.toList match {
case l @ lj :: Nil =>
fullScalaLoader.getParent match {
case null => // This can happen for old launchers.
case libraryLoader =>
cache.cachedCustomClassloader(l, () => new UncloseableURLLoader(l, libraryLoader))
fullScalaLoader match {
case u: URLClassLoader
if u.getURLs
.filterNot(_ == lj.toURI.toURL)
.sameElements(rest.map(_.toURI.toURL)) =>
cache.cachedCustomClassloader(
jars.toList,
() => new UncloseableURLLoader(jars, fullScalaLoader)
)
case _ =>
}
}
}
}
s.put(BasicKeys.extendedClassLoaderCache, cache)
.put(BasicKeys.classLoaderCache, new IncClassLoaderCache(cache))
}