Preload a number of classes in the background

I was looking into sbt start up time and in profiling was able to
identify a number of classloading bottlenecks. To speed up
initialization, we can preload those classes in the background. I saw
average speedups of roughly .75 seconds after this change. Also, the `time`
command would consistently report cpu system time very close to 400% and
I have 4 cores on my laptop. With 1.3.0 it would be more like 350%.
This commit is contained in:
Ethan Atkins 2019-09-17 18:41:40 -07:00
parent 29ea7ee6fc
commit d966c40917
1 changed files with 35 additions and 1 deletions

View File

@ -10,10 +10,43 @@ package sbt.internal
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.{ URL, URLClassLoader }
import java.util.concurrent.{ ExecutorService, Executors }
import java.util.regex.Pattern
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
import sbt.util.LogExchange
import xsbti._
private[internal] object ClassLoaderWarmup {
def warmup(): Unit = {
if (Runtime.getRuntime.availableProcessors > 1) {
val executorService: ExecutorService =
Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors - 1)
def submit[R](f: => R): Unit = {
executorService.submit(new Runnable {
override def run(): Unit = { f; () }
})
()
}
submit(LogExchange.context)
submit(Class.forName("sbt.internal.parser.SbtParserInit").getConstructor().newInstance())
submit(CorePlugin.projectSettings)
submit(IvyPlugin.projectSettings)
submit(JvmPlugin.projectSettings)
submit(() => {
try {
val clazz = Class.forName("scala.reflect.runtime.package$")
clazz.getMethod("universe").invoke(clazz.getField("MODULE$").get(null))
} catch {
case _: Exception =>
}
executorService.shutdown()
})
}
}
}
/**
* Generates a new app configuration and invokes xMainImpl.run. For AppConfigurations generated
* by recent launchers, it is unnecessary to modify the original configuration, but configurations
@ -42,7 +75,8 @@ private[sbt] class XMainConfiguration {
val instance = clazz.getField("MODULE$").get(null)
val runMethod = clazz.getMethod("run", classOf[xsbti.AppConfiguration])
try {
loader.loadClass("sbt.internal.parser.SbtParserInit").getConstructor().newInstance()
val clw = loader.loadClass("sbt.internal.ClassLoaderWarmup$")
clw.getMethod("warmup").invoke(clw.getField("MODULE$").get(null))
runMethod.invoke(instance, updatedConfiguration).asInstanceOf[xsbti.MainResult]
} catch {
case e: InvocationTargetException =>