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

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

Issue 1898043004: Use Zone to correctly measure async operations. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Add setupDuration to computeTimings Created 4 years, 8 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:developer' show UserTag; 7 import 'dart:async' show
8 Future,
9 Zone,
10 ZoneDelegate,
11 ZoneSpecification,
12 runZoned;
8 13
9 import '../common.dart'; 14 import '../common.dart';
10 import '../compiler.dart' show Compiler; 15 import '../compiler.dart' show Compiler;
11 import '../elements/elements.dart' show Element; 16 import '../elements/elements.dart' show Element;
12 17
13 typedef void DeferredAction(); 18 typedef void DeferredAction();
14 19
15 class DeferredTask { 20 class DeferredTask {
16 final Element element; 21 final Element element;
17 final DeferredAction action; 22 final DeferredAction action;
18 23
19 DeferredTask(this.element, this.action); 24 DeferredTask(this.element, this.action);
20 } 25 }
21 26
27 /// A [CompilerTask] is used to measure where time is spent in the compiler.
28 /// The main entry points are [measure] and [measureIo].
22 class CompilerTask { 29 class CompilerTask {
23 final Compiler compiler; 30 final Compiler compiler;
24 final Stopwatch watch; 31 final Stopwatch watch;
25 UserTag profilerTag;
26 final Map<String, GenericTask> _subtasks = <String, GenericTask>{}; 32 final Map<String, GenericTask> _subtasks = <String, GenericTask>{};
27 33
34 int asyncCount = 0;
35
28 CompilerTask(Compiler compiler) 36 CompilerTask(Compiler compiler)
29 : this.compiler = compiler, 37 : this.compiler = compiler,
30 watch = (compiler.options.verbose) ? new Stopwatch() : null; 38 watch = (compiler.options.verbose) ? new Stopwatch() : null;
31 39
32 DiagnosticReporter get reporter => compiler.reporter; 40 DiagnosticReporter get reporter => compiler.reporter;
33 41
42 Measurer get measurer => compiler.measurer;
43
34 String get name => "Unknown task '${this.runtimeType}'"; 44 String get name => "Unknown task '${this.runtimeType}'";
35 45
46 bool get isRunning => watch?.isRunning == true;
47
36 int get timing { 48 int get timing {
37 if (watch == null) return 0; 49 if (watch == null) return 0;
38 int total = watch.elapsedMilliseconds; 50 int total = watch.elapsedMilliseconds;
39 for (GenericTask subtask in _subtasks.values) { 51 for (GenericTask subtask in _subtasks.values) {
40 total += subtask.timing; 52 total += subtask.timing;
41 } 53 }
42 return total; 54 return total;
43 } 55 }
44 56
45 UserTag getProfilerTag() { 57 Duration get duration {
46 if (profilerTag == null) profilerTag = new UserTag(name); 58 if (watch == null) return Duration.ZERO;
47 return profilerTag; 59 Duration total = watch.elapsed;
60 for (GenericTask subtask in _subtasks.values) {
61 total += subtask.duration;
62 }
63 return total;
48 } 64 }
49 65
50 measure(action()) { 66 /// Perform [action] and use [watch] to measure its runtime (including any
51 // In verbose mode when watch != null. 67 /// asynchronous callbacks, such as, [Future.then], but excluding code
52 if (watch == null) return action(); 68 /// measured by other tasks).
53 CompilerTask previous = compiler.measuredTask; 69 measure(action()) => watch == null ? action() : measureZoned(action);
54 if (identical(this, previous)) return action(); 70
55 compiler.measuredTask = this; 71 /// Helper method that starts measuring with this [CompilerTask], that is,
72 /// make this task the currently measured task.
73 CompilerTask start() {
74 if (watch == null) return null;
75 CompilerTask previous = measurer.currentTask;
76 measurer.currentTask = this;
56 if (previous != null) previous.watch.stop(); 77 if (previous != null) previous.watch.stop();
78 // Regardless of whether [previous] is `null` we've returned from the
79 // eventloop.
80 measurer.stopAsyncWallClock();
57 watch.start(); 81 watch.start();
58 UserTag oldTag = getProfilerTag().makeCurrent(); 82 return previous;
59 try {
60 return action();
61 } finally {
62 watch.stop();
63 oldTag.makeCurrent();
64 if (previous != null) previous.watch.start();
65 compiler.measuredTask = previous;
66 }
67 } 83 }
68 84
85 /// Helper method that stops measuring with this [CompilerTask], that is,
86 /// make [previous] the currently measured task.
87 void stop(CompilerTask previous) {
88 if (watch == null) return;
89 watch.stop();
90 if (previous != null) {
91 previous.watch.start();
92 } else {
93 // If there's no previous task, we're about to return control to the
94 // event loop. Start counting that as waiting asynchronous I/O.
95 measurer.startAsyncWallClock();
96 }
97 measurer.currentTask = previous;
98 }
99
100 /// Helper method for [measure]. Don't call this method directly as it
101 /// assumes that [watch] isn't null.
102 measureZoned(action()) {
103 // Using zones, we're able to track asynchronous operations correctly, as
104 // our zone will be asked to invoke `then` blocks. Then blocks (the closure
105 // passed to runZoned, and other closures) are run via the `run` functions
106 // below.
107
108 assert(watch != null);
109
110 // The current zone is already measuring `this` task.
111 if (Zone.current[measurer] == this) return action();
112
113 /// Run [f] in [zone]. Running must be delegated to [parent] to ensure that
114 /// various state is set up correctly (in particular that `Zone.current`
115 /// has the right value). Since [measureZoned] can be called recursively
116 /// (synchronously), some of the measuring zones we create will be parents
117 /// of other measuring zones, but we still need to call through the parent
118 /// chain. Consequently, we use a zone value keyed by [measurer] to see if
119 /// we should measure or not when delegating.
120 run(Zone self, ZoneDelegate parent, Zone zone, f()) {
121 if (zone[measurer] != this) return parent.run(zone, f);
122 CompilerTask previous = start();
123 try {
124 return parent.run(zone, f);
125 } finally {
126 stop(previous);
127 }
128 }
129
130 /// Same as [run] except that [f] takes one argument, [arg].
131 runUnary(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) {
132 if (zone[measurer] != this) return parent.runUnary(zone, f, arg);
133 CompilerTask previous = start();
134 try {
135 return parent.runUnary(zone, f, arg);
136 } finally {
137 stop(previous);
138 }
139 }
140
141 /// Same as [run] except that [f] takes two arguments ([a1] and [a2]).
142 runBinary(Zone self, ZoneDelegate parent, Zone zone, f(a1, a2), a1, a2) {
143 if (zone[measurer] != this) return parent.runBinary(zone, f, a1, a2);
144 CompilerTask previous = start();
145 try {
146 return parent.runBinary(zone, f, a1, a2);
147 } finally {
148 stop(previous);
149 }
150 }
151
152 return runZoned(
153 action,
154 zoneValues: { measurer: this },
155 zoneSpecification: new ZoneSpecification(
156 run: run, runUnary: runUnary, runBinary: runBinary));
157 }
158
159 /// Asynchronous version of [measure]. Use this when action returns a future
160 /// that's truly asynchronous, such I/O. Only one task can use this method
161 /// concurrently.
162 ///
163 /// Note: we assume that this method is used only by the compiler input
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"
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].
69 measureElement(Element element, action()) { 189 measureElement(Element element, action()) {
70 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));
71 } 200 }
72 201
73 /// 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
74 /// under a subtask with the given name. 203 /// under a subtask with the given name.
75 measureSubtask(String name, action()) { 204 measureSubtask(String name, action()) {
76 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);
77 // Use a nested CompilerTask for the measurement to ensure nested [measure] 212 // Use a nested CompilerTask for the measurement to ensure nested [measure]
78 // calls work correctly. The subtasks will never themselves have nested 213 // calls work correctly. The subtasks will never themselves have nested
79 // subtasks because they are not accessible outside. 214 // subtasks because they are not accessible outside.
80 GenericTask subtask = 215 GenericTask subtask =
81 _subtasks.putIfAbsent(name, () => new GenericTask(name, compiler)); 216 _subtasks.putIfAbsent(name, () => new GenericTask(name, compiler));
82 return subtask.measure(action); 217 return subtask.measure(action);
83 } 218 }
84 219
85 Iterable<String> get subtasks => _subtasks.keys; 220 Iterable<String> get subtasks => _subtasks.keys;
86 221
87 int getSubtaskTime(String subtask) => _subtasks[subtask].timing; 222 int getSubtaskTime(String subtask) => _subtasks[subtask].timing;
223
224 bool getSubtaskIsRunning(String subtask) => _subtasks[subtask].isRunning;
88 } 225 }
89 226
90 class GenericTask extends CompilerTask { 227 class GenericTask extends CompilerTask {
91 final String name; 228 final String name;
92 229
93 GenericTask(this.name, Compiler compiler) : super(compiler); 230 GenericTask(this.name, Compiler compiler) : super(compiler);
94 } 231 }
232
233 class Measurer {
234 /// Measures the total runtime from this object was constructed.
235 ///
236 /// Note: MUST be first field to ensure [wallclock] is started before other
237 /// computations.
238 final Stopwatch wallClock = new Stopwatch()..start();
239
240 /// Measures gaps between zoned closures due to asynchronicity.
241 final Stopwatch asyncWallClock = new Stopwatch();
242
243 /// The currently running task, that is, the task whose [Stopwatch] is
244 /// currently running.
245 CompilerTask currentTask;
246
247 /// The current task which should be charged for asynchronous gaps.
248 CompilerTask currentAsyncTask;
249
250 /// Start counting the total elapsed time since the compiler started.
251 void startWallClock() {
252 wallClock.start();
253 }
254
255 /// Start counting the total elapsed time since the compiler started.
256 void stopWallClock() {
257 wallClock.stop();
258 }
259
260 /// Call this before returning to the eventloop.
261 void startAsyncWallClock() {
262 if (currentAsyncTask != null) {
263 currentAsyncTask.watch.start();
264 } else {
265 asyncWallClock.start();
266 }
267 }
268
269 /// Call this when the eventloop returns control to us.
270 void stopAsyncWallClock() {
271 if (currentAsyncTask != null) {
272 currentAsyncTask.watch.stop();
273 }
274 asyncWallClock.stop();
275 }
276 }
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