| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:characters/src/grapheme_clusters/breaks.dart'; | |
| import 'package:characters/src/grapheme_clusters/constants.dart'; | |
| import '../test/src/text_samples.dart'; | |
| import '../test/src/unicode_grapheme_tests.dart'; | |
| import '../test/src/various_tests.dart'; | |
| // Low-level benchmark of the grapheme cluster step functions. | |
| // Use ../benchmark/benchmark.dart for the more high-level `Characters` | |
| // methods. | |
| void main(List<String> args) { | |
| var count = 5; | |
| if (args.isNotEmpty) { | |
| count = int.parse(args[0]); | |
| } | |
| var text = genesis + | |
| hangul + | |
| genesis + | |
| diacretics + | |
| recJoin(splitTests) + | |
| recJoin(emojis) + | |
| recJoin(zalgo); | |
| var codeUnits = text.length; | |
| var codePoints = text.runes.length; | |
| // Warmup. | |
| var gcSumForward = benchForward(text, -1, codePoints, codeUnits, 150); | |
| var gcSumBackwards = benchBackward(text, -1, codePoints, codeUnits, 150); | |
| if (gcSumForward != gcSumBackwards) { | |
| print( | |
| 'ERROR: Did not count the same number of grapheme clusters: ' | |
| '$gcSumForward forward vs. $gcSumBackwards backward.', | |
| ); | |
| return; | |
| } | |
| for (var i = 0; i < count; i++) { | |
| gcSumForward = benchForward(text, i, codePoints, codeUnits, 1500); | |
| gcSumBackwards = benchBackward(text, i, codePoints, codeUnits, 1500); | |
| } | |
| print('gc: Grapheme Clusters, cp: Code Points, cu: Code Units.'); | |
| if (gcSumForward != gcSumBackwards) { | |
| print( | |
| 'ERROR: Did not count the same number of grapheme clusters: ' | |
| '$gcSumForward forward vs. $gcSumBackwards backward.', | |
| ); | |
| } else { | |
| var surrogates = codeUnits - codePoints; | |
| print( | |
| 'Total: $gcSumForward gc, $codePoints cp, $codeUnits cu, ' | |
| '$surrogates surrogates ' | |
| '(${(surrogates / codePoints * 100).toStringAsFixed(3)}%)', | |
| ); | |
| print('Avg ${(codePoints / gcSumForward).toStringAsFixed(3)} cp/gc'); | |
| print('Avg ${(codeUnits / gcSumForward).toStringAsFixed(3)} cu/gc'); | |
| } | |
| } | |
| String recJoin(Iterable<List<String>> texts) => | |
| texts.map((x) => x.join('')).join('\n'); | |
| int benchForward(String text, int round, int cp, int cu, int limit) { | |
| var n = 0; | |
| var step = 10; | |
| var gc = 0; | |
| var e = 0; | |
| var sw = Stopwatch()..start(); | |
| do { | |
| for (var i = 0; i < step; i++) { | |
| var breaks = Breaks(text, 0, text.length, stateSoTNoBreak); | |
| while (breaks.nextBreak() >= 0) { | |
| gc++; | |
| } | |
| } | |
| e = sw.elapsedMilliseconds; | |
| n += step; | |
| step += step; | |
| } while (e < limit); | |
| if (limit > 500) { | |
| print( | |
| 'Forward #$round: ${(gc / e).round()} gc/ms, ' | |
| '${(n * cp / e).round()} cp/ms, ' | |
| '${(n * cu / e).round()} cu/ms, ' | |
| '$n rounds in $e ms', | |
| ); | |
| } | |
| return gc ~/ n; | |
| } | |
| int benchBackward(String text, int round, int cp, int cu, int limit) { | |
| var n = 0; | |
| var step = 10; | |
| var gc = 0; | |
| var e = 0; | |
| var sw = Stopwatch()..start(); | |
| do { | |
| for (var i = 0; i < step; i++) { | |
| var breaks = BackBreaks(text, text.length, 0, stateEoTNoBreak); | |
| while (breaks.nextBreak() >= 0) { | |
| gc++; | |
| } | |
| } | |
| e = sw.elapsedMilliseconds; | |
| n += step; | |
| step += step; | |
| } while (e < limit); | |
| if (limit > 500) { | |
| print( | |
| 'Backward #$round: ${(gc / e).round()} gc/ms, ' | |
| '${(n * cp / e).round()} cp/ms, ' | |
| '${(n * cu / e).round()} cu/ms, ' | |
| '$n rounds in $e ms', | |
| ); | |
| } | |
| return gc ~/ n; | |
| } |