From a1d9656299a90d9ea4d5aa14a7873e3b4941eb59 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 1 Nov 2013 11:40:26 +0100 Subject: Don't use runtime reflection from the batch compiler. Not only does this save a big chunk of time on startup by avoiding classloading and symbol table population, but it also seems to improve steady state performance of the compiler. Theory: JIT can optimize more aggressively without the SynchronizedXxx decorators and the like being in the classloader. See "Class Heirarchy Analyis" in [1] This commit does this by: - Avoiding use of FromString in pattern matcher, instead using an established mechanism to parse system properties. - Changes FromString back to use OptManifest. AFAICT, this is now only a dependency of scala.tools.cmd.gen.Codegen, so this is just a defensive measure. The REPL still uses runtime reflection, so will pay a little performance tax. Benchmark: avg shortest 10 times 744ms # before avg shortest 10 times 675ms # after [1] https://wikis.oracle.com/display/HotSpotInternals/PerformanceTechniques --- src/compiler/scala/tools/cmd/FromString.scala | 19 +++++++++---------- .../scala/tools/nsc/transform/patmat/Logic.scala | 10 +++++++--- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'src/compiler/scala/tools') diff --git a/src/compiler/scala/tools/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala index 433bbb167e..0b074efc0f 100644 --- a/src/compiler/scala/tools/cmd/FromString.scala +++ b/src/compiler/scala/tools/cmd/FromString.scala @@ -6,21 +6,20 @@ package scala.tools package cmd -import scala.tools.nsc.io.{ File, Directory } -import scala.reflect.runtime.{universe => ru} -import scala.tools.reflect.StdRuntimeTags._ +import nsc.io.{ Path, File, Directory } +import scala.reflect.OptManifest /** A general mechanism for defining how a command line argument * (always a String) is transformed into an arbitrary type. A few * example instances are in the companion object, but in general * either IntFromString will suffice or you'll want custom transformers. */ -abstract class FromString[+T](implicit t: ru.TypeTag[T]) extends PartialFunction[String, T] { +abstract class FromString[+T](implicit m: OptManifest[T]) extends PartialFunction[String, T] { def apply(s: String): T def isDefinedAt(s: String): Boolean = true def zero: T = apply("") - def targetString: String = t.toString + def targetString: String = m.toString } object FromString { @@ -29,13 +28,13 @@ object FromString { /** Path related stringifiers. */ - val ExistingDir: FromString[Directory] = new FromString[Directory]()(tagOfDirectory) { + val ExistingDir: FromString[Directory] = new FromString[Directory] { override def isDefinedAt(s: String) = toDir(s).isDirectory def apply(s: String): Directory = if (isDefinedAt(s)) toDir(s) else cmd.runAndExit(println("'%s' is not an existing directory." format s)) } - def ExistingDirRelativeTo(root: Directory) = new FromString[Directory]()(tagOfDirectory) { + def ExistingDirRelativeTo(root: Directory) = new FromString[Directory] { private def resolve(s: String) = (toDir(s) toAbsoluteWithRoot root).toDirectory override def isDefinedAt(s: String) = resolve(s).isDirectory def apply(s: String): Directory = @@ -46,19 +45,19 @@ object FromString { /** Argument expander, i.e. turns single argument "foo bar baz" into argument * list "foo", "bar", "baz". */ - val ArgumentsFromString: FromString[List[String]] = new FromString[List[String]]()(tagOfListOfString) { + val ArgumentsFromString: FromString[List[String]] = new FromString[List[String]] { def apply(s: String) = toArgs(s) } /** Identity. */ - implicit val StringFromString: FromString[String] = new FromString[String]()(tagOfString) { + implicit val StringFromString: FromString[String] = new FromString[String] { def apply(s: String): String = s } /** Implicit as the most likely to be useful as-is. */ - implicit val IntFromString: FromString[Int] = new FromString[Int]()(tagOfInt) { + implicit val IntFromString: FromString[Int] = new FromString[Int] { override def isDefinedAt(s: String) = safeToInt(s).isDefined def apply(s: String) = safeToInt(s).get def safeToInt(s: String): Option[Int] = try Some(java.lang.Integer.parseInt(s)) catch { case _: NumberFormatException => None } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 8a4e565ced..e0bc478fad 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -162,13 +162,17 @@ trait Logic extends Debugging { // to govern how much time we spend analyzing matches for unreachability/exhaustivity object AnalysisBudget { - import scala.tools.cmd.FromString.IntFromString - val max = sys.props.get("scalac.patmat.analysisBudget").collect(IntFromString.orElse{case "off" => Integer.MAX_VALUE}).getOrElse(256) + private val budgetProp = scala.sys.Prop[Int]("scalac.patmat.analysisBudget") + private val budgetOff = "off" + val max: Int = { + val DefaultBudget = 256 + budgetProp.option.getOrElse(if (budgetProp.get.equalsIgnoreCase("off")) Integer.MAX_VALUE else DefaultBudget) + } abstract class Exception(val advice: String) extends RuntimeException("CNF budget exceeded") object exceeded extends Exception( - s"(The analysis required more space than allowed. Please try with scalac -Dscalac.patmat.analysisBudget=${AnalysisBudget.max*2} or -Dscalac.patmat.analysisBudget=off.)") + s"(The analysis required more space than allowed. Please try with scalac -D${budgetProp.key}=${AnalysisBudget.max*2} or -D${budgetProp.key}=${budgetOff}.)") } -- cgit v1.2.3