dart-sdk/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart
Martin Kustermann af1b5b8044 [dart2wasm] Switch binaryen optimization levels from -O3 to -Os
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>
2024-02-14 13:45:13 +00:00

319 lines
8.4 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.
// Micro-benchmark for testing async/await performance in presence of
// different number of live values across await.
import 'dart:async';
import 'package:benchmark_harness/benchmark_harness.dart';
class MockClass {
static final String str = "${int.parse('42')}";
static final List<int> list =
List<int>.filled(int.parse('3'), int.parse('42'));
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
String get1() => str;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
List<int> get2() => list;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use1(String a0) => a0.length;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use2(String a0, List<int> a1) => a0.length + a1.length;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use4(String a0, List<int> a1, String a2, List<int> a3) =>
a0.length + a1.length + a2.length + a3.length;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use8(String a0, List<int> a1, String a2, List<int> a3, String a4,
List<int> a5, String a6, List<int> a7) =>
a0.length +
a1.length +
a2.length +
a3.length +
a4.length +
a5.length +
a6.length +
a7.length;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
Future<void> asyncMethod() async {}
}
class MockClass2 {
static int val1 = int.parse('42');
static int val2 = int.parse('43');
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
int get1() => val1;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
int get2() => val2;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use1(int a0) => a0;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use2(int a0, int a1) => a0 + a1;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
void use4(int a0, int a1, int a2, int a3) => a0 + a1 + a2 + a3;
@pragma('vm:never-inline')
@pragma('wasm:never-inline')
@pragma('dart2js:noInline')
Future<void> asyncMethod() async {}
}
class LiveVarsBench extends AsyncBenchmarkBase {
LiveVarsBench(String name) : super(name);
@override
Future<void> exercise() async {
// These micro-benchmarks are too small, so
// make a larger number of iterations per measurement.
for (var i = 0; i < 10000; i++) {
await run();
}
}
}
class LiveObj1 extends LiveVarsBench {
LiveObj1() : super('AsyncLiveVars.LiveObj1');
final field1 = MockClass();
@override
Future<void> run() async {
final obj1 = field1.get1();
await field1.asyncMethod();
field1.use1(obj1);
await field1.asyncMethod();
field1.use1(obj1);
await field1.asyncMethod();
field1.use1(obj1);
}
}
class LiveObj2 extends LiveVarsBench {
LiveObj2() : super('AsyncLiveVars.LiveObj2');
final field1 = MockClass();
@override
Future<void> run() async {
final obj1 = field1.get1();
final obj2 = field1.get2();
await field1.asyncMethod();
field1.use1(obj1);
await field1.asyncMethod();
field1.use1(obj1);
await field1.asyncMethod();
field1.use2(obj1, obj2);
}
}
class LiveObj4 extends LiveVarsBench {
LiveObj4() : super('AsyncLiveVars.LiveObj4');
final field1 = MockClass();
final field2 = MockClass();
@override
Future<void> run() async {
final obj1 = field1.get1();
final obj2 = field1.get2();
final obj3 = field2.get1();
final obj4 = field2.get2();
await field1.asyncMethod();
field1.use1(obj1);
await field1.asyncMethod();
field2.use1(obj3);
await field2.asyncMethod();
field1.use4(obj1, obj2, obj3, obj4);
}
}
class LiveObj8 extends LiveVarsBench {
LiveObj8() : super('AsyncLiveVars.LiveObj8');
final field1 = MockClass();
final field2 = MockClass();
final field3 = MockClass();
final field4 = MockClass();
@override
Future<void> run() async {
final obj1 = field1.get1();
final obj2 = field1.get2();
final obj3 = field2.get1();
final obj4 = field2.get2();
final obj5 = field3.get1();
final obj6 = field3.get2();
final obj7 = field4.get1();
final obj8 = field4.get2();
await field1.asyncMethod();
field1.use1(obj1);
await field2.asyncMethod();
field3.use2(obj5, obj6);
await field4.asyncMethod();
field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
}
}
class LiveObj16 extends LiveVarsBench {
LiveObj16() : super('AsyncLiveVars.LiveObj16');
final field1 = MockClass();
final field2 = MockClass();
final field3 = MockClass();
final field4 = MockClass();
final field5 = MockClass();
final field6 = MockClass();
final field7 = MockClass();
final field8 = MockClass();
@override
Future<void> run() async {
final obj1 = field1.get1();
final obj2 = field1.get2();
final obj3 = field2.get1();
final obj4 = field2.get2();
final obj5 = field3.get1();
final obj6 = field3.get2();
final obj7 = field4.get1();
final obj8 = field4.get2();
final obj9 = field5.get1();
final obj10 = field5.get2();
final obj11 = field6.get1();
final obj12 = field6.get2();
final obj13 = field7.get1();
final obj14 = field7.get2();
final obj15 = field8.get1();
final obj16 = field8.get2();
await field1.asyncMethod();
field1.use1(obj1);
await field2.asyncMethod();
field5.use2(obj11, obj12);
await field4.asyncMethod();
field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
field3.use8(obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16);
}
}
class LiveInt1 extends LiveVarsBench {
LiveInt1() : super('AsyncLiveVars.LiveInt1');
final field1 = MockClass2();
@override
Future<void> run() async {
final int1 = field1.get1();
await field1.asyncMethod();
field1.use1(int1);
await field1.asyncMethod();
field1.use1(int1);
await field1.asyncMethod();
field1.use1(int1);
}
}
class LiveInt4 extends LiveVarsBench {
LiveInt4() : super('AsyncLiveVars.LiveInt4');
final field1 = MockClass2();
final field2 = MockClass2();
@override
Future<void> run() async {
final int1 = field1.get1();
final int2 = field1.get2();
final int3 = field2.get1();
final int4 = field2.get2();
await field1.asyncMethod();
field1.use1(int1);
await field1.asyncMethod();
field2.use1(int3);
await field2.asyncMethod();
field1.use4(int1, int2, int3, int4);
}
}
class LiveObj2Int2 extends LiveVarsBench {
LiveObj2Int2() : super('AsyncLiveVars.LiveObj2Int2');
final field1 = MockClass();
final field2 = MockClass2();
@override
Future<void> run() async {
final obj1 = field1.get1();
final obj2 = field1.get2();
final int1 = field2.get1();
final int2 = field2.get2();
await field1.asyncMethod();
field1.use1(obj1);
await field1.asyncMethod();
field2.use1(int1);
await field2.asyncMethod();
field1.use2(obj1, obj2);
field2.use2(int1, int2);
}
}
class LiveObj4Int4 extends LiveVarsBench {
LiveObj4Int4() : super('AsyncLiveVars.LiveObj4Int4');
final field1 = MockClass();
final field2 = MockClass();
final field3 = MockClass2();
final field4 = MockClass2();
@override
Future<void> run() async {
final obj1 = field1.get1();
final obj2 = field1.get2();
final obj3 = field2.get1();
final obj4 = field2.get2();
final int1 = field3.get1();
final int2 = field3.get2();
final int3 = field4.get1();
final int4 = field4.get2();
await field1.asyncMethod();
field1.use1(obj1);
await field2.asyncMethod();
field3.use2(int2, int4);
await field4.asyncMethod();
field2.use4(obj1, obj2, obj3, obj4);
field4.use4(int1, int2, int3, int4);
}
}
Future<void> main() async {
final benchmarks = [
LiveObj1(),
LiveObj2(),
LiveObj4(),
LiveObj8(),
LiveObj16(),
LiveInt1(),
LiveInt4(),
LiveObj2Int2(),
LiveObj4Int4()
];
for (final bench in benchmarks) {
await bench.report();
}
}