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' | 7 import 'dart:async' |
| 8 show Future, Zone, ZoneDelegate, ZoneSpecification, runZoned; | 8 show Future, Zone, ZoneDelegate, ZoneSpecification, runZoned; |
| 9 | 9 |
| 10 import '../common.dart'; | 10 /// Used to measure where time is spent in the compiler. |
| 11 import '../compiler.dart' show Compiler; | 11 /// |
| 12 import '../elements/elements.dart' show Element; | 12 /// This exposes [measure] and [measureIo], which wrap an action and assotiate |
|
Harry Terkelsen
2016/05/25 15:51:36
assotiate -> associate
Siggi Cherem (dart-lang)
2016/05/25 16:11:18
Done.
| |
| 13 | 13 /// the time spend during that action with this task. Nested measurements can be |
|
Harry Terkelsen
2016/05/25 15:51:36
spend -> spent
Siggi Cherem (dart-lang)
2016/05/25 16:11:17
Done.
| |
| 14 typedef void DeferredAction(); | 14 /// introduced by using [measureSubtask]. |
| 15 | 15 // TODO(sigmund): rename to MeasurableTask |
| 16 class DeferredTask { | 16 abstract class CompilerTask { |
|
Siggi Cherem (dart-lang)
2016/05/25 01:45:17
this is unrelated to the other kind of task, I mov
Johnni Winther
2016/05/25 09:02:00
Acknowledged.
| |
| 17 final Element element; | 17 final Measurer measurer; |
| 18 final DeferredAction action; | 18 final Stopwatch _watch; |
| 19 | |
| 20 DeferredTask(this.element, this.action); | |
| 21 } | |
| 22 | |
| 23 /// A [CompilerTask] is used to measure where time is spent in the compiler. | |
| 24 /// The main entry points are [measure] and [measureIo]. | |
| 25 class CompilerTask { | |
| 26 final Compiler compiler; | |
| 27 final Stopwatch watch; | |
| 28 final Map<String, GenericTask> _subtasks = <String, GenericTask>{}; | 19 final Map<String, GenericTask> _subtasks = <String, GenericTask>{}; |
| 29 | 20 |
| 30 int asyncCount = 0; | 21 int asyncCount = 0; |
| 31 | 22 |
| 32 CompilerTask(Compiler compiler) | 23 CompilerTask(Measurer measurer) |
| 33 : this.compiler = compiler, | 24 : measurer = measurer, |
| 34 watch = (compiler.options.verbose) ? new Stopwatch() : null; | 25 _watch = measurer.enableTaskMeasurements ? new Stopwatch() : null; |
| 35 | 26 |
| 36 DiagnosticReporter get reporter => compiler.reporter; | 27 /// Whether measurement is disabled. The functions [measure] and [measureIo] |
| 28 /// only measure time if measurements are enabled. | |
| 29 bool get _isDisabled => _watch == null; | |
| 37 | 30 |
| 38 Measurer get measurer => compiler.measurer; | 31 /// Name to use for reporting timing information. |
| 39 | 32 // Note: it would be sufficient to mark this getter abstract if we had enough |
|
Harry Terkelsen
2016/05/25 15:51:36
this note seems more like a TODO
Siggi Cherem (dart-lang)
2016/05/25 16:11:17
Yeah, not sure if it's worth deleting. Rephrase th
| |
| 33 // static checking to ensure that all subclasses override it. | |
| 40 String get name => "Unknown task '${this.runtimeType}'"; | 34 String get name => "Unknown task '${this.runtimeType}'"; |
| 41 | 35 |
| 42 bool get isRunning => watch?.isRunning == true; | 36 bool get isRunning => _watch?.isRunning == true; |
| 43 | 37 |
| 44 int get timing { | 38 int get timing { |
| 45 if (watch == null) return 0; | 39 if (_isDisabled) return 0; |
| 46 int total = watch.elapsedMilliseconds; | 40 int total = _watch.elapsedMilliseconds; |
| 47 for (GenericTask subtask in _subtasks.values) { | 41 for (GenericTask subtask in _subtasks.values) { |
| 48 total += subtask.timing; | 42 total += subtask.timing; |
| 49 } | 43 } |
| 50 return total; | 44 return total; |
| 51 } | 45 } |
| 52 | 46 |
| 53 Duration get duration { | 47 Duration get duration { |
| 54 if (watch == null) return Duration.ZERO; | 48 if (_isDisabled) return Duration.ZERO; |
| 55 Duration total = watch.elapsed; | 49 Duration total = _watch.elapsed; |
| 56 for (GenericTask subtask in _subtasks.values) { | 50 for (GenericTask subtask in _subtasks.values) { |
| 57 total += subtask.duration; | 51 total += subtask.duration; |
| 58 } | 52 } |
| 59 return total; | 53 return total; |
| 60 } | 54 } |
| 61 | 55 |
| 62 /// Perform [action] and use [watch] to measure its runtime (including any | 56 /// Perform [action] and use [_watch] to measure its runtime (including any |
|
Harry Terkelsen
2016/05/25 15:51:36
maybe remove the part about _watch in the docs sin
Siggi Cherem (dart-lang)
2016/05/25 16:11:18
good point, done.
| |
| 63 /// asynchronous callbacks, such as, [Future.then], but excluding code | 57 /// asynchronous callbacks, such as, [Future.then], but excluding code |
| 64 /// measured by other tasks). | 58 /// measured by other tasks). |
| 65 measure(action()) => watch == null ? action() : measureZoned(action); | 59 measure(action()) => _isDisabled ? action() : _measureZoned(action); |
| 66 | 60 |
| 67 /// Helper method that starts measuring with this [CompilerTask], that is, | 61 /// Helper method that starts measuring with this [CompilerTask], that is, |
| 68 /// make this task the currently measured task. | 62 /// make this task the currently measured task. |
| 69 CompilerTask start() { | 63 CompilerTask _start() { |
| 70 if (watch == null) return null; | 64 if (_isDisabled) return null; |
| 71 CompilerTask previous = measurer.currentTask; | 65 CompilerTask previous = measurer.currentTask; |
| 72 measurer.currentTask = this; | 66 measurer.currentTask = this; |
| 73 if (previous != null) previous.watch.stop(); | 67 if (previous != null) previous._watch.stop(); |
| 74 // Regardless of whether [previous] is `null` we've returned from the | 68 // Regardless of whether [previous] is `null` we've returned from the |
| 75 // eventloop. | 69 // eventloop. |
| 76 measurer.stopAsyncWallClock(); | 70 measurer.stopAsyncWallClock(); |
| 77 watch.start(); | 71 _watch.start(); |
| 78 return previous; | 72 return previous; |
| 79 } | 73 } |
| 80 | 74 |
| 81 /// Helper method that stops measuring with this [CompilerTask], that is, | 75 /// Helper method that stops measuring with this [CompilerTask], that is, |
| 82 /// make [previous] the currently measured task. | 76 /// make [previous] the currently measured task. |
| 83 void stop(CompilerTask previous) { | 77 void _stop(CompilerTask previous) { |
| 84 if (watch == null) return; | 78 if (_isDisabled) return; |
| 85 watch.stop(); | 79 _watch.stop(); |
| 86 if (previous != null) { | 80 if (previous != null) { |
| 87 previous.watch.start(); | 81 previous._watch.start(); |
| 88 } else { | 82 } else { |
| 89 // If there's no previous task, we're about to return control to the | 83 // If there's no previous task, we're about to return control to the |
| 90 // event loop. Start counting that as waiting asynchronous I/O. | 84 // event loop. Start counting that as waiting asynchronous I/O. |
| 91 measurer.startAsyncWallClock(); | 85 measurer.startAsyncWallClock(); |
| 92 } | 86 } |
| 93 measurer.currentTask = previous; | 87 measurer.currentTask = previous; |
| 94 } | 88 } |
| 95 | 89 |
| 96 /// Helper method for [measure]. Don't call this method directly as it | 90 _measureZoned(action()) { |
| 97 /// assumes that [watch] isn't null. | |
| 98 measureZoned(action()) { | |
| 99 // Using zones, we're able to track asynchronous operations correctly, as | 91 // Using zones, we're able to track asynchronous operations correctly, as |
| 100 // our zone will be asked to invoke `then` blocks. Then blocks (the closure | 92 // our zone will be asked to invoke `then` blocks. Then blocks (the closure |
| 101 // passed to runZoned, and other closures) are run via the `run` functions | 93 // passed to runZoned, and other closures) are run via the `run` functions |
| 102 // below. | 94 // below. |
| 103 | 95 |
| 104 assert(watch != null); | 96 assert(_watch != null); |
| 105 | 97 |
| 106 // The current zone is already measuring `this` task. | 98 // The current zone is already measuring `this` task. |
| 107 if (Zone.current[measurer] == this) return action(); | 99 if (Zone.current[measurer] == this) return action(); |
| 108 | 100 |
| 109 /// Run [f] in [zone]. Running must be delegated to [parent] to ensure that | 101 /// Run [f] in [zone]. Running must be delegated to [parent] to ensure that |
| 110 /// various state is set up correctly (in particular that `Zone.current` | 102 /// various state is set up correctly (in particular that `Zone.current` |
| 111 /// has the right value). Since [measureZoned] can be called recursively | 103 /// has the right value). Since [_measureZoned] can be called recursively |
| 112 /// (synchronously), some of the measuring zones we create will be parents | 104 /// (synchronously), some of the measuring zones we create will be parents |
| 113 /// of other measuring zones, but we still need to call through the parent | 105 /// of other measuring zones, but we still need to call through the parent |
| 114 /// chain. Consequently, we use a zone value keyed by [measurer] to see if | 106 /// chain. Consequently, we use a zone value keyed by [measurer] to see if |
| 115 /// we should measure or not when delegating. | 107 /// we should measure or not when delegating. |
| 116 run(Zone self, ZoneDelegate parent, Zone zone, f()) { | 108 run(Zone self, ZoneDelegate parent, Zone zone, f()) { |
| 117 if (zone[measurer] != this) return parent.run(zone, f); | 109 if (zone[measurer] != this) return parent.run(zone, f); |
| 118 CompilerTask previous = start(); | 110 CompilerTask previous = _start(); |
| 119 try { | 111 try { |
| 120 return parent.run(zone, f); | 112 return parent.run(zone, f); |
| 121 } finally { | 113 } finally { |
| 122 stop(previous); | 114 _stop(previous); |
| 123 } | 115 } |
| 124 } | 116 } |
| 125 | 117 |
| 126 /// Same as [run] except that [f] takes one argument, [arg]. | 118 /// Same as [run] except that [f] takes one argument, [arg]. |
| 127 runUnary(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) { | 119 runUnary(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) { |
| 128 if (zone[measurer] != this) return parent.runUnary(zone, f, arg); | 120 if (zone[measurer] != this) return parent.runUnary(zone, f, arg); |
| 129 CompilerTask previous = start(); | 121 CompilerTask previous = _start(); |
| 130 try { | 122 try { |
| 131 return parent.runUnary(zone, f, arg); | 123 return parent.runUnary(zone, f, arg); |
| 132 } finally { | 124 } finally { |
| 133 stop(previous); | 125 _stop(previous); |
| 134 } | 126 } |
| 135 } | 127 } |
| 136 | 128 |
| 137 /// Same as [run] except that [f] takes two arguments ([a1] and [a2]). | 129 /// Same as [run] except that [f] takes two arguments ([a1] and [a2]). |
| 138 runBinary(Zone self, ZoneDelegate parent, Zone zone, f(a1, a2), a1, a2) { | 130 runBinary(Zone self, ZoneDelegate parent, Zone zone, f(a1, a2), a1, a2) { |
| 139 if (zone[measurer] != this) return parent.runBinary(zone, f, a1, a2); | 131 if (zone[measurer] != this) return parent.runBinary(zone, f, a1, a2); |
| 140 CompilerTask previous = start(); | 132 CompilerTask previous = _start(); |
| 141 try { | 133 try { |
| 142 return parent.runBinary(zone, f, a1, a2); | 134 return parent.runBinary(zone, f, a1, a2); |
| 143 } finally { | 135 } finally { |
| 144 stop(previous); | 136 _stop(previous); |
| 145 } | 137 } |
| 146 } | 138 } |
| 147 | 139 |
| 148 return runZoned(action, | 140 return runZoned(action, |
| 149 zoneValues: {measurer: this}, | 141 zoneValues: {measurer: this}, |
| 150 zoneSpecification: new ZoneSpecification( | 142 zoneSpecification: new ZoneSpecification( |
| 151 run: run, runUnary: runUnary, runBinary: runBinary)); | 143 run: run, runUnary: runUnary, runBinary: runBinary)); |
| 152 } | 144 } |
| 153 | 145 |
| 154 /// Asynchronous version of [measure]. Use this when action returns a future | 146 /// Asynchronous version of [measure]. Use this when action returns a future |
| 155 /// that's truly asynchronous, such I/O. Only one task can use this method | 147 /// that's truly asynchronous, such I/O. Only one task can use this method |
| 156 /// concurrently. | 148 /// concurrently. |
| 157 /// | 149 /// |
| 158 /// Note: we assume that this method is used only by the compiler input | 150 /// Note: we assume that this method is used only by the compiler input |
| 159 /// provider, but it could be used by other tasks as long as the input | 151 /// provider, but it could be used by other tasks as long as the input |
| 160 /// provider will not be called by those tasks. | 152 /// provider will not be called by those tasks. |
| 161 measureIo(Future action()) { | 153 measureIo(Future action()) { |
| 162 return watch == null ? action() : measureIoHelper(action); | 154 if (_isDisabled) return action(); |
| 163 } | |
| 164 | 155 |
| 165 /// Helper method for [measureIo]. Don't call this directly as it assumes | |
| 166 /// that [watch] isn't null. | |
| 167 Future measureIoHelper(Future action()) { | |
| 168 assert(watch != null); | |
| 169 if (measurer.currentAsyncTask == null) { | 156 if (measurer.currentAsyncTask == null) { |
| 170 measurer.currentAsyncTask = this; | 157 measurer.currentAsyncTask = this; |
| 171 } else if (measurer.currentAsyncTask != this) { | 158 } else if (measurer.currentAsyncTask != this) { |
| 172 throw "Can't track async task '$name' because" | 159 throw "Can't track async task '$name' because" |
| 173 " '${measurer.currentAsyncTask.name}' is already being tracked."; | 160 " '${measurer.currentAsyncTask.name}' is already being tracked."; |
| 174 } | 161 } |
| 175 asyncCount++; | 162 asyncCount++; |
| 176 return measure(action).whenComplete(() { | 163 return measure(action).whenComplete(() { |
| 177 asyncCount--; | 164 asyncCount--; |
| 178 if (asyncCount == 0) measurer.currentAsyncTask = null; | 165 if (asyncCount == 0) measurer.currentAsyncTask = null; |
| 179 }); | 166 }); |
| 180 } | 167 } |
| 181 | 168 |
| 182 /// Convenience function for combining | |
| 183 /// [DiagnosticReporter.withCurrentElement] and [measure]. | |
| 184 measureElement(Element element, action()) { | |
| 185 return watch == null | |
| 186 ? reporter.withCurrentElement(element, action) | |
| 187 : measureElementHelper(element, action); | |
| 188 } | |
| 189 | |
| 190 /// Helper method for [measureElement]. Don't call this directly as it | |
| 191 /// assumes that [watch] isn't null. | |
| 192 measureElementHelper(Element element, action()) { | |
| 193 assert(watch != null); | |
| 194 return reporter.withCurrentElement(element, () => measure(action)); | |
| 195 } | |
| 196 | |
| 197 /// Measure the time spent in [action] (if in verbose mode) and accumulate it | 169 /// Measure the time spent in [action] (if in verbose mode) and accumulate it |
| 198 /// under a subtask with the given name. | 170 /// under a subtask with the given name. |
| 199 measureSubtask(String name, action()) { | 171 measureSubtask(String name, action()) { |
| 200 return watch == null ? action() : measureSubtaskHelper(name, action); | 172 if (_isDisabled) return action(); |
| 201 } | |
| 202 | 173 |
| 203 /// Helper method for [measureSubtask]. Don't call this directly as it | |
| 204 /// assumes that [watch] isn't null. | |
| 205 measureSubtaskHelper(String name, action()) { | |
| 206 assert(watch != null); | |
| 207 // Use a nested CompilerTask for the measurement to ensure nested [measure] | 174 // Use a nested CompilerTask for the measurement to ensure nested [measure] |
| 208 // calls work correctly. The subtasks will never themselves have nested | 175 // calls work correctly. The subtasks will never themselves have nested |
| 209 // subtasks because they are not accessible outside. | 176 // subtasks because they are not accessible outside. |
| 210 GenericTask subtask = | 177 GenericTask subtask = |
| 211 _subtasks.putIfAbsent(name, () => new GenericTask(name, compiler)); | 178 _subtasks.putIfAbsent(name, () => new GenericTask(name, measurer)); |
| 212 return subtask.measure(action); | 179 return subtask.measure(action); |
| 213 } | 180 } |
| 214 | 181 |
| 215 Iterable<String> get subtasks => _subtasks.keys; | 182 Iterable<String> get subtasks => _subtasks.keys; |
| 216 | 183 |
| 217 int getSubtaskTime(String subtask) => _subtasks[subtask].timing; | 184 int getSubtaskTime(String subtask) => _subtasks[subtask].timing; |
| 218 | 185 |
| 219 bool getSubtaskIsRunning(String subtask) => _subtasks[subtask].isRunning; | 186 bool getSubtaskIsRunning(String subtask) => _subtasks[subtask].isRunning; |
| 187 | |
| 188 /// Used by the dart2js_incremental to provide measurements on each | |
| 189 /// incremental compile. | |
| 190 clearMeasurements() { | |
| 191 if (_isDisabled) return; | |
| 192 _watch.reset(); | |
| 193 _subtasks.values.forEach((s) => s.clearMeasurements()); | |
| 194 } | |
| 220 } | 195 } |
| 221 | 196 |
| 222 class GenericTask extends CompilerTask { | 197 class GenericTask extends CompilerTask { |
| 223 final String name; | 198 final String name; |
| 224 | 199 GenericTask(this.name, Measurer measurer) : super(measurer); |
| 225 GenericTask(this.name, Compiler compiler) : super(compiler); | |
| 226 } | 200 } |
| 227 | 201 |
| 228 class Measurer { | 202 class Measurer { |
| 229 /// Measures the total runtime from this object was constructed. | 203 /// Measures the total runtime from this object was constructed. |
| 230 /// | 204 /// |
| 231 /// Note: MUST be first field to ensure [wallclock] is started before other | 205 /// Note: MUST be the first field of this class to ensure [wallclock] is |
| 232 /// computations. | 206 /// started before other computations. |
| 233 final Stopwatch wallClock = new Stopwatch()..start(); | 207 final Stopwatch wallClock = new Stopwatch()..start(); |
| 234 | 208 |
| 235 /// Measures gaps between zoned closures due to asynchronicity. | 209 /// Measures gaps between zoned closures due to asynchronicity. |
| 236 final Stopwatch asyncWallClock = new Stopwatch(); | 210 final Stopwatch asyncWallClock = new Stopwatch(); |
| 237 | 211 |
| 212 /// Whether measurement of tasks is enabled. | |
| 213 final bool enableTaskMeasurements; | |
| 214 | |
| 215 Measurer({this.enableTaskMeasurements: false}); | |
| 216 | |
| 238 /// The currently running task, that is, the task whose [Stopwatch] is | 217 /// The currently running task, that is, the task whose [Stopwatch] is |
| 239 /// currently running. | 218 /// currently running. |
| 240 CompilerTask currentTask; | 219 CompilerTask currentTask; |
| 241 | 220 |
| 242 /// The current task which should be charged for asynchronous gaps. | 221 /// The current task which should be charged for asynchronous gaps. |
| 243 CompilerTask currentAsyncTask; | 222 CompilerTask currentAsyncTask; |
| 244 | 223 |
| 245 /// Start counting the total elapsed time since the compiler started. | 224 /// Start counting the total elapsed time since the compiler started. |
| 246 void startWallClock() { | 225 void startWallClock() { |
| 247 wallClock.start(); | 226 wallClock.start(); |
| 248 } | 227 } |
| 249 | 228 |
| 250 /// Start counting the total elapsed time since the compiler started. | 229 /// Start counting the total elapsed time since the compiler started. |
| 251 void stopWallClock() { | 230 void stopWallClock() { |
| 252 wallClock.stop(); | 231 wallClock.stop(); |
| 253 } | 232 } |
| 254 | 233 |
| 255 /// Call this before returning to the eventloop. | 234 /// Call this before returning to the eventloop. |
| 256 void startAsyncWallClock() { | 235 void startAsyncWallClock() { |
| 257 if (currentAsyncTask != null) { | 236 if (currentAsyncTask != null) { |
| 258 currentAsyncTask.watch.start(); | 237 currentAsyncTask._watch.start(); |
| 259 } else { | 238 } else { |
| 260 asyncWallClock.start(); | 239 asyncWallClock.start(); |
| 261 } | 240 } |
| 262 } | 241 } |
| 263 | 242 |
| 264 /// Call this when the eventloop returns control to us. | 243 /// Call this when the eventloop returns control to us. |
| 265 void stopAsyncWallClock() { | 244 void stopAsyncWallClock() { |
| 266 if (currentAsyncTask != null) { | 245 if (currentAsyncTask != null) { |
| 267 currentAsyncTask.watch.stop(); | 246 currentAsyncTask._watch.stop(); |
| 268 } | 247 } |
| 269 asyncWallClock.stop(); | 248 asyncWallClock.stop(); |
| 270 } | 249 } |
| 271 } | 250 } |
| OLD | NEW |