mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:18:13 +00:00
af1b5b8044
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>
319 lines
8.4 KiB
Dart
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();
|
|
}
|
|
}
|