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

Side by Side Diff: utils/tests/string_encoding/benchmark_runner.dart

Issue 9410001: restructure string decoding to support iterable use and include benchmarks for UTF-8 decoding. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: "stop using introduced variable _length. Improve docs. Created 8 years, 10 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 | Annotate | Revision Log
« no previous file with comments | « utils/string_encoding/utf8_impl.dart ('k') | utils/tests/string_encoding/dunit.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
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.
4
5 /**
6 * The results of a single block of tests (count times run, overall time).
7 */
8 class BlockSample {
9 BlockSample(this.count, this.durationNanos);
10 int count;
11 int durationNanos;
12
13 static int _totalCount(List<BlockSample> samples) =>
14 _sum(samples, int (BlockSample s) => s.count);
15
16 static int _totalTime(List<BlockSample> samples) =>
17 _sum(samples, int (BlockSample s) => s.durationNanos);
18
19 static BlockSample _select(List<BlockSample> samples,
20 BlockSample selector(BlockSample a, BlockSample b)) {
21 BlockSample r = null;
22 for (BlockSample s in samples) {
23 r = (r == null) ? s : selector(r, s);
24 }
25 return r;
26 }
27
28 static int _sum(List<BlockSample> samples, int extract(BlockSample s)) {
29 int total = 0;
30 for (BlockSample s in samples) {
31 total += extract(s);
32 }
33 return total;
34 }
35 }
36
37 /**
38 * Uses sample data to build a performance model for a test. Construct
39 * the model from a set of sample results, and it generates a simple
40 * predivtive model for execution of future requests. It uses
41 * a simple least-squares linear solution to build the model.
42 */
43 class PerformanceModel {
44 PerformanceModel.calculate(List<BlockSample> source) {
45 if (0 == source.length) {
46 throw "Missing data exception";
47 } else if (1 == source.length) {
48 overheadNanos = 0;
49 perRequestNanos = source[0].durationNanos / source[0].count;
50 } else {
51 double n = source.length.toDouble();
52 double sumY = BlockSample._totalTime(source).toDouble();
53 double sumXSquared = BlockSample._sum(source,
54 int _(BlockSample s) => s.count * s.count).toDouble();
55 double sumX = BlockSample._totalCount(source).toDouble();
56 double sumXY = BlockSample._sum(source,
57 int _(BlockSample s) => s.durationNanos * s.count).toDouble();
58
59 overheadNanos =
60 ((((sumY * sumXSquared) - (sumX * sumXY)) /
61 ((n * sumXSquared) - (sumX * sumX))) / source.length).toInt();
62
63 perRequestNanos =
64 (((n * sumXY) - (sumX * sumY)) /
65 ((n * sumXSquared) - (sumX * sumX))).toInt();
66 }
67 }
68
69 bool isValid() => overheadNanos >= 0 && perRequestNanos >= 0;
70
71 int overheadNanos;
72 int perRequestNanos;
73 int repsFor(int targetDurationNanos, [int blocksize = -1]) {
74 if (blocksize <= 0) {
75 return ((targetDurationNanos - overheadNanos) / perRequestNanos).toInt();
76 } else {
77 int blockTime = overheadNanos + (blocksize * perRequestNanos);
78 int fullBlocks = targetDurationNanos ~/ blockTime;
79 int extraReps =
80 ((targetDurationNanos - (fullBlocks * blockTime)) - overheadNanos)
81 ~/ perRequestNanos;
82 return ((fullBlocks * blocksize) + extraReps).toInt();
83 }
84 }
85 }
86
87 /**
88 * Report overall test performance
89 */
90 class TestReport {
91 TestReport(this.id, this.desc, this.warmup, this.results) {
92 spaceChar = " ".charCodes()[0];
93 }
94
95 int spaceChar;
96
97 int resultsCount() => BlockSample._totalCount(results);
98
99 int resultsNanos() => BlockSample._totalTime(results);
100
101 int resultsBestNanos() {
102 BlockSample best = bestBlock(results);
103 return best.durationNanos ~/ best.count;
104 }
105
106 int resultsMeanNanos() =>
107 (BlockSample._totalTime(results) /
108 BlockSample._totalCount(results)).toInt();
109
110 int resultsWorstNanos() {
111 BlockSample worst = worstBlock(results);
112 return worst.durationNanos / worst.count;
113 }
114
115 int warmupBestNanos() {
116 BlockSample best = bestBlock(warmup);
117 return best.durationNanos / best.count;
118 }
119
120 int warmupMeanNanos() => _totalTime(warmup) / _totalCount(warmup);
121
122 int warmupWorstNanos() {
123 BlockSample worst = worstBlock(warmup);
124 return worst.durationNanos / worst.count;
125 }
126
127 BlockSample bestBlock(List<BlockSample> samples) {
128 return BlockSample._select(samples,
129 BlockSample selector(BlockSample a, BlockSample b) {
130 return a.durationNanos <= b.durationNanos ? a : b;
131 });
132 }
133
134 BlockSample worstBlock(List<BlockSample> samples) {
135 return BlockSample._select(samples,
136 BlockSample selector(BlockSample a, BlockSample b) {
137 return a.durationNanos >= b.durationNanos ? a : b;
138 });
139 }
140
141 void printReport() {
142 String text = _leftAlign("${id}", 30);
143 String totalCount = _rightAlign(resultsCount().toString(), 10);
144 String totalDurationMs =
145 _rightAlign(_stringifyDoubleAsInt(resultsNanos() / 1E6), 6);
146 String meanDuration =
147 _rightAlign(_stringifyDoubleAsInt(resultsMeanNanos().toDouble()), 8);
148
149 print("${text} total time:${totalDurationMs} ms" +
150 " iterations:${totalCount} mean:${meanDuration} ns");
151 }
152
153 void printReportWithThroughput(int sizeBytes) {
154 String text = _leftAlign("${id}", 30);
155 String totalCount = _rightAlign(resultsCount().toString(), 10);
156 String totalDurationMs =
157 _rightAlign(_stringifyDoubleAsInt(resultsNanos() / 1E6), 6);
158 String meanDuration =
159 _rightAlign(_stringifyDoubleAsInt(resultsMeanNanos()), 8);
160
161 int totalBytes = sizeBytes * resultsCount();
162 String mbPerSec = (((1E9 * sizeBytes * resultsCount()) /
163 (1024 * 1024 * resultsNanos()))).toString();
164 print("${text} total time:${totalDurationMs} ms" +
165 " iterations:${totalCount}" +
166 " mean:${meanDuration} ns; ${mbPerSec} MB/sec");
167 }
168
169 String _leftAlign(String s, int width) {
170 List<int> outCodes = [];
171 outCodes.insertRange(0, width, spaceChar);
172 outCodes.setRange(0, Math.min(width, s.length), s.charCodes());
173 return new String.fromCharCodes(outCodes);
174 }
175
176 String _rightAlign(String s, int width) {
177 List<int> outCodes = [];
178 outCodes.insertRange(0, width, spaceChar);
179 outCodes.setRange(Math.max(0, width - s.length), Math.min(width, s.length),
180 s.charCodes());
181 return new String.fromCharCodes(outCodes);
182 }
183
184 static String _stringifyDoubleAsInt(double val) {
185 if (val.isInfinite() || val.isNaN()) {
186 return "NaN";
187 } else {
188 return val.toInt().toString();
189 }
190 }
191
192 String id;
193 String desc;
194 List<BlockSample> warmup;
195 List<BlockSample> results;
196 }
197
198 class Runner {
199 static bool runTest(String testId) {
200 Options opts = new Options();
201 return opts.arguments.length == 0 ||
202 opts.arguments.some(_(String id) => id == testId);
203 }
204 }
205
206 /**
207 * Run traditional blocking-style tests. Tests may be run a specified number
208 * of times, or they can be run based on performance to estimate a particular
209 * duration.
210 */
211 class BenchmarkRunner extends Runner {
212 static void runCount(String id, String desc, CountTestConfig config,
213 Function test) {
214 if (runTest(id)) {
215 List<BlockSample> warmupSamples = _runTests(test, config._warmup, 1);
216 List<BlockSample> resultSamples = _runTests(test, config._reps, 1);
217 config.reportHandler(
218 new TestReport(id, desc, warmupSamples, resultSamples));
219 }
220 }
221
222 static void runTimed(String id, String desc, TimedTestConfig config,
223 Function test) {
224 if (runTest(id)) {
225 List<BlockSample> warmupSamples = _runTests(test, config._warmup, 1);
226 PerformanceModel model = _calibrate(config._minSampleTimeMs, 16, test);
227 int reps = model.repsFor(1E6 * config._targetTimeMs, config._blocksize);
228 int blocksize = config._blocksize < 0 ? reps : config._blocksize;
229 List<BlockSample> resultSamples = _runTests(test, reps, blocksize);
230 config.reportHandler(
231 new TestReport(id, desc, warmupSamples, resultSamples));
232 }
233 }
234
235 static PerformanceModel _calibrate(int minSampleTimeMs, int maxAttempts,
236 Function test) {
237 PerformanceModel model;
238 int i = 0;
239 do {
240 model = _buildPerformanceModel(minSampleTimeMs, test);
241 i++;
242 } while (i < maxAttempts && !model.isValid());
243 return model;
244 }
245
246 static PerformanceModel _buildPerformanceModel(
247 int minSampleTimeMs, Function test) {
248 int iterations = 1;
249 List<BlockSample> calibrationResults = [];
250 BlockSample calibration = _execBlock(test, iterations);
251 calibrationResults.add(calibration);
252 while (calibration.durationNanos < (1E6 * minSampleTimeMs)) {
253 iterations *= 2;
254 calibration = _execBlock(test, iterations);
255 calibrationResults.add(calibration);
256 }
257 return new PerformanceModel.calculate(calibrationResults);
258 }
259
260 static List<BlockSample> _runTests(Function test, int count, int blocksize) {
261 List<BlockSample> samples = [];
262 for (int rem = count; rem > 0; rem -= blocksize) {
263 BlockSample bs = _execBlock(test, Math.min(blocksize, rem));
264 samples.add(bs);
265 }
266 return samples;
267 }
268
269 static BlockSample _execBlock(Function test, int count) {
270 Stopwatch s = new Stopwatch();
271 s.start();
272 for (int i = 0; i < count; i++) {
273 test();
274 }
275 s.stop();
276 return new BlockSample(count, s.elapsedInUs() * 1000);
277 }
278 }
279
280 /**
281 * Define CPSTest type.
282 */
283 typedef void CPSTest(Function continuation);
284
285 typedef void ReportHandler(TestReport r);
286
287 /**
288 * Run non-blocking-style using Continuation Passing Style callbacks. Tests may
289 * be run a specified number of times, or they can be run based on performance
290 * to estimate a particular duration.
291 */
292 class CPSBenchmarkRunner extends Runner {
293
294 CPSBenchmarkRunner(): _cpsTests = [];
295
296 void addTest(CPSTest test) {
297 _cpsTests.add(test);
298 }
299
300 void runTests([int index = 0, Function continuation = null]) {
301 if (index < _cpsTests.length) {
302 _cpsTests[index](_(){
303 _addToEventQueue(_() => runTests(index + 1, continuation));
304 });
305 } else {
306 if (null != continuation) {
307 _addToEventQueue(_() => continuation());
308 }
309 }
310 }
311
312 List<CPSTest> _cpsTests;
313
314 static void runCount(String id, String desc, CountTestConfig config,
315 CPSTest test, void continuation()) {
316 if (runTest(id)) {
317 _runTests(test, config._warmup, 1, (List<BlockSample> warmupSamples){
318 int blocksize =
319 config._blocksize <= 0 ? config._reps : config._blocksize;
320 _runTests(test, config._reps, blocksize,
321 _(List<BlockSample> resultSamples) {
322 config.reportHandler(
323 new TestReport(id, desc, warmupSamples, resultSamples));
324 continuation();
325 });
326 });
327 } else {
328 continuation();
329 }
330 }
331
332 static void runTimed(String id, String desc, TimedTestConfig config,
333 CPSTest test, void continuation()) {
334 if (runTest(id)) {
335 _runTests(test, config._warmup, 1, (List<BlockSample> warmupSamples){
336 _calibrate(config._minSampleTimeMs, 5, test, (PerformanceModel model){
337 int reps =
338 model.repsFor(1E6 * config._targetTimeMs, config._blocksize);
339 int blocksize =
340 config._blocksize <= 0 ? reps : config._blocksize;
341 _runTests(test, reps, blocksize, (List<BlockSample> results) {
342 config.reportHandler(
343 new TestReport(id, desc, warmupSamples, results));
344 continuation();
345 });
346 });
347 });
348 } else {
349 continuation();
350 }
351 }
352
353 static void nextTest(Function testLoop, int iteration) {
354 _addToEventQueue(() => testLoop(iteration + 1));
355 }
356
357 static void _calibrate(int minSampleTimeMs, int maxAttempts,
358 CPSTest test, void continuation(PerformanceModel model)) {
359 _buildPerformanceModel(minSampleTimeMs, test, (PerformanceModel model){
360 if (maxAttempts > 1 && !model.isValid()) {
361 _calibrate(minSampleTimeMs, maxAttempts - 1, test, continuation);
362 } else {
363 continuation(model);
364 }
365 });
366 }
367
368 static void _buildPerformanceModel(
369 int minSampleTimeMs, CPSTest test, void continuation(PerformanceModel m),
370 [int iterations = 1, List<BlockSample> calibrationResults = null]) {
371 List<BlockSample> _calibrationResults =
372 null == calibrationResults ? [] : calibrationResults;
373 _runTests(test, iterations, 1000, (List<BlockSample> calibration) {
374 _calibrationResults.addAll(calibration);
375 if (BlockSample._totalTime(calibration) < (1E6 * minSampleTimeMs)) {
376 _buildPerformanceModel(minSampleTimeMs, test, continuation,
377 iterations: iterations * 2,
378 calibrationResults: _calibrationResults);
379 } else {
380 PerformanceModel model =
381 new PerformanceModel.calculate(_calibrationResults);
382 continuation(model);
383 }
384 });
385 }
386
387 static void _runTests(CPSTest test, int reps, int blocksize,
388 void continuation(List<BlockSample> samples),
389 [List<BlockSample> samples = null]) {
390 List<BlockSample> localSamples = (null == samples) ? [] : samples;
391 if (reps > 0) {
392 int blockCount = Math.min(blocksize, reps);
393 _execBlock(test, blockCount, (BlockSample sample){
394 localSamples.add(sample);
395 _addToEventQueue(() =>
396 _runTests(test, reps - blockCount, blocksize,
397 continuation, localSamples));
398 });
399 } else {
400 continuation(localSamples);
401 }
402 }
403
404 static void _execBlock(CPSTest test, int count,
405 void continuation(BlockSample sample)) {
406 Stopwatch s = new Stopwatch();
407 s.start();
408 _innerLoop(test, count, () {
409 s.stop();
410 continuation(new BlockSample(count, s.elapsedInUs() * 1000));
411 });
412 }
413
414 static void _innerLoop(CPSTest test, int remainingCount,
415 Function continuation) {
416 if (remainingCount > 1) {
417 test(() => _innerLoop(test, remainingCount - 1, continuation));
418 } else {
419 continuation();
420 }
421 }
422
423 static void _addToEventQueue(Function action) {
424 new Timer(_(Timer t) => action(), 0);
425 }
426 }
427
428 class CountTestConfig {
429 CountTestConfig(int this._warmup, int this._reps,
430 [int blocksize = -1, ReportHandler reportHandler = null]) {
431 this._blocksize = blocksize;
432 this._reportHandler = (null == reportHandler) ?
433 _(TestReport r) => r.printReport() : reportHandler;
434 }
435
436 Function _reportHandler;
437 Function get reportHandler() => _reportHandler;
438 int _warmup;
439 int _reps;
440 int _blocksize;
441 }
442
443 class TimedTestConfig {
444 TimedTestConfig(int this._warmup, int this._targetTimeMs,
445 [int minSampleTimeMs = 100, int blocksize = -1,
446 ReportHandler reportHandler = null]) :
447 this._minSampleTimeMs = minSampleTimeMs,
448 this._blocksize = blocksize {
449 this._reportHandler = (null == reportHandler) ?
450 _(TestReport r) => r.printReport() : reportHandler;
451 }
452
453 Function _reportHandler;
454 Function get reportHandler() => _reportHandler;
455 int _warmup;
456 int _targetTimeMs;
457 int _minSampleTimeMs;
458 int _blocksize;
459 }
OLDNEW
« no previous file with comments | « utils/string_encoding/utf8_impl.dart ('k') | utils/tests/string_encoding/dunit.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698