Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(459)

Side by Side Diff: pkg/compiler/lib/src/common/tasks.dart

Issue 1454373002: Use Zone to correctly measure async operations. (Closed) Base URL: git@github.com:dart-lang/sdk.git@_temporary_fletch_patches
Patch Set: Address Florian's comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/compiler/lib/src/apiimpl.dart ('k') | pkg/compiler/lib/src/compiler.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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?.isRunning == true;
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 that `Zone.current`
116 /// has the right value). Since [measureZoned] can be called recursively
117 /// (synchronously), some of the measuring zones we create will be parents
118 /// of other 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 /// Same as [run] except that [f] takes one argument, [arg].
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 /// Same as [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 ///
164 /// Note: we assume that this method is used only by the compiler input
165 /// provider, but it could be used by other tasks as long as the input
166 /// provider will not be called by those tasks.
167 measureIo(Future action()) {
168 return watch == null ? action() : measureIoHelper(action);
169 }
170
171 /// Helper method for [measureIo]. Don't call this directly as it assumes
172 /// that [watch] isn't null.
173 Future measureIoHelper(Future action()) {
174 assert(watch != null);
175 if (measurer.currentAsyncTask == null) {
176 measurer.currentAsyncTask = this;
177 } else if (measurer.currentAsyncTask != this) {
178 throw "Can't track async task '$name' because"
179 " '${measurer.currentAsyncTask.name}' is already being tracked.";
180 }
181 asyncCount++;
182 return measure(action).whenComplete(() {
183 asyncCount--;
184 if (asyncCount == 0) measurer.currentAsyncTask = null;
185 });
186 }
187
188 /// Convenience function for combining
189 /// [DiagnosticReporter.withCurrentElement] and [measure].
61 measureElement(Element element, action()) { 190 measureElement(Element element, action()) {
62 reporter.withCurrentElement(element, () => measure(action)); 191 return watch == null
192 ? reporter.withCurrentElement(element, action)
193 : measureElementHelper(element, action);
194 }
195
196 /// Helper method for [measureElement]. Don't call this directly as it
197 /// assumes that [watch] isn't null.
198 measureElementHelper(Element element, action()) {
199 assert(watch != null);
200 return reporter.withCurrentElement(element, () => measure(action));
63 } 201 }
64 202
65 /// Measure the time spent in [action] (if in verbose mode) and accumulate it 203 /// Measure the time spent in [action] (if in verbose mode) and accumulate it
66 /// under a subtask with the given name. 204 /// under a subtask with the given name.
67 measureSubtask(String name, action()) { 205 measureSubtask(String name, action()) {
68 if (watch == null) return action(); 206 return watch == null ? action() : measureSubtaskHelper(name, action);
207 }
208
209 /// Helper method for [measureSubtask]. Don't call this directly as it
210 /// assumes that [watch] isn't null.
211 measureSubtaskHelper(String name, action()) {
212 assert(watch != null);
69 // Use a nested CompilerTask for the measurement to ensure nested [measure] 213 // Use a nested CompilerTask for the measurement to ensure nested [measure]
70 // calls work correctly. The subtasks will never themselves have nested 214 // calls work correctly. The subtasks will never themselves have nested
71 // subtasks because they are not accessible outside. 215 // subtasks because they are not accessible outside.
72 GenericTask subtask = _subtasks.putIfAbsent(name, 216 GenericTask subtask = _subtasks.putIfAbsent(name,
73 () => new GenericTask(name, compiler)); 217 () => new GenericTask(name, compiler));
74 return subtask.measure(action); 218 return subtask.measure(action);
75 } 219 }
76 220
77 Iterable<String> get subtasks => _subtasks.keys; 221 Iterable<String> get subtasks => _subtasks.keys;
78 222
79 int getSubtaskTime(String subtask) => _subtasks[subtask].timing; 223 int getSubtaskTime(String subtask) => _subtasks[subtask].timing;
224
225 bool getSubtaskIsRunning(String subtask) => _subtasks[subtask].isRunning;
80 } 226 }
81 227
82 class GenericTask extends CompilerTask { 228 class GenericTask extends CompilerTask {
83 final String name; 229 final String name;
84 230
85 GenericTask(this.name, Compiler compiler) 231 GenericTask(this.name, Compiler compiler)
86 : super(compiler); 232 : super(compiler);
87 } 233 }
234
235 class Measurer {
236 /// Measures the total runtime from this object was constructed.
237 ///
238 /// Note: MUST be first field to ensure [wallclock] is started before other
239 /// computations.
240 final Stopwatch wallClock = new Stopwatch()..start();
241
242 /// Measures gaps between zoned closures due to asynchronicity.
243 final Stopwatch asyncWallClock = new Stopwatch();
244
245 /// The currently running task, that is, the task whose [Stopwatch] is
246 /// currently running.
247 CompilerTask currentTask;
248
249 /// The current task which should be charged for asynchronous gaps.
250 CompilerTask currentAsyncTask;
251
252 /// Start counting the total elapsed time since the compiler started.
253 void startWallClock() {
254 wallClock.start();
255 }
256
257 /// Start counting the total elapsed time since the compiler started.
258 void stopWallClock() {
259 wallClock.stop();
260 }
261
262 /// Call this before returning to the eventloop.
263 void startAsyncWallClock() {
264 if (currentAsyncTask != null) {
265 currentAsyncTask.watch.start();
266 } else {
267 asyncWallClock.start();
268 }
269 }
270
271 /// Call this when the eventloop returns control to us.
272 void stopAsyncWallClock() {
273 if (currentAsyncTask != null) {
274 currentAsyncTask.watch.stop();
275 }
276 asyncWallClock.stop();
277 }
278 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/apiimpl.dart ('k') | pkg/compiler/lib/src/compiler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698