// 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 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; late List chunks; int totalInputSize = 0; int totalOutputSize = 0; 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); if (data.length != 10000) { throw 'Expected input data of exactly 10000 bytes.'; } if (size < data.length) { // Split into chunks. chunks = []; 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 = [expanded]; totalInputSize = size; totalOutputSize = text.length * size ~/ data.length; } else { // Use data as is. chunks = [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 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 args) { const texts = {'en': en, 'da': da, 'sk': sk, 'ru': ru, 'ne': ne, 'zh': zh}; final bool testMalformed = 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(); } }