| // 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. |
| |
| // 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:benchmark_harness/benchmark_harness.dart'; |
| import 'package:ffi/ffi.dart'; |
| |
| import 'dlopen_helper.dart'; |
| |
| part 'benchmark_generated.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); |
| |
| 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 MyClass { |
| int a; |
| MyClass(this.a); |
| } |
| |
| // |
| // Non-generated benchmark: This benchmark does not have a common structure with |
| // the generated benchmarks. |
| // |
| |
| 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); |
| } |
| } |
| |
| // |
| // Main driver. |
| // |
| |
| void main(List<String> args) { |
| // Force loading the dylib with RLTD_GLOBAL so that the |
| // Native benchmarks below can do process lookup. |
| dlopenGlobalPlatformSpecific('native_functions', |
| path: Platform.script.resolve('../native/out/').path); |
| |
| final benchmarks = [ |
| Uint8x01.new, |
| () => Uint8x01(isLeaf: true), |
| Uint8x01Native.new, |
| Uint8x01NativeLeaf.new, |
| Uint16x01.new, |
| Uint32x01.new, |
| Uint64x01.new, |
| Int8x01.new, |
| () => Int8x01(isLeaf: true), |
| Int8x01Native.new, |
| Int8x01NativeLeaf.new, |
| Int16x01.new, |
| Int32x01.new, |
| Int32x02.new, |
| Int32x04.new, |
| Int32x10.new, |
| Int32x20.new, |
| Int64x01.new, |
| Int64x02.new, |
| Int64x04.new, |
| Int64x10.new, |
| Int64x20.new, |
| () => Int64x20(isLeaf: true), |
| Int64x20Native.new, |
| Int64x20NativeLeaf.new, |
| Int64Mintx01.new, |
| () => Int64Mintx01(isLeaf: true), |
| Floatx01.new, |
| Floatx02.new, |
| Floatx04.new, |
| Floatx10.new, |
| Floatx20.new, |
| () => Floatx20(isLeaf: true), |
| Floatx20Native.new, |
| Floatx20NativeLeaf.new, |
| Doublex01.new, |
| Doublex02.new, |
| Doublex04.new, |
| Doublex10.new, |
| Doublex20.new, |
| () => Doublex20(isLeaf: true), |
| Doublex20Native.new, |
| Doublex20NativeLeaf.new, |
| PointerUint8x01.new, |
| PointerUint8x02.new, |
| PointerUint8x04.new, |
| PointerUint8x10.new, |
| PointerUint8x20.new, |
| () => PointerUint8x20(isLeaf: true), |
| PointerUint8x20Native.new, |
| PointerUint8x20NativeLeaf.new, |
| Handlex01.new, |
| Handlex02.new, |
| Handlex04.new, |
| Handlex10.new, |
| Handlex20.new, |
| Handlex20Native.new, |
| ]; |
| |
| final filter = args.firstOrNull; |
| for (var constructor in benchmarks) { |
| final benchmark = constructor(); |
| if (filter == null || benchmark.name.contains(filter)) { |
| benchmark.report(); |
| } |
| } |
| } |