Make warm compilation speed with sbt 2x faster#2262
Conversation
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`).
| * JIT code cache for the compiler will be discarded between every call to | ||
| * the sbt `compile` task. | ||
| */ | ||
| private[this] val fixedLoaderCache = new mutable.WeakHashMap[ClassLoader, ClassLoader] |
There was a problem hiding this comment.
in weak hashMap only values are weak. You are keeping SBT classloaders(keys) from being GC-d.
I'd prefer to have one more level of indirection to also remove this issue.
There was a problem hiding this comment.
Are you sure? From https://docs.oracle.com/javase/8/docs/api/java/util/WeakHashMap.html: "Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use."
| */ | ||
| def fixBridgeLoader(bridgeLoader: ClassLoader) = bridgeLoader match { | ||
| def fixBridgeLoader(bridgeLoader: ClassLoader): ClassLoader = | ||
| fixedLoaderCache.getOrElseUpdate(bridgeLoader, computeFixedLoader(bridgeLoader)) |
There was a problem hiding this comment.
should this method be synchronized?
There was a problem hiding this comment.
BTW: CompilerClassLoader.loadClass should definitely be synchronized.
There was a problem hiding this comment.
You're right, this method should be synchronized. I don't think loadClass needs to be however because I'm only calling super.loadClass and resolveClass which should both be thread-safe. In fact, I think that I should be able to call https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#registerAsParallelCapable-- to make my ClassLoader parallel.
This method could be called from multiple threads since sbt could run multiple `compile` task in parallel.
Previously, every call to
compilein sbt with dotty took about thesame time because we created a new ClassLoader everytime and thus
thrased the JIT code cache, by reusing ClassLoaders we can make
compileabout 2x faster.You can reproduce this by running:
> dotty-compiler-bootstrapped/compileThis takes ~50 seconds on my machine. Then clean using:
And run
dotty-compiler-bootstrapped/compileagain, this takes ~25seconds for me. I get very similar timings from scalac (replacing
dotty-compiler-bootstrappedbydotty-compiler).