| // Copyright (c) 2020, 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. |
| // |
| // Benchmark for UTF-8 decoding |
| |
| // @dart=2.9 |
| |
| import 'dart:convert'; |
| import 'dart:typed_data'; |
| |
| import 'package:benchmark_harness/benchmark_harness.dart'; |
| |
| import 'datext_latin1_10k.dart'; |
| import 'entext_ascii_10k.dart'; |
| import 'netext_3_10k.dart'; |
| import 'rutext_2_10k.dart'; |
| import 'sktext_10k.dart'; |
| import 'zhtext_10k.dart'; |
| |
| class Utf8Decode extends BenchmarkBase { |
| final String language; |
| final String text; |
| final int size; |
| final bool allowMalformed; |
| List<Uint8List> chunks; |
| int totalInputSize; |
| int totalOutputSize; |
| |
| static String _makeName(String language, int size, bool allowMalformed) { |
| String name = 'Utf8Decode.$language.'; |
| name += size >= 1000000 |
| ? '${size ~/ 1000000}M' |
| : size >= 1000 ? '${size ~/ 1000}k' : '$size'; |
| if (allowMalformed) name += '.malformed'; |
| return name; |
| } |
| |
| Utf8Decode(this.language, this.text, this.size, this.allowMalformed) |
| : super(_makeName(language, size, allowMalformed)); |
| |
| @override |
| void setup() { |
| final Uint8List data = utf8.encode(text) as Uint8List; |
| if (data.length != 10000) { |
| throw 'Expected input data of exactly 10000 bytes.'; |
| } |
| if (size < data.length) { |
| // Split into chunks. |
| chunks = <Uint8List>[]; |
| int startPos = 0; |
| for (int pos = size; pos < data.length; pos += size) { |
| int endPos = pos; |
| while ((data[endPos] & 0xc0) == 0x80) { |
| endPos--; |
| } |
| chunks.add(Uint8List.fromList(data.sublist(startPos, endPos))); |
| startPos = endPos; |
| } |
| chunks.add(Uint8List.fromList(data.sublist(startPos, data.length))); |
| totalInputSize = data.length; |
| totalOutputSize = text.length; |
| } else if (size > data.length) { |
| // Repeat data to the desired length. |
| final Uint8List expanded = Uint8List(size); |
| for (int i = 0; i < size; i++) { |
| expanded[i] = data[i % data.length]; |
| } |
| chunks = <Uint8List>[expanded]; |
| totalInputSize = size; |
| totalOutputSize = text.length * size ~/ data.length; |
| } else { |
| // Use data as is. |
| chunks = <Uint8List>[data]; |
| totalInputSize = data.length; |
| totalOutputSize = text.length; |
| } |
| } |
| |
| @override |
| void run() { |
| int lengthSum = 0; |
| for (int i = 0; i < chunks.length; i++) { |
| final String s = utf8.decode(chunks[i], allowMalformed: allowMalformed); |
| lengthSum += s.length; |
| } |
| if (lengthSum != totalOutputSize) { |
| throw 'Output length doesn\'t match expected.'; |
| } |
| } |
| |
| @override |
| void exercise() { |
| // Only a single run per measurement. |
| run(); |
| } |
| |
| @override |
| void warmup() { |
| BenchmarkBase.measureFor(run, 1000); |
| } |
| |
| @override |
| double measure() { |
| // Report time per input byte. |
| return super.measure() / totalInputSize; |
| } |
| |
| @override |
| void report() { |
| // Report time in nanoseconds. |
| final double score = measure() * 1000.0; |
| print('$name(RunTime): $score ns.'); |
| } |
| } |
| |
| void main(List<String> args) { |
| const texts = { |
| 'en': en, |
| 'da': da, |
| 'sk': sk, |
| 'ru': ru, |
| 'ne': ne, |
| 'zh': zh, |
| }; |
| final bool testMalformed = |
| args != null && args.isNotEmpty && args.first == 'malformed'; |
| final benchmarks = [ |
| // Only benchmark with allowMalformed: false unless specified otherwise. |
| for (bool allowMalformed in [false, if (testMalformed) true]) |
| for (int size in [10, 10000, 10000000]) |
| for (String language in texts.keys) |
| () => Utf8Decode(language, texts[language], size, allowMalformed) |
| ]; |
| |
| for (var bm in benchmarks) { |
| bm().report(); |
| } |
| } |