mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
![Martin Kustermann](/assets/img/avatar_default.png)
We want dart2wasm be comparable to dart2js / dart2aot, the ladder two are much more conservative with inlining compared to current dart2wasm. The -O3 is described in the binaryen sources as agressive for performance and therefore willing to compromise code size. The -Os is more nuanced: It will perform many optimizations that are done in -O3 (and e.g. not in -O2) but it will make inlining less agressive. This reduces flute compile-time by 10% and code size by 10% This benchmark results are mixed (some things get faster, some things slower). Naturally there'll be specialized micro benchmarks that get hit hard by this. Where performance matters we should rather make dart2wasm use better inlining heuristics and annotate code with `@pragma('wasm:prefer-inline')` Change-Id: Idf7e75e4e385629c9cec66359efe0afe50db3e72 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352523 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
206 lines
5.8 KiB
Dart
206 lines
5.8 KiB
Dart
// 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.
|
|
|
|
const int iterations = 100000;
|
|
|
|
void main() {
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.T.dynamic', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getT<dynamic>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.T.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getT<int>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.T.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getT<int?>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.ListOfT.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getListOfT<int>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.ListOfT.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getListOfT<int?>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.NullableT.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getNullableT<int>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.NullableT.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getNullableT<int?>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.ListOfNullableT.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getListOfT<int>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericFunction.ListOfNullableT.nullableInt',
|
|
() {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
getListOfNullableT<int?>();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
final foos = <Foo<Object?>>[
|
|
Foo<int>(),
|
|
Foo<int?>(),
|
|
Foo<dynamic>(),
|
|
];
|
|
final Foo fooInt = foos[int.parse('0')];
|
|
final Foo fooNullableInt = foos[int.parse('1')];
|
|
final Foo fooDynamic = foos[int.parse('2')];
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.T.dynamic', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooDynamic.getT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.T.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooInt.getT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.T.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooNullableInt.getT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.ListOfT.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooInt.getListOfT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.ListOfT.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooNullableInt.getListOfT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.NullableT.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooInt.getNullableT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.NullableT.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooNullableInt.getNullableT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.ListOfNullableT.int', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooInt.getListOfT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
SyncCallBenchmark('TypeLiteral.GenericClass.ListOfNullableT.nullableInt', () {
|
|
for (int i = 0; i < iterations; ++i) {
|
|
fooNullableInt.getListOfNullableT();
|
|
}
|
|
return iterations;
|
|
}).report();
|
|
}
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getT<T>() => T;
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getNullableT<T>() => MakeNullable<T?>;
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getListOfT<T>() => List<T>;
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getListOfNullableT<T>() => List<T?>;
|
|
|
|
class Foo<T> {
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getT() => T;
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getNullableT() => MakeNullable<T?>;
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getListOfT() => List<T>;
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:noInline')
|
|
Type getListOfNullableT() => List<T?>;
|
|
}
|
|
|
|
typedef MakeNullable<X> = X?;
|
|
|
|
// Same as from [Calls] benchmark.
|
|
class SyncCallBenchmark {
|
|
final String name;
|
|
final int Function() performCalls;
|
|
|
|
SyncCallBenchmark(this.name, this.performCalls);
|
|
|
|
// Returns the number of nanoseconds per call.
|
|
double measureFor(Duration duration) {
|
|
final sw = Stopwatch()..start();
|
|
final durationInMicroseconds = duration.inMicroseconds;
|
|
|
|
int numberOfCalls = 0;
|
|
int totalMicroseconds = 0;
|
|
do {
|
|
numberOfCalls += performCalls();
|
|
totalMicroseconds = sw.elapsedMicroseconds;
|
|
} while (totalMicroseconds < durationInMicroseconds);
|
|
|
|
final int totalNanoseconds = sw.elapsed.inMicroseconds * 1000;
|
|
return totalNanoseconds / numberOfCalls;
|
|
}
|
|
|
|
// Runs warmup phase, runs benchmark and reports result.
|
|
void report() {
|
|
// Warmup for 100 ms.
|
|
measureFor(const Duration(milliseconds: 100));
|
|
|
|
// Run benchmark for 2 seconds.
|
|
final double nsPerCall = measureFor(const Duration(seconds: 2));
|
|
|
|
// Report result.
|
|
print('$name(RunTimeRaw): $nsPerCall ns.');
|
|
}
|
|
}
|