|  | // 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(); | 
|  | } | 
|  | } |