mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 02:42:05 +00:00
158 lines
4 KiB
Dart
158 lines
4 KiB
Dart
|
// 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.
|
||
|
//
|
||
|
// Generates both the dart and dart2 version of this benchmark.
|
||
|
|
||
|
import 'dart:io';
|
||
|
import 'dart:math';
|
||
|
|
||
|
import 'package:path/path.dart' as path;
|
||
|
|
||
|
const String benchmarkName = 'SubtypeTestCache';
|
||
|
|
||
|
const List<int> assertionCounts = [
|
||
|
1,
|
||
|
5,
|
||
|
10,
|
||
|
25,
|
||
|
50,
|
||
|
75,
|
||
|
100,
|
||
|
250,
|
||
|
500,
|
||
|
750,
|
||
|
1000
|
||
|
];
|
||
|
|
||
|
void generateBenchmarkClassesAndUtilities(IOSink output) {
|
||
|
final maxCount = assertionCounts.reduce(max);
|
||
|
output.writeln('''
|
||
|
// 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 suite measures the overhead of looking up elements of
|
||
|
// SubtypeTestCaches, which are used when a type testing stub cannot determine
|
||
|
// whether a given type is assignable.
|
||
|
|
||
|
import 'package:benchmark_harness/benchmark_harness.dart';
|
||
|
|
||
|
void main() {''');
|
||
|
|
||
|
// We must run the benchmarks from smallest count to largest, since a single
|
||
|
// STC is shared across all the benchmarks (due to the single call site in
|
||
|
// [check]). This ensures that benchmarks that are testing counts small
|
||
|
// enough for a linear STC use a linear STC.
|
||
|
final sortedCounts = assertionCounts.toList(growable: false);
|
||
|
sortedCounts.sort();
|
||
|
for (final count in sortedCounts) {
|
||
|
output.write('''
|
||
|
const STC$count().report();
|
||
|
''');
|
||
|
}
|
||
|
// We need to run the STCSame<max> benchmark only after running all the
|
||
|
// STC<count> benchmarks, so that we ensure the shared STC is properly primed.
|
||
|
output.write('''
|
||
|
const STCSame$maxCount().report();
|
||
|
''');
|
||
|
output.writeln('''
|
||
|
}
|
||
|
|
||
|
class STCBenchmarkBase extends BenchmarkBase {
|
||
|
final int count;
|
||
|
const STCBenchmarkBase(String name, this.count) : super(name);
|
||
|
|
||
|
// Normalize the cost across the benchmarks by number of type tests.
|
||
|
@override
|
||
|
void report() => emitter.emit(name, measure() / count);
|
||
|
}
|
||
|
''');
|
||
|
|
||
|
for (final count in assertionCounts) {
|
||
|
output.write('''
|
||
|
class STC$count extends STCBenchmarkBase {
|
||
|
const STC$count() : super('$benchmarkName.STC$count', $count);
|
||
|
|
||
|
@override
|
||
|
void run() {
|
||
|
''');
|
||
|
|
||
|
for (int i = 0; i < count; i++) {
|
||
|
output.write('''
|
||
|
check<int>(instances[$i]);
|
||
|
''');
|
||
|
}
|
||
|
|
||
|
output.writeln('''
|
||
|
}
|
||
|
}
|
||
|
''');
|
||
|
}
|
||
|
|
||
|
output.write('''
|
||
|
class STCSame$maxCount extends STCBenchmarkBase {
|
||
|
const STCSame$maxCount() : super('$benchmarkName.STCSame$maxCount', $maxCount);
|
||
|
|
||
|
@override
|
||
|
void run() {
|
||
|
// Do $maxCount AssertAssignable checks for the last type checked in the
|
||
|
// STC$maxCount benchmark.
|
||
|
''');
|
||
|
|
||
|
for (int i = 0; i < maxCount; i++) {
|
||
|
output.write('''
|
||
|
check<int>(instances[${maxCount - 1}]);
|
||
|
''');
|
||
|
}
|
||
|
|
||
|
output.writeln('''
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@pragma('vm:never-inline')
|
||
|
@pragma('dart2js:never-inline')
|
||
|
void check<S>(dynamic s) => s as C<S> Function();
|
||
|
|
||
|
class C<T> {}
|
||
|
''');
|
||
|
|
||
|
for (int i = 0; i < maxCount; i++) {
|
||
|
output.write('''
|
||
|
class C$i<T> extends C<T> {}
|
||
|
|
||
|
C$i<S> closure$i<S>() => C$i<S>();
|
||
|
|
||
|
''');
|
||
|
}
|
||
|
|
||
|
// We create constant tearoffs of the closures above to use for our values
|
||
|
// in the `as` checks. We could make constant instances of the classes, but
|
||
|
// the specialized TTS for the `C` class hierarchy means that we'll never
|
||
|
// actually hit the SubtypeTestCache!
|
||
|
//
|
||
|
// Using closures both avoids the likelihood of eventually optimizing the TTS
|
||
|
// for this check and making this benchmark outdated and also ensures the VM
|
||
|
// performs the most intensive check for each STC entry, i.e., the
|
||
|
// Subtype7TestCache stub is called.
|
||
|
output.write('''
|
||
|
const instances = <dynamic>[
|
||
|
''');
|
||
|
for (int i = 0; i < maxCount; i++) {
|
||
|
output.write('''
|
||
|
closure$i<int>,
|
||
|
''');
|
||
|
}
|
||
|
output.write('''
|
||
|
];
|
||
|
''');
|
||
|
}
|
||
|
|
||
|
void main() {
|
||
|
final dartFilePath = path.join(
|
||
|
path.dirname(Platform.script.path), 'dart', '$benchmarkName.dart');
|
||
|
final dartSink = File(dartFilePath).openWrite();
|
||
|
generateBenchmarkClassesAndUtilities(dartSink);
|
||
|
dartSink..flush();
|
||
|
}
|