blob: a16ef74430723d58ee02a7eef515f153ac741ef1 [file] [log] [blame]
// 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();
}
}