dart-sdk/benchmarks/FfiCall/dart/FfiCall.dart
Daco Harkes b113fffb65 Reland "[vm/ffi] Throw on returning Error in Handle"
Fix: Check handle contents for Smi.
Closes: https://github.com/flutter/flutter/issues/112726

Orignal CL description:

Makes `Dart_Handle` FFI returns behave as the following snippet:

```
Dart_Handle ExampleSnippet() {
  Dart_Handle result = ...;
  if (Dart_IsError(result)) {
    Dart_PropagateError(result);
  }
  return result;
}
```

Also makes FFI consistent with Dart_NativeFunctions, which will
automatically throw upon return if Dart_SetReturnValue set the result
to an error.

`UnhandledExceptions` cannot flow out into Dart generated code. So,
the implementation needs to be in `FfiCallInstr::EmitNativeCode`.

Using `Dart_IsError` is slow compared to a machine code class id
check. So, we should do the handle unwrapping and class id check in
machine code.

Unwrapping Handles in machine code is only safe when the GC is
guaranteed to not run: Either (1) in `kThreadInGenerated`, or (2) in
`kThreadInNative`, but only when transitioned into safepoint. So, the
handle cannot be unwrapped immediately after the FFI call in machine code. We first need to transition back to generated.

This means we need to transition again to native to do the actual
`Dart_PropagateError` call. We can do so without the stub in JIT
because we never return with normal control flow.

Performance impact of this change is within benchmark noise in both
JIT and AOT.
Size impact is 42 bytes on x64, which is 10% in AOT and 12% in JIT.

For more numbers see: go/dart-ffi-handle-error

TEST=runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
TEST=tests/ffi/vmspecific_handle_test.dart

Closes: https://github.com/dart-lang/sdk/issues/49936
Change-Id: Id8edfd841a7d6246438386007d83747868a0a151
Cq-Include-Trybots: luci.dart.try:vm-canary-linux-debug-try,vm-ffi-android-debug-arm64c-try,vm-ffi-android-debug-arm-try,vm-kernel-gcc-linux-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-debug-x64c-try,vm-kernel-msvc-windows-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-tsan-linux-release-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-precomp-ffi-qemu-linux-release-riscv64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-mac-release-arm64-try,vm-kernel-precomp-win-debug-x64c-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262342
Reviewed-by: Martin Kustermann <kustermann@google.com>
Auto-Submit: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
2022-10-03 12:25:08 +00:00

1349 lines
35 KiB
Dart

// Copyright (c) 2019, 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.
// TODO(37581): Generate this file.
// These micro benchmarks track the speed of reading and writing C memory from
// Dart with a specific marshalling and unmarshalling of data.
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'dlopen_helper.dart';
// Number of benchmark iterations per function.
const N = 1000;
// The native library that holds all the native functions being called.
DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific('native_functions',
path: Platform.script.resolve('../native/out/').path);
//
// Native types and their Dart counterparts.
//
typedef NativeFunction1Uint8 = Uint8 Function(Uint8);
typedef NativeFunction1Uint16 = Uint16 Function(Uint16);
typedef NativeFunction1Uint32 = Uint32 Function(Uint32);
typedef NativeFunction1Uint64 = Uint64 Function(Uint64);
typedef NativeFunction1Int8 = Int8 Function(Int8);
typedef NativeFunction1Int16 = Int16 Function(Int16);
typedef NativeFunction1Int32 = Int32 Function(Int32);
typedef NativeFunction1Int64 = Int64 Function(Int64);
typedef Function1int = int Function(int);
typedef NativeFunction2Int32 = Int32 Function(Int32, Int32);
typedef NativeFunction2Int64 = Int64 Function(Int64, Int64);
typedef Function2int = int Function(int, int);
typedef NativeFunction4Int32 = Int32 Function(Int32, Int32, Int32, Int32);
typedef NativeFunction4Int64 = Int64 Function(Int64, Int64, Int64, Int64);
typedef Function4int = int Function(int, int, int, int);
typedef NativeFunction10Int32 = Int32 Function(
Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32);
typedef NativeFunction10Int64 = Int64 Function(
Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64);
typedef Function10int = int Function(
int, int, int, int, int, int, int, int, int, int);
typedef NativeFunction20Int32 = Int32 Function(
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32,
Int32);
typedef NativeFunction20Int64 = Int64 Function(
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64,
Int64);
typedef Function20int = int Function(int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int);
typedef NativeFunction1Float = Float Function(Float);
typedef NativeFunction1Double = Double Function(Double);
typedef Function1double = double Function(double);
typedef NativeFunction2Float = Float Function(Float, Float);
typedef NativeFunction2Double = Double Function(Double, Double);
typedef Function2double = double Function(double, double);
typedef NativeFunction4Float = Float Function(Float, Float, Float, Float);
typedef NativeFunction4Double = Double Function(Double, Double, Double, Double);
typedef Function4double = double Function(double, double, double, double);
typedef NativeFunction10Float = Float Function(
Float, Float, Float, Float, Float, Float, Float, Float, Float, Float);
typedef NativeFunction10Double = Double Function(Double, Double, Double, Double,
Double, Double, Double, Double, Double, Double);
typedef Function10double = double Function(double, double, double, double,
double, double, double, double, double, double);
typedef NativeFunction20Float = Float Function(
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float,
Float);
typedef NativeFunction20Double = Double Function(
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double,
Double);
typedef Function20double = double Function(
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double);
typedef Function1PointerUint8 = Pointer<Uint8> Function(Pointer<Uint8>);
typedef Function2PointerUint8 = Pointer<Uint8> Function(
Pointer<Uint8>, Pointer<Uint8>);
typedef Function4PointerUint8 = Pointer<Uint8> Function(
Pointer<Uint8>, Pointer<Uint8>, Pointer<Uint8>, Pointer<Uint8>);
typedef Function10PointerUint8 = Pointer<Uint8> Function(
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>);
typedef Function20PointerUint8 = Pointer<Uint8> Function(
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>);
typedef NativeFunction1Handle = Handle Function(Handle);
typedef Function1Object = Object Function(Object);
typedef NativeFunction2Handle = Handle Function(Handle, Handle);
typedef Function2Object = Object Function(Object, Object);
typedef NativeFunction4Handle = Handle Function(Handle, Handle, Handle, Handle);
typedef Function4Object = Object Function(Object, Object, Object, Object);
typedef NativeFunction10Handle = Handle Function(Handle, Handle, Handle, Handle,
Handle, Handle, Handle, Handle, Handle, Handle);
typedef Function10Object = Object Function(Object, Object, Object, Object,
Object, Object, Object, Object, Object, Object);
typedef NativeFunction20Handle = Handle Function(
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle,
Handle);
typedef Function20Object = Object Function(
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object,
Object);
//
// Benchmark fixtures.
//
abstract class FfiBenchmarkBase extends BenchmarkBase {
final bool isLeaf;
FfiBenchmarkBase(String name, {this.isLeaf: false})
: super('$name${isLeaf ? 'Leaf' : ''}');
void expectEquals(actual, expected) {
if (actual != expected) {
throw Exception('$name: Unexpected result: $actual, expected $expected');
}
}
void expectApprox(actual, expected) {
if (0.999 * expected > actual || actual > 1.001 * expected) {
throw Exception('$name: Unexpected result: $actual, expected $expected');
}
}
void expectIdentical(actual, expected) {
if (!identical(actual, expected)) {
throw Exception('$name: Unexpected result: $actual, expected $expected');
}
}
}
class Uint8x01 extends FfiBenchmarkBase {
final Function1int f;
Uint8x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Uint8,
Function1int>('Function1Uint8', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Uint8,
Function1int>('Function1Uint8', isLeaf: false),
super('FfiCall.Uint8x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(17);
}
expectEquals(x, N * 17 + N * 42);
}
}
class Uint16x01 extends FfiBenchmarkBase {
final Function1int f;
Uint16x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
Function1int>('Function1Uint16', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
Function1int>('Function1Uint16', isLeaf: false),
super('FfiCall.Uint16x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(17);
}
expectEquals(x, N * (17 + 42));
}
}
class Uint32x01 extends FfiBenchmarkBase {
final Function1int f;
Uint32x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
Function1int>('Function1Uint32', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
Function1int>('Function1Uint32', isLeaf: false),
super('FfiCall.Uint32x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i);
}
expectEquals(x, N * (N - 1) / 2 + N * 42);
}
}
class Uint64x01 extends FfiBenchmarkBase {
final Function1int f;
Uint64x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
Function1int>('Function1Uint64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
Function1int>('Function1Uint64', isLeaf: false),
super('FfiCall.Uint64x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i);
}
expectEquals(x, N * (N - 1) / 2 + N * 42);
}
}
class Int8x01 extends FfiBenchmarkBase {
final Function1int f;
Int8x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Int8,
Function1int>('Function1Int8', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Int8,
Function1int>('Function1Int8', isLeaf: false),
super('FfiCall.Int8x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(17);
}
expectEquals(x, N * (17 + 42));
}
}
class Int16x01 extends FfiBenchmarkBase {
final Function1int f;
Int16x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Int16,
Function1int>('Function1Int16', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Int16,
Function1int>('Function1Int16', isLeaf: false),
super('FfiCall.Int16x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(17);
}
expectEquals(x, N * (17 + 42));
}
}
class Int32x01 extends FfiBenchmarkBase {
final Function1int f;
Int32x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Int32,
Function1int>('Function1Int32', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Int32,
Function1int>('Function1Int32', isLeaf: false),
super('FfiCall.Int32x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i);
}
expectEquals(x, N * (N - 1) / 2 + N * 42);
}
}
class Int32x02 extends FfiBenchmarkBase {
final Function2int f;
Int32x02({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction2Int32,
Function2int>('Function2Int32', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction2Int32,
Function2int>('Function2Int32', isLeaf: false),
super('FfiCall.Int32x02', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i);
}
expectEquals(x, N * (N - 1) * 2 / 2);
}
}
class Int32x04 extends FfiBenchmarkBase {
final Function4int f;
Int32x04({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction4Int32,
Function4int>('Function4Int32', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction4Int32,
Function4int>('Function4Int32', isLeaf: false),
super('FfiCall.Int32x04', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i, i, i);
}
expectEquals(x, N * (N - 1) * 4 / 2);
}
}
class Int32x10 extends FfiBenchmarkBase {
final Function10int f;
Int32x10({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction10Int32,
Function10int>('Function10Int32', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction10Int32,
Function10int>('Function10Int32', isLeaf: false),
super('FfiCall.Int32x10', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i, i, i, i, i, i, i, i, i);
}
expectEquals(x, N * (N - 1) * 10 / 2);
}
}
class Int32x20 extends FfiBenchmarkBase {
final Function20int f;
Int32x20({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction20Int32,
Function20int>('Function20Int32', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction20Int32,
Function20int>('Function20Int32', isLeaf: false),
super('FfiCall.Int32x20', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
}
expectEquals(x, N * (N - 1) * 20 / 2);
}
}
class Int64x01 extends FfiBenchmarkBase {
final Function1int f;
Int64x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Int64,
Function1int>('Function1Int64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Int64,
Function1int>('Function1Int64', isLeaf: false),
super('FfiCall.Int64x01', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i);
}
expectEquals(x, N * (N - 1) / 2 + N * 42);
}
}
class Int64x02 extends FfiBenchmarkBase {
final Function2int f;
Int64x02({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction2Int64,
Function2int>('Function2Int64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction2Int64,
Function2int>('Function2Int64', isLeaf: false),
super('FfiCall.Int64x02', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i);
}
expectEquals(x, N * (N - 1) * 2 / 2);
}
}
class Int64x04 extends FfiBenchmarkBase {
final Function4int f;
Int64x04({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction4Int64,
Function4int>('Function4Int64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction4Int64,
Function4int>('Function4Int64', isLeaf: false),
super('FfiCall.Int64x04', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i, i, i);
}
expectEquals(x, N * (N - 1) * 4 / 2);
}
}
class Int64x10 extends FfiBenchmarkBase {
final Function10int f;
Int64x10({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction10Int64,
Function10int>('Function10Int64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction10Int64,
Function10int>('Function10Int64', isLeaf: false),
super('FfiCall.Int64x10', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i, i, i, i, i, i, i, i, i);
}
expectEquals(x, N * (N - 1) * 10 / 2);
}
}
class Int64x20 extends FfiBenchmarkBase {
final Function20int f;
Int64x20({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction20Int64,
Function20int>('Function20Int64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction20Int64,
Function20int>('Function20Int64', isLeaf: false),
super('FfiCall.Int64x20', isLeaf: isLeaf);
@override
void run() {
int x = 0;
for (int i = 0; i < N; i++) {
x += f(i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
}
expectEquals(x, N * (N - 1) * 20 / 2);
}
}
class Int64Mintx01 extends FfiBenchmarkBase {
final Function1int f;
Int64Mintx01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Int64,
Function1int>('Function1Int64', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Int64,
Function1int>('Function1Int64', isLeaf: false),
super('FfiCall.Int64Mintx01', isLeaf: isLeaf);
@override
void run() {
int x = 0x7FFFFFFF00000000;
for (int i = 0; i < N; i++) {
x = f(x);
}
expectEquals(x, 0x7FFFFFFF00000000 + N * 42);
}
}
class Floatx01 extends FfiBenchmarkBase {
final Function1double f;
Floatx01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Float,
Function1double>('Function1Float', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Float,
Function1double>('Function1Float', isLeaf: false),
super('FfiCall.Floatx01', isLeaf: isLeaf);
@override
void run() {
double x = 0.0;
for (int i = 0; i < N; i++) {
x += f(17.0);
}
expectApprox(x, N * (17.0 + 42.0));
}
}
class Floatx02 extends FfiBenchmarkBase {
final Function2double f;
Floatx02({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction2Float,
Function2double>('Function2Float', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction2Float,
Function2double>('Function2Float', isLeaf: false),
super('FfiCall.Floatx02', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0);
}
expectApprox(x, N * (1.0 + 2.0));
}
}
class Floatx04 extends FfiBenchmarkBase {
final Function4double f;
Floatx04({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction4Float,
Function4double>('Function4Float', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction4Float,
Function4double>('Function4Float', isLeaf: false),
super('FfiCall.Floatx04', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0, 3.0, 4.0);
}
final double expected = N * (1.0 + 2.0 + 3.0 + 4.0);
expectApprox(x, expected);
}
}
class Floatx10 extends FfiBenchmarkBase {
final Function10double f;
Floatx10({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction10Float,
Function10double>('Function10Float', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction10Float,
Function10double>('Function10Float', isLeaf: false),
super('FfiCall.Floatx10', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
}
final double expected =
N * (1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0 + 8.0 + 9.0 + 10.0);
expectApprox(x, expected);
}
}
class Floatx20 extends FfiBenchmarkBase {
final Function20double f;
Floatx20({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction20Float,
Function20double>('Function20Float', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction20Float,
Function20double>('Function20Float', isLeaf: false),
super('FfiCall.Floatx20', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0);
}
final double expected = N *
(1.0 +
2.0 +
3.0 +
4.0 +
5.0 +
6.0 +
7.0 +
8.0 +
9.0 +
10.0 +
11.0 +
12.0 +
13.0 +
14.0 +
15.0 +
16.0 +
17.0 +
18.0 +
19.0 +
20.0);
expectApprox(x, expected);
}
}
class Doublex01 extends FfiBenchmarkBase {
final Function1double f;
Doublex01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction1Double,
Function1double>('Function1Double', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction1Double,
Function1double>('Function1Double', isLeaf: false),
super('FfiCall.Doublex01', isLeaf: isLeaf);
@override
void run() {
double x = 0.0;
for (int i = 0; i < N; i++) {
x += f(17.0);
}
final double expected = N * (17.0 + 42.0);
expectApprox(x, expected);
}
}
class Doublex02 extends FfiBenchmarkBase {
final Function2double f;
Doublex02({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction2Double,
Function2double>('Function2Double', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction2Double,
Function2double>('Function2Double', isLeaf: false),
super('FfiCall.Doublex02', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0);
}
final double expected = N * (1.0 + 2.0);
expectApprox(x, expected);
}
}
class Doublex04 extends FfiBenchmarkBase {
final Function4double f;
Doublex04({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction4Double,
Function4double>('Function4Double', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction4Double,
Function4double>('Function4Double', isLeaf: false),
super('FfiCall.Doublex04', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0, 3.0, 4.0);
}
final double expected = N * (1.0 + 2.0 + 3.0 + 4.0);
expectApprox(x, expected);
}
}
class Doublex10 extends FfiBenchmarkBase {
final Function10double f;
Doublex10({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction10Double,
Function10double>('Function10Double', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction10Double,
Function10double>('Function10Double', isLeaf: false),
super('FfiCall.Doublex10', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
}
final double expected =
N * (1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0 + 8.0 + 9.0 + 10.0);
expectApprox(x, expected);
}
}
class Doublex20 extends FfiBenchmarkBase {
final Function20double f;
Doublex20({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<NativeFunction20Double,
Function20double>('Function20Double', isLeaf: true)
: ffiTestFunctions.lookupFunction<NativeFunction20Double,
Function20double>('Function20Double', isLeaf: false),
super('FfiCall.Doublex20', isLeaf: isLeaf);
@override
void run() {
double x = 0;
for (int i = 0; i < N; i++) {
x += f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0);
}
final double expected = N *
(1.0 +
2.0 +
3.0 +
4.0 +
5.0 +
6.0 +
7.0 +
8.0 +
9.0 +
10.0 +
11.0 +
12.0 +
13.0 +
14.0 +
15.0 +
16.0 +
17.0 +
18.0 +
19.0 +
20.0);
expectApprox(x, expected);
}
}
class PointerUint8x01 extends FfiBenchmarkBase {
final Function1PointerUint8 f;
PointerUint8x01({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<Function1PointerUint8,
Function1PointerUint8>('Function1PointerUint8', isLeaf: true)
: ffiTestFunctions.lookupFunction<Function1PointerUint8,
Function1PointerUint8>('Function1PointerUint8', isLeaf: false),
super('FfiCall.PointerUint8x01', isLeaf: isLeaf);
Pointer<Uint8> p1 = nullptr;
@override
void setup() => p1 = calloc(N + 1);
@override
void teardown() => calloc.free(p1);
@override
void run() {
Pointer<Uint8> x = p1;
for (int i = 0; i < N; i++) {
x = f(x);
}
expectApprox(x.address, p1.address + N);
}
}
class PointerUint8x02 extends FfiBenchmarkBase {
final Function2PointerUint8 f;
PointerUint8x02({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<Function2PointerUint8,
Function2PointerUint8>('Function2PointerUint8', isLeaf: true)
: ffiTestFunctions.lookupFunction<Function2PointerUint8,
Function2PointerUint8>('Function2PointerUint8', isLeaf: false),
super('FfiCall.PointerUint8x02', isLeaf: isLeaf);
Pointer<Uint8> p1 = nullptr;
Pointer<Uint8> p2 = nullptr;
@override
void setup() {
p1 = calloc(N + 1);
p2 = p1.elementAt(1);
}
@override
void teardown() {
calloc.free(p1);
}
@override
void run() {
Pointer<Uint8> x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2);
}
expectEquals(x.address, p1.address + N * sizeOf<Uint8>());
}
}
class PointerUint8x04 extends FfiBenchmarkBase {
final Function4PointerUint8 f;
PointerUint8x04({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<Function4PointerUint8,
Function4PointerUint8>('Function4PointerUint8', isLeaf: true)
: ffiTestFunctions.lookupFunction<Function4PointerUint8,
Function4PointerUint8>('Function4PointerUint8', isLeaf: false),
super('FfiCall.PointerUint8x04', isLeaf: isLeaf);
Pointer<Uint8> p1 = nullptr;
Pointer<Uint8> p2 = nullptr;
Pointer<Uint8> p3 = nullptr;
Pointer<Uint8> p4 = nullptr;
@override
void setup() {
p1 = calloc(N + 1);
p2 = p1.elementAt(1);
p3 = p1.elementAt(2);
p4 = p1.elementAt(3);
}
@override
void teardown() {
calloc.free(p1);
}
@override
void run() {
Pointer<Uint8> x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2, p3, p4);
}
expectEquals(x.address, p1.address + N * sizeOf<Uint8>());
}
}
class PointerUint8x10 extends FfiBenchmarkBase {
final Function10PointerUint8 f;
PointerUint8x10({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<Function10PointerUint8,
Function10PointerUint8>('Function10PointerUint8', isLeaf: true)
: ffiTestFunctions
.lookupFunction<Function10PointerUint8, Function10PointerUint8>(
'Function10PointerUint8',
isLeaf: false),
super('FfiCall.PointerUint8x10', isLeaf: isLeaf);
Pointer<Uint8> p1 = nullptr;
Pointer<Uint8> p2 = nullptr;
Pointer<Uint8> p3 = nullptr;
Pointer<Uint8> p4 = nullptr;
Pointer<Uint8> p5 = nullptr;
Pointer<Uint8> p6 = nullptr;
Pointer<Uint8> p7 = nullptr;
Pointer<Uint8> p8 = nullptr;
Pointer<Uint8> p9 = nullptr;
Pointer<Uint8> p10 = nullptr;
@override
void setup() {
p1 = calloc(N + 1);
p2 = p1.elementAt(1);
p3 = p1.elementAt(2);
p4 = p1.elementAt(3);
p5 = p1.elementAt(4);
p6 = p1.elementAt(5);
p7 = p1.elementAt(6);
p8 = p1.elementAt(7);
p9 = p1.elementAt(8);
p10 = p1.elementAt(9);
}
@override
void teardown() {
calloc.free(p1);
}
@override
void run() {
Pointer<Uint8> x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2, p3, p4, p5, p6, p7, p8, p9, p10);
}
expectEquals(x.address, p1.address + N * sizeOf<Uint8>());
}
}
class PointerUint8x20 extends FfiBenchmarkBase {
final Function20PointerUint8 f;
PointerUint8x20({isLeaf: false})
: f = isLeaf
? ffiTestFunctions.lookupFunction<Function20PointerUint8,
Function20PointerUint8>('Function20PointerUint8', isLeaf: true)
: ffiTestFunctions
.lookupFunction<Function20PointerUint8, Function20PointerUint8>(
'Function20PointerUint8',
isLeaf: false),
super('FfiCall.PointerUint8x20', isLeaf: isLeaf);
Pointer<Uint8> p1 = nullptr;
Pointer<Uint8> p2 = nullptr;
Pointer<Uint8> p3 = nullptr;
Pointer<Uint8> p4 = nullptr;
Pointer<Uint8> p5 = nullptr;
Pointer<Uint8> p6 = nullptr;
Pointer<Uint8> p7 = nullptr;
Pointer<Uint8> p8 = nullptr;
Pointer<Uint8> p9 = nullptr;
Pointer<Uint8> p10 = nullptr;
Pointer<Uint8> p11 = nullptr;
Pointer<Uint8> p12 = nullptr;
Pointer<Uint8> p13 = nullptr;
Pointer<Uint8> p14 = nullptr;
Pointer<Uint8> p15 = nullptr;
Pointer<Uint8> p16 = nullptr;
Pointer<Uint8> p17 = nullptr;
Pointer<Uint8> p18 = nullptr;
Pointer<Uint8> p19 = nullptr;
Pointer<Uint8> p20 = nullptr;
@override
void setup() {
p1 = calloc(N + 1);
p2 = p1.elementAt(1);
p3 = p1.elementAt(2);
p4 = p1.elementAt(3);
p5 = p1.elementAt(4);
p6 = p1.elementAt(5);
p7 = p1.elementAt(6);
p8 = p1.elementAt(7);
p9 = p1.elementAt(8);
p10 = p1.elementAt(9);
p11 = p1.elementAt(10);
p12 = p1.elementAt(11);
p13 = p1.elementAt(12);
p14 = p1.elementAt(13);
p15 = p1.elementAt(14);
p16 = p1.elementAt(15);
p17 = p1.elementAt(16);
p18 = p1.elementAt(17);
p19 = p1.elementAt(18);
p20 = p1.elementAt(19);
}
@override
void teardown() {
calloc.free(p1);
}
@override
void run() {
Pointer<Uint8> x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
p16, p17, p18, p19, p20);
}
expectEquals(x.address, p1.address + N * sizeOf<Uint8>());
}
}
class MyClass {
int a;
MyClass(this.a);
}
class Handlex01 extends FfiBenchmarkBase {
final Function1Object f;
Handlex01()
: f = ffiTestFunctions.lookupFunction<NativeFunction1Handle,
Function1Object>('Function1Handle', isLeaf: false),
super('FfiCall.Handlex01', isLeaf: false);
@override
void run() {
final p1 = MyClass(123);
Object x = p1;
for (int i = 0; i < N; i++) {
x = f(x);
}
expectIdentical(x, p1);
}
}
class Handlex02 extends FfiBenchmarkBase {
final Function2Object f;
Handlex02()
: f = ffiTestFunctions.lookupFunction<NativeFunction2Handle,
Function2Object>('Function2Handle', isLeaf: false),
super('FfiCall.Handlex02', isLeaf: false);
@override
void run() {
final p1 = MyClass(123);
final p2 = MyClass(2);
Object x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2);
}
expectIdentical(x, p1);
}
}
class Handlex04 extends FfiBenchmarkBase {
final Function4Object f;
Handlex04()
: f = ffiTestFunctions.lookupFunction<NativeFunction4Handle,
Function4Object>('Function4Handle', isLeaf: false),
super('FfiCall.Handlex04', isLeaf: false);
@override
void run() {
final p1 = MyClass(123);
final p2 = MyClass(2);
final p3 = MyClass(3);
final p4 = MyClass(4);
Object x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2, p3, p4);
}
expectIdentical(x, p1);
}
}
class Handlex10 extends FfiBenchmarkBase {
final Function10Object f;
Handlex10()
: f = ffiTestFunctions.lookupFunction<NativeFunction10Handle,
Function10Object>('Function10Handle', isLeaf: false),
super('FfiCall.Handlex10', isLeaf: false);
@override
void run() {
final p1 = MyClass(123);
final p2 = MyClass(2);
final p3 = MyClass(3);
final p4 = MyClass(4);
final p5 = MyClass(5);
final p6 = MyClass(6);
final p7 = MyClass(7);
final p8 = MyClass(8);
final p9 = MyClass(9);
final p10 = MyClass(10);
Object x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2, p3, p4, p5, p6, p7, p8, p9, p10);
}
expectIdentical(x, p1);
}
}
class Handlex20 extends FfiBenchmarkBase {
final Function20Object f;
Handlex20()
: f = ffiTestFunctions.lookupFunction<NativeFunction20Handle,
Function20Object>('Function20Handle', isLeaf: false),
super('FfiCall.Handlex20', isLeaf: false);
@override
void run() {
final p1 = MyClass(123);
final p2 = MyClass(2);
final p3 = MyClass(3);
final p4 = MyClass(4);
final p5 = MyClass(5);
final p6 = MyClass(6);
final p7 = MyClass(7);
final p8 = MyClass(8);
final p9 = MyClass(9);
final p10 = MyClass(10);
final p11 = MyClass(11);
final p12 = MyClass(12);
final p13 = MyClass(13);
final p14 = MyClass(14);
final p15 = MyClass(15);
final p16 = MyClass(16);
final p17 = MyClass(17);
final p18 = MyClass(18);
final p19 = MyClass(19);
final p20 = MyClass(20);
Object x = p1;
for (int i = 0; i < N; i++) {
x = f(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
p16, p17, p18, p19, p20);
}
expectIdentical(x, p1);
}
}
//
// Main driver.
//
void main(List<String> args) {
final benchmarks = [
() => Uint8x01(),
() => Uint8x01(isLeaf: true),
() => Uint16x01(),
() => Uint32x01(),
() => Uint64x01(),
() => Int8x01(),
() => Int8x01(isLeaf: true),
() => Int16x01(),
() => Int32x01(),
() => Int32x02(),
() => Int32x04(),
() => Int32x10(),
() => Int32x20(),
() => Int64x01(),
() => Int64x02(),
() => Int64x04(),
() => Int64x10(),
() => Int64x20(),
() => Int64x20(isLeaf: true),
() => Int64Mintx01(),
() => Int64Mintx01(isLeaf: true),
() => Floatx01(),
() => Floatx02(),
() => Floatx04(),
() => Floatx10(),
() => Floatx20(),
() => Floatx20(isLeaf: true),
() => Doublex01(),
() => Doublex02(),
() => Doublex04(),
() => Doublex10(),
() => Doublex20(),
() => Doublex20(isLeaf: true),
() => PointerUint8x01(),
() => PointerUint8x02(),
() => PointerUint8x04(),
() => PointerUint8x10(),
() => PointerUint8x20(),
() => PointerUint8x20(isLeaf: true),
() => Handlex01(),
() => Handlex02(),
() => Handlex04(),
() => Handlex10(),
() => Handlex20(),
];
final filter = args.length == 1 ? args[0] : null;
for (final benchmark in benchmarks) {
final b = benchmark();
final run = (filter != null) ? b.name.contains(filter) : true;
if (run) {
b.report();
}
}
}