| // Copyright (c) 2023, 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. |
| |
| /// This benchmark compares a large switch statement on various kinds of |
| /// values. The switch statement is part of a finite-state-machine (FSM) |
| /// recognizer. |
| /// |
| /// There are four copies of the same code, differing only by importing |
| /// different declarations of the type `State` which makes the same constant |
| /// names available using different types. |
| /// |
| /// The switch dispatch on the following types is benchmarked: |
| /// |
| /// - a compact range of `int` values, |
| /// - an enum, |
| /// - a class that is a bit like an `enum` but not declared as an enum, |
| /// - strings. |
| /// |
| /// The actual state-machine is somewhat aritificial. It recognizes a character |
| /// string of '0' and '1' character 'bits' that encode a valid UTF-8 string. The |
| /// state machine has 48 states and minimal logic in most states, so that as |
| /// much time as possible is executing the switch dispatch. |
| /// |
| /// The data is passed to the recogizer as a Uint8List to minimize the time to |
| /// access the bytes of the ASCII '0' / '1' character input sequence. |
| |
| import 'dart:typed_data'; |
| |
| import 'package:benchmark_harness/benchmark_harness.dart'; |
| import 'package:expect/expect.dart'; |
| |
| import 'match_class.dart' as match_class; |
| import 'match_enum.dart' as match_enum; |
| import 'match_int.dart' as match_int; |
| import 'match_string.dart' as match_string; |
| |
| class Benchmark extends BenchmarkBase { |
| final bool Function(Uint8List) match; |
| |
| Benchmark(String kind, this.match) : super('SwitchFSM.$kind'); |
| |
| void validation() { |
| void check(String s, bool expected) { |
| Expect.equals(expected, match(convert(s)), '"$s"'); |
| } |
| |
| check('', true); |
| check('0', false); |
| check('00', false); |
| check('000', false); |
| check('0000', false); |
| check('00000', false); |
| check('000000', false); |
| check('0000000', false); |
| check('00000000', true); |
| check('01010101', true); |
| check('10000000', false); |
| check('001010101', false); |
| check('11000000' '00000000', false); |
| check('11000000' '10111111', true); |
| check('11000000' '11111111', false); |
| check('11100000' '00000000' '00000000', false); |
| check('11100000' '10000000' '00000000', false); |
| check('11100000' '10111111' '10111111', true); |
| check('11110111' '10111111' '10111111' '01111111', false); |
| check('11110111' '10111111' '10111111' '10111111', true); |
| Expect.equals(testInputLength, testInput.length); |
| } |
| |
| static const testInputLength = 1000; |
| static final Uint8List testInput = convert(makeTestInput(testInputLength)); |
| |
| static String makeTestInput(int length) { |
| // The test input uses most states of the FSM. It is repeated and padded to |
| // make the length 1000. |
| final testPattern = '' |
| '11110111101111111011111110111111' |
| '111011111011111110111111' |
| '1101111110111111'; |
| final paddingPattern = '00000000'; |
| final repeats = testPattern * (length ~/ testPattern.length); |
| final padding = |
| paddingPattern * ((length - repeats.length) ~/ paddingPattern.length); |
| return repeats + padding; |
| } |
| |
| static Uint8List convert(String s) => Uint8List.fromList(s.codeUnits); |
| |
| @override |
| void run() { |
| Expect.equals(true, match(testInput)); |
| } |
| } |
| |
| enum SomeEnum { element } |
| |
| void main() { |
| // TODO(http://dartbug.com/51657): dart2js will remove `_Enum.index` in simple |
| // programs that don't appear to use the field. This defeats the enum-switch |
| // optimization that works more reliably in larger programs. Remove this code |
| // that marks `_Enum.index` as used when #51657 is fixed. |
| Expect.equals(0, SomeEnum.element.index); |
| |
| final benchmarks = [ |
| Benchmark('enum', match_enum.match), |
| Benchmark('int', match_int.match), |
| Benchmark('class', match_class.match), |
| Benchmark('string', match_string.match), |
| ]; |
| |
| for (final benchmark in benchmarks) { |
| benchmark.validation(); |
| } |
| |
| for (final benchmark in benchmarks) { |
| benchmark.warmup(); |
| } |
| |
| for (final benchmark in benchmarks) { |
| benchmark.report(); |
| } |
| } |