mirror of https://github.com/sbt/sbt.git
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:
parent
af0cfc9740
commit
f1698d2bf2
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue