mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 17:40:04 +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>
303 lines
7.8 KiB
Dart
303 lines
7.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.
|
|
|
|
import 'package:benchmark_harness/benchmark_harness.dart';
|
|
|
|
// Micro-benchmark for multiple returns.
|
|
//
|
|
// The goal of this benchmark is to compare and track performance of
|
|
// various ways to return multiple values from a method.
|
|
|
|
int input1 = int.parse('42');
|
|
String input2 = input1.toString();
|
|
|
|
const int N = 1000000;
|
|
final int expectedSum = (input1 + input2.length) * N;
|
|
|
|
class ResultClass {
|
|
final int result0;
|
|
final String result1;
|
|
const ResultClass(this.result0, this.result1);
|
|
}
|
|
|
|
@pragma('vm:prefer-inline')
|
|
@pragma('wasm:prefer-inline')
|
|
@pragma('dart2js:prefer-inline')
|
|
List<Object> inlinedList() => [input1, input2];
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
List<Object> notInlinedList() => [input1, input2];
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
List<Object> forwardedList() => notInlinedList();
|
|
|
|
@pragma('vm:prefer-inline')
|
|
@pragma('wasm:prefer-inline')
|
|
@pragma('dart2js:prefer-inline')
|
|
ResultClass inlinedClass() => ResultClass(input1, input2);
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
ResultClass notInlinedClass() => ResultClass(input1, input2);
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
ResultClass forwardedClass() => notInlinedClass();
|
|
|
|
@pragma('vm:prefer-inline')
|
|
@pragma('wasm:prefer-inline')
|
|
@pragma('dart2js:prefer-inline')
|
|
(int, String) inlinedRecord() => (input1, input2);
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
(int, String) notInlinedRecord() => (input1, input2);
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
(int, String) forwardedRecord() => notInlinedRecord();
|
|
|
|
@pragma('vm:prefer-inline')
|
|
@pragma('wasm:prefer-inline')
|
|
@pragma('dart2js:prefer-inline')
|
|
({int result0, String result1}) inlinedRecordNamed() =>
|
|
(result0: input1, result1: input2);
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
({int result0, String result1}) notInlinedRecordNamed() =>
|
|
(result0: input1, result1: input2);
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
({int result0, String result1}) forwardedRecordNamed() =>
|
|
notInlinedRecordNamed();
|
|
|
|
class BenchInlinedList extends BenchmarkBase {
|
|
BenchInlinedList() : super('MultipleReturns.Inlined.List');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = inlinedList();
|
|
final int r0 = result[0] as int;
|
|
final String r1 = result[1] as String;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchNotInlinedList extends BenchmarkBase {
|
|
BenchNotInlinedList() : super('MultipleReturns.NotInlined.List');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = notInlinedList();
|
|
final int r0 = result[0] as int;
|
|
final String r1 = result[1] as String;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchForwardedList extends BenchmarkBase {
|
|
BenchForwardedList() : super('MultipleReturns.Forwarded.List');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = forwardedList();
|
|
final int r0 = result[0] as int;
|
|
final String r1 = result[1] as String;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchInlinedClass extends BenchmarkBase {
|
|
BenchInlinedClass() : super('MultipleReturns.Inlined.Class');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = inlinedClass();
|
|
final int r0 = result.result0;
|
|
final String r1 = result.result1;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchNotInlinedClass extends BenchmarkBase {
|
|
BenchNotInlinedClass() : super('MultipleReturns.NotInlined.Class');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = notInlinedClass();
|
|
final int r0 = result.result0;
|
|
final String r1 = result.result1;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchForwardedClass extends BenchmarkBase {
|
|
BenchForwardedClass() : super('MultipleReturns.Forwarded.Class');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = forwardedClass();
|
|
final int r0 = result.result0;
|
|
final String r1 = result.result1;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchInlinedRecord extends BenchmarkBase {
|
|
BenchInlinedRecord() : super('MultipleReturns.Inlined.Record');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = inlinedRecord();
|
|
final int r0 = result.$1;
|
|
final String r1 = result.$2;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchNotInlinedRecord extends BenchmarkBase {
|
|
BenchNotInlinedRecord() : super('MultipleReturns.NotInlined.Record');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = notInlinedRecord();
|
|
final int r0 = result.$1;
|
|
final String r1 = result.$2;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchForwardedRecord extends BenchmarkBase {
|
|
BenchForwardedRecord() : super('MultipleReturns.Forwarded.Record');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = forwardedRecord();
|
|
final int r0 = result.$1;
|
|
final String r1 = result.$2;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchInlinedRecordNamed extends BenchmarkBase {
|
|
BenchInlinedRecordNamed() : super('MultipleReturns.Inlined.RecordNamed');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = inlinedRecordNamed();
|
|
final int r0 = result.result0;
|
|
final String r1 = result.result1;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchNotInlinedRecordNamed extends BenchmarkBase {
|
|
BenchNotInlinedRecordNamed()
|
|
: super('MultipleReturns.NotInlined.RecordNamed');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = notInlinedRecordNamed();
|
|
final int r0 = result.result0;
|
|
final String r1 = result.result1;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchForwardedRecordNamed extends BenchmarkBase {
|
|
BenchForwardedRecordNamed() : super('MultipleReturns.Forwarded.RecordNamed');
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
final result = forwardedRecordNamed();
|
|
final int r0 = result.result0;
|
|
final String r1 = result.result1;
|
|
sum += r0 + r1.length;
|
|
}
|
|
if (sum != expectedSum) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
final benchmarks = [
|
|
BenchInlinedList(),
|
|
BenchInlinedClass(),
|
|
BenchInlinedRecord(),
|
|
BenchInlinedRecordNamed(),
|
|
BenchNotInlinedList(),
|
|
BenchNotInlinedClass(),
|
|
BenchNotInlinedRecord(),
|
|
BenchNotInlinedRecordNamed(),
|
|
BenchForwardedList(),
|
|
BenchForwardedClass(),
|
|
BenchForwardedRecord(),
|
|
BenchForwardedRecordNamed(),
|
|
];
|
|
|
|
for (final benchmark in benchmarks) {
|
|
benchmark.warmup();
|
|
}
|
|
for (final benchmark in benchmarks) {
|
|
benchmark.report();
|
|
}
|
|
}
|