Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.common.tasks; | 5 library dart2js.common.tasks; |
| 6 | 6 |
| 7 import 'dart:async' show | |
| 8 Future, | |
| 9 Zone, | |
| 10 ZoneDelegate, | |
| 11 ZoneSpecification, | |
| 12 runZoned; | |
| 13 | |
| 7 import '../common.dart'; | 14 import '../common.dart'; |
| 8 import '../compiler.dart' show | 15 import '../compiler.dart' show |
| 9 Compiler; | 16 Compiler; |
| 10 import '../elements/elements.dart' show | 17 import '../elements/elements.dart' show |
| 11 Element; | 18 Element; |
| 12 | 19 |
| 13 typedef void DeferredAction(); | 20 typedef void DeferredAction(); |
| 14 | 21 |
| 15 class DeferredTask { | 22 class DeferredTask { |
| 16 final Element element; | 23 final Element element; |
| 17 final DeferredAction action; | 24 final DeferredAction action; |
| 18 | 25 |
| 19 DeferredTask(this.element, this.action); | 26 DeferredTask(this.element, this.action); |
| 20 } | 27 } |
| 21 | 28 |
| 29 /// A [CompilerTask] is used to measure where time is spent in the compiler. | |
| 30 /// The main entry points are [measure] and [measureIo]. | |
| 22 class CompilerTask { | 31 class CompilerTask { |
| 23 final Compiler compiler; | 32 final Compiler compiler; |
| 24 final Stopwatch watch; | 33 final Stopwatch watch; |
| 25 final Map<String, GenericTask> _subtasks = <String, GenericTask>{}; | 34 final Map<String, GenericTask> _subtasks = <String, GenericTask>{}; |
| 26 | 35 |
| 36 int asyncCount = 0; | |
| 37 | |
| 27 CompilerTask(Compiler compiler) | 38 CompilerTask(Compiler compiler) |
| 28 : this.compiler = compiler, | 39 : this.compiler = compiler, |
| 29 watch = (compiler.verbose) ? new Stopwatch() : null; | 40 watch = (compiler.verbose) ? new Stopwatch() : null; |
| 30 | 41 |
| 31 DiagnosticReporter get reporter => compiler.reporter; | 42 DiagnosticReporter get reporter => compiler.reporter; |
| 32 | 43 |
| 44 Measurer get measurer => compiler.measurer; | |
| 45 | |
| 33 String get name => "Unknown task '${this.runtimeType}'"; | 46 String get name => "Unknown task '${this.runtimeType}'"; |
| 34 | 47 |
| 48 bool get isRunning => watch != null && watch.isRunning; | |
|
floitsch
2016/01/07 14:40:39
fyi: there is a pattern emerging for this: watch?.
ahe
2016/01/08 09:02:02
Done.
| |
| 49 | |
| 35 int get timing { | 50 int get timing { |
| 36 if (watch == null) return 0; | 51 if (watch == null) return 0; |
| 37 int total = watch.elapsedMilliseconds; | 52 int total = watch.elapsedMilliseconds; |
| 38 for (GenericTask subtask in _subtasks.values) { | 53 for (GenericTask subtask in _subtasks.values) { |
| 39 total += subtask.timing; | 54 total += subtask.timing; |
| 40 } | 55 } |
| 41 return total; | 56 return total; |
| 42 } | 57 } |
| 43 | 58 |
| 44 measure(action()) { | 59 Duration get duration { |
| 45 // In verbose mode when watch != null. | 60 if (watch == null) return Duration.ZERO; |
| 46 if (watch == null) return action(); | 61 Duration total = watch.elapsed; |
| 47 CompilerTask previous = compiler.measuredTask; | 62 for (GenericTask subtask in _subtasks.values) { |
| 48 if (identical(this, previous)) return action(); | 63 total += subtask.duration; |
| 49 compiler.measuredTask = this; | |
| 50 if (previous != null) previous.watch.stop(); | |
| 51 watch.start(); | |
| 52 try { | |
| 53 return action(); | |
| 54 } finally { | |
| 55 watch.stop(); | |
| 56 if (previous != null) previous.watch.start(); | |
| 57 compiler.measuredTask = previous; | |
| 58 } | 64 } |
| 65 return total; | |
| 59 } | 66 } |
| 60 | 67 |
| 68 /// Perform [action] and use [watch] to measure its runtime (including any | |
| 69 /// asynchronous callbacks, such as, [Future.then], but excluding code | |
| 70 /// measured by other tasks). | |
| 71 measure(action()) => watch == null ? action() : measureZoned(action); | |
| 72 | |
| 73 /// Helper method that starts measuring with this [CompilerTask], that is, | |
| 74 /// make this task the currently measured task. | |
| 75 CompilerTask start() { | |
| 76 if (watch == null) return null; | |
| 77 CompilerTask previous = measurer.currentTask; | |
| 78 measurer.currentTask = this; | |
| 79 if (previous != null) previous.watch.stop(); | |
| 80 // Regardless of [previous] is `null` we've returned from the eventloop. | |
| 81 measurer.stopAsyncWallClock(); | |
| 82 watch.start(); | |
| 83 return previous; | |
| 84 } | |
| 85 | |
| 86 /// Helper method that stops measuring with this [CompilerTask], that is, | |
| 87 /// make [previous] the currently measured task. | |
| 88 void stop(CompilerTask previous) { | |
| 89 if (watch == null) return; | |
| 90 watch.stop(); | |
| 91 if (previous != null) { | |
| 92 previous.watch.start(); | |
| 93 } else { | |
| 94 // If there's no previous task, we're about to return control to the | |
| 95 // event loop. Start counting that as waiting asynchronous I/O. | |
| 96 measurer.startAsyncWallClock(); | |
| 97 } | |
| 98 measurer.currentTask = previous; | |
| 99 } | |
| 100 | |
| 101 /// Helper method for [measure]. Don't call this method directly as it | |
| 102 /// assumes that [watch] isn't null. | |
| 103 measureZoned(action()) { | |
| 104 // Using zones, we're able to track asynchronous operations correctly, as | |
| 105 // our zone will be asked to invoke `then` blocks. Then blocks (the closure | |
| 106 // passed to runZoned, and other closures) are run via the `run` functions | |
| 107 // below. | |
| 108 | |
| 109 assert(watch != null); | |
| 110 | |
| 111 // The current zone is already measuring `this` task. | |
| 112 if (Zone.current[measurer] == this) return action(); | |
| 113 | |
| 114 /// Run [f] in [zone]. Running must be delegated to [parent] to ensure that | |
| 115 /// various state is set up correctly (in particular in the root | |
|
floitsch
2016/01/07 14:40:39
in particular `Zone.current`
ahe
2016/01/08 09:02:02
Done.
| |
| 116 /// zone). Since [measureZoned] can be called recursively (synchronously), | |
| 117 /// some of the measuring zones we create will be parents of other | |
| 118 /// measuring zones, but we still need to call through the parent | |
| 119 /// chain. Consequently, we use a zone value keyed by [measurer] to see if | |
| 120 /// we should measure or not when delegating. | |
| 121 run(Zone self, ZoneDelegate parent, Zone zone, f()) { | |
| 122 if (zone[measurer] != this) return parent.run(zone, f); | |
| 123 CompilerTask previous = start(); | |
| 124 try { | |
| 125 return parent.run(zone, f); | |
| 126 } finally { | |
| 127 stop(previous); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 /// Equivalent to [run] except that [f] takes one argument, [arg]. | |
|
floitsch
2016/01/07 14:40:40
nit: s/Equivalent to/Same as/
ahe
2016/01/08 09:02:02
Done.
| |
| 132 runUnary(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) { | |
| 133 if (zone[measurer] != this) return parent.runUnary(zone, f, arg); | |
| 134 CompilerTask previous = start(); | |
| 135 try { | |
| 136 return parent.runUnary(zone, f, arg); | |
| 137 } finally { | |
| 138 stop(previous); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 /// Equivalent to [run] except that [f] takes two arguments ([a1] and [a2]). | |
| 143 runBinary(Zone self, ZoneDelegate parent, Zone zone, f(a1, a2), a1, a2) { | |
| 144 if (zone[measurer] != this) return parent.runBinary(zone, f, a1, a2); | |
| 145 CompilerTask previous = start(); | |
| 146 try { | |
| 147 return parent.runBinary(zone, f, a1, a2); | |
| 148 } finally { | |
| 149 stop(previous); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 return runZoned( | |
| 154 action, | |
| 155 zoneValues: { measurer: this }, | |
| 156 zoneSpecification: new ZoneSpecification( | |
| 157 run: run, runUnary: runUnary, runBinary: runBinary)); | |
| 158 } | |
| 159 | |
| 160 /// Asynchronous version of [measure]. Use this when action returns a future | |
| 161 /// that's truly asynchronous, such I/O. Only one task can use this method | |
| 162 /// concurrently. | |
| 163 /// Note: we assume that this method is used only by the compiler input | |
|
floitsch
2016/01/07 14:40:39
New line before "Note:".
ahe
2016/01/08 09:02:02
Done.
| |
| 164 /// provider, but it could be used by other tasks as long as the input | |
| 165 /// provider will not be called by those tasks. | |
| 166 measureIo(Future action()) { | |
| 167 return watch == null ? action() : measureIoHelper(action); | |
| 168 } | |
| 169 | |
| 170 /// Helper method for [measureIo]. Don't call this directly as it assumes | |
| 171 /// that [watch] isn't null. | |
| 172 Future measureIoHelper(Future action()) { | |
| 173 assert(watch != null); | |
| 174 if (measurer.currentAsyncTask == null) { | |
| 175 measurer.currentAsyncTask = this; | |
| 176 } else if (measurer.currentAsyncTask != this) { | |
| 177 throw "Can't track async task '$name' because" | |
|
floitsch
2016/01/07 14:40:39
Generally we avoid throwing just strings.
This loo
ahe
2016/01/08 09:02:02
Why?
floitsch
2016/01/08 12:07:48
Long thread on dart-readability, and Bob is in the
ahe
2016/01/08 12:23:45
I disagree. This error message is harder to read:
| |
| 178 " '${measurer.currentAsyncTask.name}' is already being tracked."; | |
| 179 } | |
| 180 asyncCount++; | |
| 181 return measure(action).whenComplete(() { | |
| 182 asyncCount--; | |
| 183 if (asyncCount == 0) measurer.currentAsyncTask = null; | |
| 184 }); | |
| 185 } | |
| 186 | |
| 187 /// Convenience function for combining | |
| 188 /// [DiagnosticReporter.withCurrentElement] and [measure]. | |
| 61 measureElement(Element element, action()) { | 189 measureElement(Element element, action()) { |
| 62 reporter.withCurrentElement(element, () => measure(action)); | 190 return watch == null |
| 191 ? reporter.withCurrentElement(element, action) | |
| 192 : measureElementHelper(element, action); | |
| 193 } | |
| 194 | |
| 195 /// Helper method for [measureElement]. Don't call this directly as it | |
| 196 /// assumes that [watch] isn't null. | |
| 197 measureElementHelper(Element element, action()) { | |
| 198 assert(watch != null); | |
| 199 return reporter.withCurrentElement(element, () => measure(action)); | |
| 63 } | 200 } |
| 64 | 201 |
| 65 /// Measure the time spent in [action] (if in verbose mode) and accumulate it | 202 /// Measure the time spent in [action] (if in verbose mode) and accumulate it |
| 66 /// under a subtask with the given name. | 203 /// under a subtask with the given name. |
| 67 measureSubtask(String name, action()) { | 204 measureSubtask(String name, action()) { |
| 68 if (watch == null) return action(); | 205 return watch == null ? action() : measureSubtaskHelper(name, action); |
| 206 } | |
| 207 | |
| 208 /// Helper method for [measureSubtask]. Don't call this directly as it | |
| 209 /// assumes that [watch] isn't null. | |
| 210 measureSubtaskHelper(String name, action()) { | |
| 211 assert(watch != null); | |
| 69 // Use a nested CompilerTask for the measurement to ensure nested [measure] | 212 // Use a nested CompilerTask for the measurement to ensure nested [measure] |
| 70 // calls work correctly. The subtasks will never themselves have nested | 213 // calls work correctly. The subtasks will never themselves have nested |
| 71 // subtasks because they are not accessible outside. | 214 // subtasks because they are not accessible outside. |
| 72 GenericTask subtask = _subtasks.putIfAbsent(name, | 215 GenericTask subtask = _subtasks.putIfAbsent(name, |
| 73 () => new GenericTask(name, compiler)); | 216 () => new GenericTask(name, compiler)); |
| 74 return subtask.measure(action); | 217 return subtask.measure(action); |
| 75 } | 218 } |
| 76 | 219 |
| 77 Iterable<String> get subtasks => _subtasks.keys; | 220 Iterable<String> get subtasks => _subtasks.keys; |
| 78 | 221 |
| 79 int getSubtaskTime(String subtask) => _subtasks[subtask].timing; | 222 int getSubtaskTime(String subtask) => _subtasks[subtask].timing; |
| 223 | |
| 224 bool getSubtaskIsRunning(String subtask) => _subtasks[subtask].isRunning; | |
| 80 } | 225 } |
| 81 | 226 |
| 82 class GenericTask extends CompilerTask { | 227 class GenericTask extends CompilerTask { |
| 83 final String name; | 228 final String name; |
| 84 | 229 |
| 85 GenericTask(this.name, Compiler compiler) | 230 GenericTask(this.name, Compiler compiler) |
| 86 : super(compiler); | 231 : super(compiler); |
| 87 } | 232 } |
| 233 | |
| 234 class Measurer { | |
| 235 // Constructor must be first to ensure [wallclock] is started before other | |
|
floitsch
2016/01/07 14:40:39
I'm not sure I understand the comment. It reads as
ahe
2016/01/08 09:02:02
This is from the Dart Programming Language Specifi
| |
| 236 // computations. | |
| 237 Measurer() | |
| 238 : wallClock = new Stopwatch()..start(), | |
| 239 asyncWallClock = new Stopwatch(); | |
| 240 | |
| 241 /// Measures the total runtime from this object was constructed. | |
| 242 final Stopwatch wallClock; | |
| 243 | |
| 244 /// Measures gaps between zoned closures due to asynchronicity. | |
| 245 final Stopwatch asyncWallClock; | |
| 246 | |
| 247 /// The currently running task, that is, the task whose [Stopwatch] is | |
| 248 /// currently running. | |
| 249 CompilerTask currentTask; | |
| 250 | |
| 251 /// The current task which should be charged for asynchronous gaps. | |
| 252 CompilerTask currentAsyncTask; | |
| 253 | |
| 254 /// Start counting the total elapsed time since the compiler started. | |
| 255 void startWallClock() { | |
| 256 wallClock.start(); | |
| 257 } | |
| 258 | |
| 259 /// Start counting the total elapsed time since the compiler started. | |
| 260 void stopWallClock() { | |
| 261 wallClock.stop(); | |
| 262 } | |
| 263 | |
| 264 /// Call this before returning to the eventloop. | |
| 265 void startAsyncWallClock() { | |
| 266 if (currentAsyncTask != null) { | |
| 267 currentAsyncTask.watch.start(); | |
| 268 } else { | |
| 269 asyncWallClock.start(); | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 /// Call this when the eventloop returns control to us. | |
| 274 void stopAsyncWallClock() { | |
| 275 if (currentAsyncTask != null) { | |
| 276 currentAsyncTask.watch.stop(); | |
| 277 } | |
| 278 asyncWallClock.stop(); | |
| 279 } | |
| 280 } | |
| OLD | NEW |