diff options
author | Paul Phillips <paulp@improving.org> | 2010-12-18 02:13:07 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-12-18 02:13:07 +0000 |
commit | e911fdab94900bea85645201311f5ec2dc758fcd (patch) | |
tree | b54f4b2a9cf4d8e6a55e93640ca26a3f796391b9 | |
parent | 9cbadc4d7cb11a303e500cc47af9e050b5adb96d (diff) | |
download | scala-e911fdab94900bea85645201311f5ec2dc758fcd.tar.gz scala-e911fdab94900bea85645201311f5ec2dc758fcd.tar.bz2 scala-e911fdab94900bea85645201311f5ec2dc758fcd.zip |
Added some infrastructure for tracking and disp...
Added some infrastructure for tracking and displaying information. Used
it to generate phase timing tables. Couldn't bring myself to add another
option so it's temporarily behind a system property until I sort out the
output options.
% scalac -Dscala.timings foo.scala
// or: ant -Djvm.opts="-Dscala.timings"
[...]
phase id ms share
-------------- -- ---- -----
typer 4 5816 44.94
mixin 20 1220 9.43
specialize 13 1179 9.11
erasure 15 916 7.08
...etc. No review.
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 19 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Phase.scala | 34 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/PhaseAssembly.scala | 34 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/plugins/Plugins.scala | 8 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/TableDef.scala | 2 | ||||
-rw-r--r-- | test/files/run/t3895b.scala | 7 |
6 files changed, 75 insertions, 29 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 45fb691904..1f10d304c1 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -144,10 +144,13 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable if (opt.fatalWarnings) globalError(msg) else reporter.warning(NoPosition, msg) + private def elapsedMessage(msg: String, start: Long) = + msg + " in " + (currentTime - start) + "ms" + def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) def informProgress(msg: String) = if (opt.verbose) inform("[" + msg + "]") def inform[T](msg: String, value: T): T = returning(value)(x => inform(msg + x)) - def informTime(msg: String, start: Long) = informProgress(msg + " in " + (currentTime - start) + "ms") + def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) def logError(msg: String, t: Throwable): Unit = () def log(msg: => AnyRef): Unit = if (opt.logPhase) inform("[log " + phase + "] " + msg) @@ -231,6 +234,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable def encoding = optSetting[String](settings.encoding) def sourceReader = optSetting[String](settings.sourceReader) + // XXX: short term, but I can't bear to add another option. + // scalac -Dscala.timings will make this true. + def timings = system.props contains "scala.timings" + def debug = settings.debug.value def deprecation = settings.deprecation.value def experimental = settings.Xexperimental.value @@ -605,6 +612,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable /* The set of phase objects that is the basis for the compiler phase chain */ protected lazy val phasesSet = new mutable.HashSet[SubComponent] protected lazy val phasesDescMap = new mutable.HashMap[SubComponent, String] withDefaultValue "" + private lazy val phaseTimings = new Phase.TimingModel // tracking phase stats protected def addToPhasesSet(sub: SubComponent, descr: String) { phasesSet += sub phasesDescMap(sub) = descr @@ -868,6 +876,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } else globalPhase.run + // progress update + informTime(globalPhase.description, startTime) + phaseTimings(globalPhase) = currentTime - startTime + // write icode to *.icode files if (opt.writeICode && (runIsAt(icodePhase) || opt.printPhase && runIsPast(icodePhase))) writeICode() @@ -885,8 +897,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable if (opt.browsePhase) treeBrowser browse (phase.name, units) - // progress update - informTime(globalPhase.description, startTime) + // move the pointer globalPhase = globalPhase.next // run tree/icode checkers @@ -903,6 +914,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable profiler.stopProfiling() profiler.captureSnapshot() } + if (opt.timings) + inform(phaseTimings.formatted) // If no phase was specified for -Xshow-class/object, show it now. if (settings.Yshow.isDefault) diff --git a/src/compiler/scala/tools/nsc/Phase.scala b/src/compiler/scala/tools/nsc/Phase.scala index 9726834b88..f4cdfdadee 100644 --- a/src/compiler/scala/tools/nsc/Phase.scala +++ b/src/compiler/scala/tools/nsc/Phase.scala @@ -6,6 +6,7 @@ package scala.tools.nsc import symtab.Flags +import util.TableDef abstract class Phase(val prev: Phase) { @@ -48,4 +49,37 @@ abstract class Phase(val prev: Phase) { } } +object Phase { + val MaxPhases = 64 + + /** A class for tracking something about each phase. + */ + class Model[T: Manifest] { + case class Cell(ph: Phase, value: T) { + def name = ph.name + def id = ph.id + } + val values = new Array[Cell](MaxPhases + 1) + def results = values filterNot (_ == null) + def apply(ph: Phase): T = values(ph.id).value + def update(ph: Phase, value: T): Unit = values(ph.id) = Cell(ph, value) + } + /** A class for recording the elapsed time of each phase in the + * interests of generating a classy and informative table. + */ + class TimingModel extends Model[Long] { + var total: Long = 0 + def table() = { + total = results map (_.value) sum; + new Format.Table(results sortBy (-_.value)) + } + object Format extends TableDef[Cell] { + >> ("phase" -> (_.name)) >+ " " + << ("id" -> (_.id)) >+ " " + >> ("ms" -> (_.value)) >+ " " + << ("share" -> (_.value.toDouble * 100 / total formatted "%.2f")) + } + def formatted = "" + table() + } +} diff --git a/src/compiler/scala/tools/nsc/PhaseAssembly.scala b/src/compiler/scala/tools/nsc/PhaseAssembly.scala index c121098d56..9a306d939c 100644 --- a/src/compiler/scala/tools/nsc/PhaseAssembly.scala +++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala @@ -6,8 +6,8 @@ package scala.tools.nsc -import scala.collection.mutable.{HashSet, HashMap} -import java.io.{BufferedWriter, FileWriter} +import java.io.{ BufferedWriter, FileWriter } +import scala.collection.mutable /** * PhaseAssembly @@ -15,7 +15,8 @@ import java.io.{BufferedWriter, FileWriter} * the rest of the compiler. See SIP 00002 * */ -trait PhaseAssembly { self: Global => +trait PhaseAssembly { + self: Global => /** * Aux datastructure for solving the constraint system @@ -35,8 +36,8 @@ trait PhaseAssembly { self: Global => class Node(name: String) { val phasename = name var phaseobj: Option[List[SubComponent]] = None - val after = new HashSet[Edge]() - var before = new HashSet[Edge]() + val after = new mutable.HashSet[Edge]() + var before = new mutable.HashSet[Edge]() var visited = false var level = 0 @@ -46,8 +47,8 @@ trait PhaseAssembly { self: Global => } } - val nodes = new HashMap[String,Node]() - val edges = new HashSet[Edge]() + val nodes = new mutable.HashMap[String,Node]() + val edges = new mutable.HashSet[Edge]() /* Given a phase object, get the node for this phase object. If the * node object does not exist, then create it. @@ -184,22 +185,19 @@ trait PhaseAssembly { self: Global => * dependency on something that is dropped. */ def removeDanglingNodes() { - var dnodes = nodes.valuesIterator filter (_.phaseobj.isEmpty) - for (node <- dnodes) { + for (node <- nodes.valuesIterator filter (_.phaseobj.isEmpty)) { val msg = "dropping dependency on node with no phase object: "+node.phasename informProgress(msg) nodes -= node.phasename + for (edge <- node.before) { edges -= edge edge.frm.after -= edge - edge.frm.phaseobj match { - case Some(lsc) => if (! lsc.head.internal) warning(msg) - case _ => - } + if (edge.frm.phaseobj exists (lsc => !lsc.head.internal)) + warning(msg) } } } - } /* Method called from computePhaseDescriptors in class Global @@ -241,7 +239,7 @@ trait PhaseAssembly { self: Global => /** Given the phases set, will build a dependency graph from the phases set * Using the aux. method of the DependencyGraph to create nodes and egdes. */ - private def phasesSetToDepGraph(phsSet: HashSet[SubComponent]): DependencyGraph = { + private def phasesSetToDepGraph(phsSet: mutable.HashSet[SubComponent]): DependencyGraph = { val graph = new DependencyGraph() for (phs <- phsSet) { @@ -283,9 +281,9 @@ trait PhaseAssembly { self: Global => * Plug-in supplied phases are marked as green nodes and hard links are marked as blue edges. */ private def graphToDotFile(graph: DependencyGraph, filename: String) { - var sbuf = new StringBuilder - var extnodes = new HashSet[graph.Node]() - var fatnodes = new HashSet[graph.Node]() + val sbuf = new StringBuilder + val extnodes = new mutable.HashSet[graph.Node]() + val fatnodes = new mutable.HashSet[graph.Node]() sbuf.append("digraph G {\n") for (edge <- graph.edges) { sbuf.append("\"" + edge.frm.allPhaseNames + "(" + edge.frm.level + ")" + "\"->\"" + edge.to.allPhaseNames + "(" + edge.to.level + ")" + "\"") diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 24962ee085..e2f4806c01 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -105,8 +105,12 @@ trait Plugins { * Extract all phases supplied by plugins and add them to the phasesSet. * @see phasesSet */ - protected def computePluginPhases(): Unit = - phasesSet ++= (plugins flatMap (_.components)) + protected def computePluginPhases(): Unit = { + // For reasons not yet apparent to me, plugins started appearing + // as null when I added phaseTimings to global. + if (plugins != null) + phasesSet ++= (plugins flatMap (_.components)) + } /** Summary of the options for all loaded plugins */ def pluginOptionsHelp: String = diff --git a/src/compiler/scala/tools/nsc/util/TableDef.scala b/src/compiler/scala/tools/nsc/util/TableDef.scala index b18eaaa400..10c63eeee2 100644 --- a/src/compiler/scala/tools/nsc/util/TableDef.scala +++ b/src/compiler/scala/tools/nsc/util/TableDef.scala @@ -4,7 +4,7 @@ package util import TableDef._ /** A class for representing tabular data in a way that preserves - * its inner beauty. See JavaStackFrame for an example usage. + * its inner beauty. See Exceptional for an example usage. * One creates an instance of TableDef by defining the columns of * the table, then uses that to create an instance of Table by * passing in a sequence of rows. diff --git a/test/files/run/t3895b.scala b/test/files/run/t3895b.scala index fd74aab125..a74f7b2ead 100644 --- a/test/files/run/t3895b.scala +++ b/test/files/run/t3895b.scala @@ -1,5 +1,4 @@ -class DryRun -{ +class DryRun { import scala.tools.nsc.{Global, Settings, CompilerCommand} import scala.tools.nsc.reporters.ConsoleReporter @@ -7,12 +6,10 @@ class DryRun settings.classpath.value = System.getProperty("java.class.path") val command = new CompilerCommand(List(), settings) val reporter = new ConsoleReporter(settings, scala.Console.in, new java.io.PrintWriter(new java.io.PrintStream(scala.Console.out))) - object compiler extends Global(command.settings, reporter) - { + object compiler extends Global(command.settings, reporter) { object test1 lazy val test2 = 1 object test3 - } def test { compiler.test1 |