From bb6b4d1dc93668ab4f46d8170813d88bbf39a97d Mon Sep 17 00:00:00 2001 From: Robert Nystrom Date: Thu, 21 Apr 2022 11:03:19 -0700 Subject: [PATCH] [patterns] Add benchmarks for exhaustiveness checking. --- .../benchmark/large_fields_call_counts.dart | 115 ++++++++++++++++ .../benchmark/large_fields_timed.dart | 127 ++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_call_counts.dart create mode 100644 working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_timed.dart diff --git a/working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_call_counts.dart b/working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_call_counts.dart new file mode 100644 index 0000000000..608aa92936 --- /dev/null +++ b/working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_call_counts.dart @@ -0,0 +1,115 @@ +// Copyright (c) 2022, 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:exhaustiveness_prototype/exhaustive.dart'; +import 'package:exhaustiveness_prototype/profile.dart' as profile; +import 'package:exhaustiveness_prototype/space.dart'; +import 'package:exhaustiveness_prototype/static_type.dart'; + +import '../test/utils.dart'; + +void main() { + profile.enabled = true; + + // (A) + // /|\ + // B C D + var a = StaticType('A', isSealed: true); + var b = StaticType('B', inherits: [a]); + var c = StaticType('C', inherits: [a]); + var d = StaticType('D', inherits: [a]); + var t = StaticType('T', fields: {'w': a, 'x': a, 'y': a, 'z': a}); + + expectExhaustiveOnlyAll(t, [ + {'w': b, 'x': b, 'y': b, 'z': b}, + {'w': b, 'x': b, 'y': b, 'z': c}, + {'w': b, 'x': b, 'y': b, 'z': d}, + {'w': b, 'x': b, 'y': c, 'z': b}, + {'w': b, 'x': b, 'y': c, 'z': c}, + {'w': b, 'x': b, 'y': c, 'z': d}, + {'w': b, 'x': b, 'y': d, 'z': b}, + {'w': b, 'x': b, 'y': d, 'z': c}, + {'w': b, 'x': b, 'y': d, 'z': d}, + {'w': b, 'x': c, 'y': b, 'z': b}, + {'w': b, 'x': c, 'y': b, 'z': c}, + {'w': b, 'x': c, 'y': b, 'z': d}, + {'w': b, 'x': c, 'y': c, 'z': b}, + {'w': b, 'x': c, 'y': c, 'z': c}, + {'w': b, 'x': c, 'y': c, 'z': d}, + {'w': b, 'x': c, 'y': d, 'z': b}, + {'w': b, 'x': c, 'y': d, 'z': c}, + {'w': b, 'x': c, 'y': d, 'z': d}, + {'w': b, 'x': d, 'y': b, 'z': b}, + {'w': b, 'x': d, 'y': b, 'z': c}, + {'w': b, 'x': d, 'y': b, 'z': d}, + {'w': b, 'x': d, 'y': c, 'z': b}, + {'w': b, 'x': d, 'y': c, 'z': c}, + {'w': b, 'x': d, 'y': c, 'z': d}, + {'w': b, 'x': d, 'y': d, 'z': b}, + {'w': b, 'x': d, 'y': d, 'z': c}, + {'w': b, 'x': d, 'y': d, 'z': d}, + {'w': c, 'x': b, 'y': b, 'z': b}, + {'w': c, 'x': b, 'y': b, 'z': c}, + {'w': c, 'x': b, 'y': b, 'z': d}, + {'w': c, 'x': b, 'y': c, 'z': b}, + {'w': c, 'x': b, 'y': c, 'z': c}, + {'w': c, 'x': b, 'y': c, 'z': d}, + {'w': c, 'x': b, 'y': d, 'z': b}, + {'w': c, 'x': b, 'y': d, 'z': c}, + {'w': c, 'x': b, 'y': d, 'z': d}, + {'w': c, 'x': c, 'y': b, 'z': b}, + {'w': c, 'x': c, 'y': b, 'z': c}, + {'w': c, 'x': c, 'y': b, 'z': d}, + {'w': c, 'x': c, 'y': c, 'z': b}, + {'w': c, 'x': c, 'y': c, 'z': c}, + {'w': c, 'x': c, 'y': c, 'z': d}, + {'w': c, 'x': c, 'y': d, 'z': b}, + {'w': c, 'x': c, 'y': d, 'z': c}, + {'w': c, 'x': c, 'y': d, 'z': d}, + {'w': c, 'x': d, 'y': b, 'z': b}, + {'w': c, 'x': d, 'y': b, 'z': c}, + {'w': c, 'x': d, 'y': b, 'z': d}, + {'w': c, 'x': d, 'y': c, 'z': b}, + {'w': c, 'x': d, 'y': c, 'z': c}, + {'w': c, 'x': d, 'y': c, 'z': d}, + {'w': c, 'x': d, 'y': d, 'z': b}, + {'w': c, 'x': d, 'y': d, 'z': c}, + {'w': c, 'x': d, 'y': d, 'z': d}, + {'w': d, 'x': b, 'y': b, 'z': b}, + {'w': d, 'x': b, 'y': b, 'z': c}, + {'w': d, 'x': b, 'y': b, 'z': d}, + {'w': d, 'x': b, 'y': c, 'z': b}, + {'w': d, 'x': b, 'y': c, 'z': c}, + {'w': d, 'x': b, 'y': c, 'z': d}, + {'w': d, 'x': b, 'y': d, 'z': b}, + {'w': d, 'x': b, 'y': d, 'z': c}, + {'w': d, 'x': b, 'y': d, 'z': d}, + {'w': d, 'x': c, 'y': b, 'z': b}, + {'w': d, 'x': c, 'y': b, 'z': c}, + {'w': d, 'x': c, 'y': b, 'z': d}, + {'w': d, 'x': c, 'y': c, 'z': b}, + {'w': d, 'x': c, 'y': c, 'z': c}, + {'w': d, 'x': c, 'y': c, 'z': d}, + {'w': d, 'x': c, 'y': d, 'z': b}, + {'w': d, 'x': c, 'y': d, 'z': c}, + {'w': d, 'x': c, 'y': d, 'z': d}, + {'w': d, 'x': d, 'y': b, 'z': b}, + {'w': d, 'x': d, 'y': b, 'z': c}, + {'w': d, 'x': d, 'y': b, 'z': d}, + {'w': d, 'x': d, 'y': c, 'z': b}, + {'w': d, 'x': d, 'y': c, 'z': c}, + {'w': d, 'x': d, 'y': c, 'z': d}, + {'w': d, 'x': d, 'y': d, 'z': b}, + {'w': d, 'x': d, 'y': d, 'z': c}, + {'w': d, 'x': d, 'y': d, 'z': d}, + ]); +} + +/// Test that [cases] are exhaustive over [type] if and only if all cases are +/// included and that all subsets of the cases are not exhaustive. +void expectExhaustiveOnlyAll(StaticType type, List cases) { + var spaces = parseSpaces(cases); + profile.reset(); + print(isExhaustive(Space(type), spaces)); + profile.log(); +} diff --git a/working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_timed.dart b/working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_timed.dart new file mode 100644 index 0000000000..7f3110e37d --- /dev/null +++ b/working/0546-patterns/exhaustiveness_prototype/benchmark/large_fields_timed.dart @@ -0,0 +1,127 @@ +// Copyright (c) 2022, 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 'dart:math'; + +import 'package:exhaustiveness_prototype/exhaustive.dart'; +import 'package:exhaustiveness_prototype/space.dart'; +import 'package:exhaustiveness_prototype/static_type.dart'; + +import '../test/utils.dart'; + +void main() { + // (A) + // /|\ + // B C D + var a = StaticType('A', isSealed: true); + var b = StaticType('B', inherits: [a]); + var c = StaticType('C', inherits: [a]); + var d = StaticType('D', inherits: [a]); + var t = StaticType('T', fields: {'w': a, 'x': a, 'y': a, 'z': a}); + + expectExhaustiveOnlyAll(t, [ + {'w': b, 'x': b, 'y': b, 'z': b}, + {'w': b, 'x': b, 'y': b, 'z': c}, + {'w': b, 'x': b, 'y': b, 'z': d}, + {'w': b, 'x': b, 'y': c, 'z': b}, + {'w': b, 'x': b, 'y': c, 'z': c}, + {'w': b, 'x': b, 'y': c, 'z': d}, + {'w': b, 'x': b, 'y': d, 'z': b}, + {'w': b, 'x': b, 'y': d, 'z': c}, + {'w': b, 'x': b, 'y': d, 'z': d}, + {'w': b, 'x': c, 'y': b, 'z': b}, + {'w': b, 'x': c, 'y': b, 'z': c}, + {'w': b, 'x': c, 'y': b, 'z': d}, + {'w': b, 'x': c, 'y': c, 'z': b}, + {'w': b, 'x': c, 'y': c, 'z': c}, + {'w': b, 'x': c, 'y': c, 'z': d}, + {'w': b, 'x': c, 'y': d, 'z': b}, + {'w': b, 'x': c, 'y': d, 'z': c}, + {'w': b, 'x': c, 'y': d, 'z': d}, + {'w': b, 'x': d, 'y': b, 'z': b}, + {'w': b, 'x': d, 'y': b, 'z': c}, + {'w': b, 'x': d, 'y': b, 'z': d}, + {'w': b, 'x': d, 'y': c, 'z': b}, + {'w': b, 'x': d, 'y': c, 'z': c}, + {'w': b, 'x': d, 'y': c, 'z': d}, + {'w': b, 'x': d, 'y': d, 'z': b}, + {'w': b, 'x': d, 'y': d, 'z': c}, + {'w': b, 'x': d, 'y': d, 'z': d}, + {'w': c, 'x': b, 'y': b, 'z': b}, + {'w': c, 'x': b, 'y': b, 'z': c}, + {'w': c, 'x': b, 'y': b, 'z': d}, + {'w': c, 'x': b, 'y': c, 'z': b}, + {'w': c, 'x': b, 'y': c, 'z': c}, + {'w': c, 'x': b, 'y': c, 'z': d}, + {'w': c, 'x': b, 'y': d, 'z': b}, + {'w': c, 'x': b, 'y': d, 'z': c}, + {'w': c, 'x': b, 'y': d, 'z': d}, + {'w': c, 'x': c, 'y': b, 'z': b}, + {'w': c, 'x': c, 'y': b, 'z': c}, + {'w': c, 'x': c, 'y': b, 'z': d}, + {'w': c, 'x': c, 'y': c, 'z': b}, + {'w': c, 'x': c, 'y': c, 'z': c}, + {'w': c, 'x': c, 'y': c, 'z': d}, + {'w': c, 'x': c, 'y': d, 'z': b}, + {'w': c, 'x': c, 'y': d, 'z': c}, + {'w': c, 'x': c, 'y': d, 'z': d}, + {'w': c, 'x': d, 'y': b, 'z': b}, + {'w': c, 'x': d, 'y': b, 'z': c}, + {'w': c, 'x': d, 'y': b, 'z': d}, + {'w': c, 'x': d, 'y': c, 'z': b}, + {'w': c, 'x': d, 'y': c, 'z': c}, + {'w': c, 'x': d, 'y': c, 'z': d}, + {'w': c, 'x': d, 'y': d, 'z': b}, + {'w': c, 'x': d, 'y': d, 'z': c}, + {'w': c, 'x': d, 'y': d, 'z': d}, + {'w': d, 'x': b, 'y': b, 'z': b}, + {'w': d, 'x': b, 'y': b, 'z': c}, + {'w': d, 'x': b, 'y': b, 'z': d}, + {'w': d, 'x': b, 'y': c, 'z': b}, + {'w': d, 'x': b, 'y': c, 'z': c}, + {'w': d, 'x': b, 'y': c, 'z': d}, + {'w': d, 'x': b, 'y': d, 'z': b}, + {'w': d, 'x': b, 'y': d, 'z': c}, + {'w': d, 'x': b, 'y': d, 'z': d}, + {'w': d, 'x': c, 'y': b, 'z': b}, + {'w': d, 'x': c, 'y': b, 'z': c}, + {'w': d, 'x': c, 'y': b, 'z': d}, + {'w': d, 'x': c, 'y': c, 'z': b}, + {'w': d, 'x': c, 'y': c, 'z': c}, + {'w': d, 'x': c, 'y': c, 'z': d}, + {'w': d, 'x': c, 'y': d, 'z': b}, + {'w': d, 'x': c, 'y': d, 'z': c}, + {'w': d, 'x': c, 'y': d, 'z': d}, + {'w': d, 'x': d, 'y': b, 'z': b}, + {'w': d, 'x': d, 'y': b, 'z': c}, + {'w': d, 'x': d, 'y': b, 'z': d}, + {'w': d, 'x': d, 'y': c, 'z': b}, + {'w': d, 'x': d, 'y': c, 'z': c}, + {'w': d, 'x': d, 'y': c, 'z': d}, + {'w': d, 'x': d, 'y': d, 'z': b}, + {'w': d, 'x': d, 'y': d, 'z': c}, + {'w': d, 'x': d, 'y': d, 'z': d}, + ]); +} + +/// Test that [cases] are exhaustive over [type] if and only if all cases are +/// included and that all subsets of the cases are not exhaustive. +void expectExhaustiveOnlyAll(StaticType type, List cases) { + const trials = 100; + + var best = 9999999; + for (var j = 0; j < 100000; j++) { + var watch = Stopwatch()..start(); + for (var i = 0; i < trials; i++) { + var spaces = parseSpaces(cases); + var actual = isExhaustive(Space(type), spaces); + if (!actual) { + throw 'Expected exhaustive'; + } + } + + var elapsed = watch.elapsedMilliseconds; + best = min(elapsed, best); + print('${elapsed / trials}ms (best ${best / trials}ms)'); + } +}