From fbf13f561f677b641a252c9ee38cfff080b403a6 Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Fri, 19 Jul 2019 14:59:57 +0000 Subject: [PATCH] [benchmarks/ffi] Add micro and macro benchmarks for dart:ffi Adds micro benchmarks to measure low level (1) C memory reads and writes from Dart and (2) calls from Dart into C. This CL also adds a macro benchmark to measure overall performance using BoringSSL to digest data. The shared libraries are precompiled for Linux and live in cipd packages. The benchmarks run on all hardware architectures (with the exception of Linux'es hardfp on Arm32: https://github.com/dart-lang/sdk/issues/36309). Issue: https://github.com/dart-lang/sdk/issues/36247 Change-Id: I8dfb30cc66a26a2942bb09194c5eb0da0b6ca1b5 Cq-Include-Trybots: luci.dart.try:benchmark-linux-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108724 Commit-Queue: Daco Harkes Reviewed-by: Jonas Termansen Auto-Submit: Daco Harkes --- DEPS | 21 + .../FfiBoringssl/dart/FfiBoringssl.dart | 144 +++ benchmarks/FfiBoringssl/dart/digest.dart | 99 ++ .../FfiBoringssl/dart/dlopen_helper.dart | 58 + .../FfiBoringssl/dart/native/.gitignore | 7 + benchmarks/FfiBoringssl/dart/native/Makefile | 81 ++ benchmarks/FfiBoringssl/dart/native/arm.cmake | 17 + .../FfiBoringssl/dart/native/arm64.cmake | 17 + benchmarks/FfiBoringssl/dart/types.dart | 25 + benchmarks/FfiCall/dart/FfiCall.dart | 1150 +++++++++++++++++ benchmarks/FfiCall/dart/dlopen_helper.dart | 58 + benchmarks/FfiCall/dart/native/.gitignore | 5 + benchmarks/FfiCall/dart/native/Makefile | 60 + .../FfiCall/dart/native/native_functions.c | 132 ++ benchmarks/FfiMemory/dart/FfiMemory.dart | 431 ++++++ benchmarks/FfiStruct/dart/FfiStruct.dart | 120 ++ tests/ffi/function_structs_test.dart | 4 +- tests/ffi/very_large_struct.dart | 4 +- tools/bots/try_benchmarks.sh | 4 + 19 files changed, 2432 insertions(+), 5 deletions(-) create mode 100644 benchmarks/FfiBoringssl/dart/FfiBoringssl.dart create mode 100644 benchmarks/FfiBoringssl/dart/digest.dart create mode 100644 benchmarks/FfiBoringssl/dart/dlopen_helper.dart create mode 100644 benchmarks/FfiBoringssl/dart/native/.gitignore create mode 100644 benchmarks/FfiBoringssl/dart/native/Makefile create mode 100644 benchmarks/FfiBoringssl/dart/native/arm.cmake create mode 100644 benchmarks/FfiBoringssl/dart/native/arm64.cmake create mode 100644 benchmarks/FfiBoringssl/dart/types.dart create mode 100644 benchmarks/FfiCall/dart/FfiCall.dart create mode 100644 benchmarks/FfiCall/dart/dlopen_helper.dart create mode 100644 benchmarks/FfiCall/dart/native/.gitignore create mode 100644 benchmarks/FfiCall/dart/native/Makefile create mode 100644 benchmarks/FfiCall/dart/native/native_functions.c create mode 100644 benchmarks/FfiMemory/dart/FfiMemory.dart create mode 100644 benchmarks/FfiStruct/dart/FfiStruct.dart diff --git a/DEPS b/DEPS index 96dfe4e0f61..2b20bf90ba3 100644 --- a/DEPS +++ b/DEPS @@ -408,6 +408,27 @@ deps = { ], "dep_type": "cipd", }, + + # TODO(37531): Remove these cipd packages and build with sdk instead when + # benchmark runner gets support for that. + Var("dart_root") + "/benchmarks/FfiBoringssl/dart/native/out/": { + "packages": [ + { + "package": "dart/benchmarks/ffiboringssl", + "version": "commit:a86c69888b9a416f5249aacb4690a765be064969", + }, + ], + "dep_type": "cipd", + }, + Var("dart_root") + "/benchmarks/FfiCall/dart/native/out/": { + "packages": [ + { + "package": "dart/benchmarks/fficall", + "version": "version:1", + }, + ], + "dep_type": "cipd", + }, } deps_os = { diff --git a/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart b/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart new file mode 100644 index 00000000000..c87711bab92 --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart @@ -0,0 +1,144 @@ +// 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. + +// Macro-benchmark for ffi with boringssl. + +import 'dart:convert'; +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:benchmark_harness/benchmark_harness.dart'; + +import 'digest.dart'; +import 'types.dart'; + +// +// BoringSSL functions +// + +Uint8List inventData(int length) { + final result = Uint8List(length); + for (int i = 0; i < length; i++) { + result[i] = i % 256; + } + return result; +} + +Uint8List toUint8List(Bytes bytes, int length) { + final result = Uint8List(length); + final uint8bytes = bytes.asUint8Pointer(); + for (int i = 0; i < length; i++) { + result[i] = uint8bytes.elementAt(i).load(); + } + return result; +} + +void copyFromUint8ListToTarget(Uint8List source, Data target) { + final int length = source.length; + final uint8target = target.asUint8Pointer(); + for (int i = 0; i < length; i++) { + uint8target.offsetBy(i).store(source[i]); + } +} + +String hash(Pointer data, int length, Pointer hashAlgorithm) { + final context = EVP_MD_CTX_new(); + EVP_DigestInit(context, hashAlgorithm); + EVP_DigestUpdate(context, data, length); + final int resultSize = EVP_MD_CTX_size(context); + final Pointer result = + Pointer.allocate(count: resultSize).cast(); + EVP_DigestFinal(context, result, nullptr.cast()); + EVP_MD_CTX_free(context); + final String hash = base64Encode(toUint8List(result.load(), resultSize)); + result.free(); + return hash; +} + +// +// Benchmark fixtures. +// + +// Number of repeats: 1 && Length in bytes: 10000000 +// * CPU: Intel(R) Xeon(R) Gold 6154 +// * Architecture: x64 +// * 23000 - 52000000 us (without optimizations) +// * 23000 - 30000 us (with optimizations) +// * Architecture: SimDBC64 +// * 23000 - 5500000 us (without optimizations) +// * 23000 - 30000 us (with optimizations) +const int L = 1000; // Length of data in bytes. + +final hashAlgorithm = EVP_sha512(); + +// Hash of generated data of `L` bytes with `hashAlgorithm`. +const String expectedHash = + "bNLtqb+cBZcSkCmwBUuB5DP2uLe0madetwXv10usGUFJg1sdGhTEi+aW5NWIRW1RKiLq56obV74rVurn014Iyw=="; + +/// This benchmark runs a digest algorithm on data residing in C memory. +/// +/// This benchmark is intended as macro benchmark with a realistic workload. +class DigestCMemory extends BenchmarkBase { + DigestCMemory() : super("FfiBoringssl.DigestCMemory"); + + Pointer data; // Data in C memory that we want to digest. + + void setup() { + data = Pointer.allocate(count: L).cast(); + copyFromUint8ListToTarget(inventData(L), data.load()); + hash(data, L, hashAlgorithm); + } + + void teardown() { + data.free(); + } + + void run() { + final String result = hash(data, L, hashAlgorithm); + if (result != expectedHash) { + throw Exception("$name: Unexpected result: $result"); + } + } +} + +/// This benchmark runs a digest algorithm on data residing in Dart memory. +/// +/// This benchmark is intended as macro benchmark with a realistic workload. +class DigestDartMemory extends BenchmarkBase { + DigestDartMemory() : super("FfiBoringssl.DigestDartMemory"); + + Uint8List data; // Data in C memory that we want to digest. + + void setup() { + data = inventData(L); + final Pointer dataInC = Pointer.allocate(count: L).cast(); + copyFromUint8ListToTarget(data, dataInC.load()); + hash(dataInC, L, hashAlgorithm); + dataInC.free(); + } + + void teardown() {} + + void run() { + final Pointer dataInC = Pointer.allocate(count: L).cast(); + copyFromUint8ListToTarget(data, dataInC.load()); + final String result = hash(dataInC, L, hashAlgorithm); + dataInC.free(); + if (result != expectedHash) { + throw Exception("$name: Unexpected result: $result"); + } + } +} + +// +// Main driver. +// + +main() { + final benchmarks = [ + () => DigestCMemory(), + () => DigestDartMemory(), + ]; + benchmarks.forEach((benchmark) => benchmark().report()); +} diff --git a/benchmarks/FfiBoringssl/dart/digest.dart b/benchmarks/FfiBoringssl/dart/digest.dart new file mode 100644 index 00000000000..ecc869bb26c --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/digest.dart @@ -0,0 +1,99 @@ +// 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. + +import 'dart:ffi'; +import 'dart:io'; + +import 'dlopen_helper.dart'; +import 'types.dart'; + +// See: +// https://commondatastorage.googleapis.com/chromium-boringssl-docs/digest.h.html + +DynamicLibrary openSsl() { + // Force load crypto. + dlopenPlatformSpecific("crypto", + path: Platform.script.resolve("native/out/").path); + DynamicLibrary ssl = dlopenPlatformSpecific("ssl", + path: Platform.script.resolve("native/out/").path); + return ssl; +} + +final DynamicLibrary ssl = openSsl(); + +/// The following functions return EVP_MD objects that implement the named +/// hash function. +/// +/// ```c +/// const EVP_MD *EVP_sha512(void); +/// ``` +final Pointer Function() EVP_sha512 = + ssl.lookupFunction Function(), Pointer Function()>( + 'EVP_sha512'); + +/// EVP_MD_CTX_new allocates and initialises a fresh EVP_MD_CTX and returns it, +/// or NULL on allocation failure. The caller must use EVP_MD_CTX_free to +/// release the resulting object. +/// +/// ```c +/// EVP_MD_CTX *EVP_MD_CTX_new(void); +/// ``` +final Pointer Function() EVP_MD_CTX_new = ssl.lookupFunction< + Pointer Function(), + Pointer Function()>('EVP_MD_CTX_new'); + +/// EVP_MD_CTX_free calls EVP_MD_CTX_cleanup and then frees ctx itself. +/// +/// ```c +/// void EVP_MD_CTX_free(EVP_MD_CTX *ctx); +/// ``` +final void Function(Pointer) EVP_MD_CTX_free = ssl.lookupFunction< + Void Function(Pointer), + void Function(Pointer)>('EVP_MD_CTX_free'); + +/// EVP_DigestInit acts like EVP_DigestInit_ex except that ctx is initialised +/// before use. +/// +/// ```c +/// int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type); +/// ``` +final int Function(Pointer, Pointer) EVP_DigestInit = + ssl.lookupFunction, Pointer), + int Function(Pointer, Pointer)>('EVP_DigestInit'); + +/// EVP_DigestUpdate hashes len bytes from data into the hashing operation +/// in ctx. It returns one. +/// +/// ```c +/// int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, +/// size_t len); +/// ``` +final int Function(Pointer, Pointer, int) EVP_DigestUpdate = + ssl.lookupFunction< + Int32 Function(Pointer, Pointer, IntPtr), + int Function( + Pointer, Pointer, int)>('EVP_DigestUpdate'); + +/// EVP_DigestFinal acts like EVP_DigestFinal_ex except that EVP_MD_CTX_cleanup +/// is called on ctx before returning. +/// +/// ```c +/// int EVP_DigestFinal(EVP_MD_CTX *ctx, uint8_t *md_out, +/// unsigned int *out_size); +/// ``` +final int Function(Pointer, Pointer, Pointer) + EVP_DigestFinal = ssl.lookupFunction< + Int32 Function(Pointer, Pointer, Pointer), + int Function(Pointer, Pointer, + Pointer)>('EVP_DigestFinal'); + +/// EVP_MD_CTX_size returns the digest size of ctx, in bytes. It will crash if +/// a digest hasn't been set on ctx. +/// +/// ```c +/// size_t EVP_MD_CTX_size(const EVP_MD_CTX *ctx); +/// ``` +final int Function(Pointer) EVP_MD_CTX_size = ssl.lookupFunction< + IntPtr Function(Pointer), + int Function(Pointer)>('EVP_MD_CTX_size'); diff --git a/benchmarks/FfiBoringssl/dart/dlopen_helper.dart b/benchmarks/FfiBoringssl/dart/dlopen_helper.dart new file mode 100644 index 00000000000..b0310022ecb --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/dlopen_helper.dart @@ -0,0 +1,58 @@ +// 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. + +import 'dart:ffi'; +import 'dart:io'; + +const kArm = "arm"; +const kArm64 = "arm64"; +const kIa32 = "ia32"; +const kX64 = "x64"; + +// https://stackoverflow.com/questions/45125516/possible-values-for-uname-m +final _unames = { + "arm": kArm, + "aarch64_be": kArm64, + "aarch64": kArm64, + "armv8b": kArm64, + "armv8l": kArm64, + "i386": kIa32, + "i686": kIa32, + "x86_64": kX64, +}; + +String _checkRunningMode(String architecture) { + // Check if we're running in 32bit mode. + final int pointerSize = sizeOf(); + if (pointerSize == 4 && architecture == kX64) return kIa32; + if (pointerSize == 4 && architecture == kArm64) return kArm; + + return architecture; +} + +String _architecture() { + final String uname = Process.runSync("uname", ["-m"]).stdout.trim(); + final String architecture = _unames[uname]; + if (architecture == null) + throw Exception("Unrecognized architecture: '$uname'"); + + // Check if we're running in 32bit mode. + return _checkRunningMode(architecture); +} + +String _platformPath(String name, {String path = ""}) { + if (Platform.isMacOS || Platform.isIOS) + return "${path}mac/${_architecture()}/lib$name.dylib"; + + if (Platform.isWindows) + return "${path}win/${_checkRunningMode(kX64)}/$name.dll"; + + // Unknown platforms default to Unix implementation. + return "${path}linux/${_architecture()}/lib$name.so"; +} + +DynamicLibrary dlopenPlatformSpecific(String name, {String path}) { + final String fullPath = _platformPath(name, path: path); + return DynamicLibrary.open(fullPath); +} diff --git a/benchmarks/FfiBoringssl/dart/native/.gitignore b/benchmarks/FfiBoringssl/dart/native/.gitignore new file mode 100644 index 00000000000..44040287a6d --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/native/.gitignore @@ -0,0 +1,7 @@ +# 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. + +build/ +out/ +src/ diff --git a/benchmarks/FfiBoringssl/dart/native/Makefile b/benchmarks/FfiBoringssl/dart/native/Makefile new file mode 100644 index 00000000000..149ded1f6bb --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/native/Makefile @@ -0,0 +1,81 @@ +# 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(37531): Remove this makefile and build with sdk instead when +# benchmark runner gets support for that. + +REVISION=a86c69888b9a416f5249aacb4690a765be064969 + +STRIPARM=arm-linux-gnueabihf-strip +STRIPARM64=aarch64-linux-gnu-strip + +.PHONY: all cipd build/linux/x64 build/linux/ia32 build/linux/arm build/linux/arm64 clean + +all: out/linux/x64/libssl.so out/linux/x64/libcrypto.so out/linux/ia32/libssl.so out/linux/ia32/libcrypto.so out/linux/arm/libssl.so out/linux/arm/libcrypto.so out/linux/arm64/libssl.so out/linux/arm64/libcrypto.so + +cipd: + cipd create -name dart/benchmarks/ffiboringssl -in out -install-mode copy -tag commit:$(REVISION) + +src: + test -e src || git clone https://boringssl.googlesource.com/boringssl src + cd src && git reset --hard $(REVISION) + +build/linux/x64: src + mkdir -p build/linux/x64 && cd build/linux/x64 && cmake -DBUILD_SHARED_LIBS=1 ../../../src && make + +out/linux/x64: + mkdir -p out/linux/x64 + +out/linux/x64/libssl.so: build/linux/x64 out/linux/x64 + cp build/linux/x64/ssl/libssl.so $@ + strip $@ + +out/linux/x64/libcrypto.so: build/linux/x64 out/linux/x64 + cp build/linux/x64/crypto/libcrypto.so $@ + strip $@ + +build/linux/ia32: src + mkdir -p build/linux/ia32 && cd build/linux/ia32 && cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_TOOLCHAIN_FILE=../../../src/util/32-bit-toolchain.cmake ../../../src && make + +out/linux/ia32: + mkdir -p out/linux/ia32 + +out/linux/ia32/libssl.so: build/linux/ia32 out/linux/ia32 + cp build/linux/ia32/ssl/libssl.so $@ + strip $@ + +out/linux/ia32/libcrypto.so: build/linux/ia32 out/linux/ia32 + cp build/linux/ia32/crypto/libcrypto.so $@ + strip $@ + +build/linux/arm: src + mkdir -p build/linux/arm && cd build/linux/arm && cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_TOOLCHAIN_FILE=../../../arm.cmake ../../../src && make + +out/linux/arm: + mkdir -p out/linux/arm + +out/linux/arm/libssl.so: build/linux/arm out/linux/arm + cp build/linux/arm/ssl/libssl.so $@ + $(STRIPARM) $@ + +out/linux/arm/libcrypto.so: build/linux/arm out/linux/arm + cp build/linux/arm/crypto/libcrypto.so $@ + $(STRIPARM) $@ + +build/linux/arm64: src + mkdir -p build/linux/arm64 && cd build/linux/arm64 && cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_TOOLCHAIN_FILE=../../../arm64.cmake ../../../src && make + +out/linux/arm64: + mkdir -p out/linux/arm64 + +out/linux/arm64/libssl.so: build/linux/arm64 out/linux/arm64 + cp build/linux/arm64/ssl/libssl.so $@ + $(STRIPARM64) $@ + +out/linux/arm64/libcrypto.so: build/linux/arm64 out/linux/arm64 + cp build/linux/arm64/crypto/libcrypto.so $@ + $(STRIPARM64) $@ + +clean: + rm -rf build src out diff --git a/benchmarks/FfiBoringssl/dart/native/arm.cmake b/benchmarks/FfiBoringssl/dart/native/arm.cmake new file mode 100644 index 00000000000..7ead7fc1f53 --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/native/arm.cmake @@ -0,0 +1,17 @@ +# 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(37531): Remove this cmake file and build with sdk instead when +# benchmark runner gets support for that. + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR "arm") +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +set(CMAKE_AS_COMPILER arm-linux-gnueabihf-as) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=attributes" CACHE STRING "c++ flags") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=attributes" CACHE STRING "c flags") +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} " CACHE STRING "asm flags") diff --git a/benchmarks/FfiBoringssl/dart/native/arm64.cmake b/benchmarks/FfiBoringssl/dart/native/arm64.cmake new file mode 100644 index 00000000000..771a2a618de --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/native/arm64.cmake @@ -0,0 +1,17 @@ +# 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(37531): Remove this cmake file and build with sdk instead when +# benchmark runner gets support for that. + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR "arm64") +set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) +set(CMAKE_AS_COMPILER aarch64-linux-gnu-as) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=attributes" CACHE STRING "c++ flags") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=attributes" CACHE STRING "c flags") +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} " CACHE STRING "asm flags") diff --git a/benchmarks/FfiBoringssl/dart/types.dart b/benchmarks/FfiBoringssl/dart/types.dart new file mode 100644 index 00000000000..c36c851628d --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/types.dart @@ -0,0 +1,25 @@ +// 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. + +import 'dart:ffi'; + +/// digest algorithm. +class EVP_MD extends Struct {} + +/// digest context. +class EVP_MD_CTX extends Struct {} + +/// Type for `void*` used to represent opaque data. +class Data extends Struct { + static Data fromUint8Pointer(Pointer p) => p.cast().load(); + + Pointer asUint8Pointer() => this.addressOf.cast(); +} + +/// Type for `uint8_t*` used to represent byte data. +class Bytes extends Struct { + static Data fromUint8Pointer(Pointer p) => p.cast().load(); + + Pointer asUint8Pointer() => this.addressOf.cast(); +} diff --git a/benchmarks/FfiCall/dart/FfiCall.dart b/benchmarks/FfiCall/dart/FfiCall.dart new file mode 100644 index 00000000000..d7e31c4b939 --- /dev/null +++ b/benchmarks/FfiCall/dart/FfiCall.dart @@ -0,0 +1,1150 @@ +// 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:benchmark_harness/benchmark_harness.dart'; + +import 'dlopen_helper.dart'; + +// +// Trampoline functions. +// + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("native_functions", + path: Platform.script.resolve("native/out/").path); + +typedef NativeFunction1Uint8 = Uint8 Function(Uint8); +typedef Function1int = int Function(int); +Function1int function1Uint8 = ffiTestFunctions + .lookupFunction("Function1Uint8"); + +typedef NativeFunction1Uint16 = Uint16 Function(Uint16); +Function1int function1Uint16 = ffiTestFunctions + .lookupFunction("Function1Uint16"); + +typedef NativeFunction1Uint32 = Uint32 Function(Uint32); +Function1int function1Uint32 = ffiTestFunctions + .lookupFunction("Function1Uint32"); + +typedef NativeFunction1Uint64 = Uint64 Function(Uint64); +Function1int function1Uint64 = ffiTestFunctions + .lookupFunction("Function1Uint64"); + +typedef NativeFunction1Int8 = Int8 Function(Int8); +Function1int function1Int8 = ffiTestFunctions + .lookupFunction("Function1Int8"); + +typedef NativeFunction1Int16 = Int16 Function(Int16); +Function1int function1Int16 = ffiTestFunctions + .lookupFunction("Function1Int16"); + +typedef NativeFunction1Int32 = Int32 Function(Int32); +Function1int function1Int32 = ffiTestFunctions + .lookupFunction("Function1Int32"); + +typedef NativeFunction2Int32 = Int32 Function(Int32, Int32); +typedef Function2int = int Function(int, int); +Function2int function2Int32 = ffiTestFunctions + .lookupFunction("Function2Int32"); + +typedef NativeFunction4Int32 = Int32 Function(Int32, Int32, Int32, Int32); +typedef Function4int = int Function(int, int, int, int); +Function4int function4Int32 = ffiTestFunctions + .lookupFunction("Function4Int32"); + +typedef NativeFunction10Int32 = Int32 Function( + Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32); +typedef Function10int = int Function( + int, int, int, int, int, int, int, int, int, int); +Function10int function10Int32 = ffiTestFunctions + .lookupFunction("Function10Int32"); + +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 Function20int = int Function(int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int); +Function20int function20Int32 = ffiTestFunctions + .lookupFunction("Function20Int32"); + +typedef NativeFunction1Int64 = Int64 Function(Int64); +Function1int function1Int64 = ffiTestFunctions + .lookupFunction("Function1Int64"); + +typedef NativeFunction2Int64 = Int64 Function(Int64, Int64); +Function2int function2Int64 = ffiTestFunctions + .lookupFunction("Function2Int64"); + +typedef NativeFunction4Int64 = Int64 Function(Int64, Int64, Int64, Int64); +Function4int function4Int64 = ffiTestFunctions + .lookupFunction("Function4Int64"); + +typedef NativeFunction10Int64 = Int64 Function( + Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64); +Function10int function10Int64 = ffiTestFunctions + .lookupFunction("Function10Int64"); + +typedef NativeFunction20Int64 = Int64 Function( + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64, + Int64); +Function20int function20Int64 = ffiTestFunctions + .lookupFunction("Function20Int64"); + +typedef NativeFunction1Float = Float Function(Float); +typedef Function1double = double Function(double); +Function1double function1Float = ffiTestFunctions + .lookupFunction("Function1Float"); + +typedef NativeFunction2Float = Float Function(Float, Float); +typedef Function2double = double Function(double, double); +Function2double function2Float = ffiTestFunctions + .lookupFunction("Function2Float"); + +typedef NativeFunction4Float = Float Function(Float, Float, Float, Float); +typedef Function4double = double Function(double, double, double, double); +Function4double function4Float = ffiTestFunctions + .lookupFunction("Function4Float"); + +typedef NativeFunction10Float = Float Function( + Float, Float, Float, Float, Float, Float, Float, Float, Float, Float); +typedef Function10double = double Function(double, double, double, double, + double, double, double, double, double, double); +Function10double function10Float = ffiTestFunctions + .lookupFunction("Function10Float"); + +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 Function20double = double Function( + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double, + double); +Function20double function20Float = ffiTestFunctions + .lookupFunction("Function20Float"); + +typedef NativeFunction1Double = Double Function(Double); +Function1double function1Double = ffiTestFunctions + .lookupFunction("Function1Double"); + +typedef NativeFunction2Double = Double Function(Double, Double); +Function2double function2Double = ffiTestFunctions + .lookupFunction("Function1Double"); + +typedef NativeFunction4Double = Double Function(Double, Double, Double, Double); +Function4double function4Double = ffiTestFunctions + .lookupFunction("Function1Double"); + +typedef NativeFunction10Double = Double Function(Double, Double, Double, Double, + Double, Double, Double, Double, Double, Double); +Function10double function10Double = + ffiTestFunctions.lookupFunction( + "Function10Double"); + +typedef NativeFunction20Double = Double Function( + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double); +Function20double function20Double = + ffiTestFunctions.lookupFunction( + "Function20Double"); + +typedef Function1PointerUint8 = Pointer Function(Pointer); +Function1PointerUint8 function1PointerUint8 = ffiTestFunctions.lookupFunction< + Function1PointerUint8, Function1PointerUint8>("Function1PointerUint8"); + +typedef Function2PointerUint8 = Pointer Function( + Pointer, Pointer); +Function2PointerUint8 function2PointerUint8 = ffiTestFunctions.lookupFunction< + Function2PointerUint8, Function2PointerUint8>("Function2PointerUint8"); + +typedef Function4PointerUint8 = Pointer Function( + Pointer, Pointer, Pointer, Pointer); +Function4PointerUint8 function4PointerUint8 = ffiTestFunctions.lookupFunction< + Function4PointerUint8, Function4PointerUint8>("Function4PointerUint8"); + +typedef Function10PointerUint8 = Pointer Function( + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer); +Function10PointerUint8 function10PointerUint8 = ffiTestFunctions.lookupFunction< + Function10PointerUint8, Function10PointerUint8>("Function10PointerUint8"); + +typedef Function20PointerUint8 = Pointer Function( + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer, + Pointer); +Function20PointerUint8 function20PointerUint8 = ffiTestFunctions.lookupFunction< + Function20PointerUint8, Function20PointerUint8>("Function20PointerUint8"); + +// +// Trampoline call. +// + +int doCall1Uint8(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Uint8(17); + } + return x; +} + +int doCall1Uint16(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Uint16(17); + } + return x; +} + +int doCall1Uint32(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Uint32(i); + } + return x; +} + +int doCall1Uint64(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Uint64(i); + } + return x; +} + +int doCall1Int8(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Int8(17); + } + return x; +} + +int doCall1Int16(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Int16(17); + } + return x; +} + +int doCall1Int32(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Int32(i); + } + return x; +} + +int doCall2Int32(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function2Int32(i, i); + } + return x; +} + +int doCall4Int32(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function4Int32(i, i, i, i); + } + return x; +} + +int doCall10Int32(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function10Int32(i, i, i, i, i, i, i, i, i, i); + } + return x; +} + +int doCall20Int32(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function20Int32( + i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); + } + return x; +} + +int doCall1Int64(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function1Int64(i); + } + return x; +} + +int doCall2Int64(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function2Int64(i, i); + } + return x; +} + +int doCall4Int64(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function4Int64(i, i, i, i); + } + return x; +} + +int doCall10Int64(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function10Int64(i, i, i, i, i, i, i, i, i, i); + } + return x; +} + +int doCall20Int64(int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += function20Int64( + i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); + } + return x; +} + +int doCall1Int64Mint(int length) { + int x = 0x7FFFFFFF00000000; + + for (int i = 0; i < length; i++) { + x = function1Int64(x); + } + return x; +} + +double doCall1Float(int length) { + double x = 0.0; + for (int i = 0; i < length; i++) { + x += function1Float(17.0); + } + return x; +} + +double doCall2Float(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function2Float(1.0, 2.0); + } + return x; +} + +double doCall4Float(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function4Float(1.0, 2.0, 3.0, 4.0); + } + return x; +} + +double doCall10Float(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function10Float(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); + } + return x; +} + +double doCall20Float(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function20Float(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); + } + return x; +} + +double doCall1Double(int length) { + double x = 0.0; + for (int i = 0; i < length; i++) { + x += function1Double(17.0); + } + return x; +} + +double doCall2Double(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function2Double(1.0, 2.0); + } + return x; +} + +double doCall4Double(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function4Double(1.0, 2.0, 3.0, 4.0); + } + return x; +} + +double doCall10Double(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function10Double(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); + } + return x; +} + +double doCall20Double(int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += function20Double(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); + } + return x; +} + +Pointer doCall1PointerUint8(int length, Pointer p1) { + Pointer x = p1; + for (int i = 0; i < length; i++) { + x = function1PointerUint8(x); + } + return x; +} + +Pointer doCall2PointerUint8( + int length, Pointer p1, Pointer p2) { + Pointer x = p1; + for (int i = 0; i < length; i++) { + x = function2PointerUint8(x, p2); + } + return x; +} + +Pointer doCall4PointerUint8(int length, Pointer p1, + Pointer p2, Pointer p3, Pointer p4) { + Pointer x = p1; + for (int i = 0; i < length; i++) { + x = function4PointerUint8(x, p2, p3, p4); + } + return x; +} + +Pointer doCall10PointerUint8( + int length, + Pointer p1, + Pointer p2, + Pointer p3, + Pointer p4, + Pointer p5, + Pointer p6, + Pointer p7, + Pointer p8, + Pointer p9, + Pointer p10) { + Pointer x = p1; + for (int i = 0; i < length; i++) { + x = function10PointerUint8(x, p2, p3, p4, p5, p6, p7, p8, p9, p10); + } + return x; +} + +Pointer doCall20PointerUint8( + int length, + Pointer p1, + Pointer p2, + Pointer p3, + Pointer p4, + Pointer p5, + Pointer p6, + Pointer p7, + Pointer p8, + Pointer p9, + Pointer p10, + Pointer p11, + Pointer p12, + Pointer p13, + Pointer p14, + Pointer p15, + Pointer p16, + Pointer p17, + Pointer p18, + Pointer p19, + Pointer p20) { + Pointer x = p1; + for (int i = 0; i < length; i++) { + x = function20PointerUint8(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, + p13, p14, p15, p16, p17, p18, p19, p20); + } + return x; +} + +// +// Benchmark fixtures. +// + +// Number of repeats: 1000 +// * CPU: Intel(R) Xeon(R) Gold 6154 +// * Architecture: x64 +// * 200 - 1100 us +// * Architecture: SimDBC64 +// * 2800 - 110000 us +const N = 1000; + +class Uint8x01 extends BenchmarkBase { + Uint8x01() : super("FfiCall.Uint8x01"); + + void run() { + final int x = doCall1Uint8(N); + if (x != N * 17 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Uint16x01 extends BenchmarkBase { + Uint16x01() : super("FfiCall.Uint16x01"); + + void run() { + final int x = doCall1Uint16(N); + if (x != N * 17 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Uint32x01 extends BenchmarkBase { + Uint32x01() : super("FfiCall.Uint32x01"); + + void run() { + final int x = doCall1Uint32(N); + if (x != N * (N - 1) / 2 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Uint64x01 extends BenchmarkBase { + Uint64x01() : super("FfiCall.Uint64x01"); + + void run() { + final int x = doCall1Uint64(N); + if (x != N * (N - 1) / 2 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int8x01 extends BenchmarkBase { + Int8x01() : super("FfiCall.Int8x01"); + + void run() { + final int x = doCall1Int8(N); + if (x != N * 17 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int16x01 extends BenchmarkBase { + Int16x01() : super("FfiCall.Int16x01"); + + void run() { + final int x = doCall1Int16(N); + if (x != N * 17 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int32x01 extends BenchmarkBase { + Int32x01() : super("FfiCall.Int32x01"); + + void run() { + final int x = doCall1Int32(N); + if (x != N * (N - 1) / 2 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int32x02 extends BenchmarkBase { + Int32x02() : super("FfiCall.Int32x02"); + + void run() { + final int x = doCall2Int32(N); + if (x != N * (N - 1) * 2 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int32x04 extends BenchmarkBase { + Int32x04() : super("FfiCall.Int32x04"); + + void run() { + final int x = doCall4Int32(N); + if (x != N * (N - 1) * 4 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int32x10 extends BenchmarkBase { + Int32x10() : super("FfiCall.Int32x10"); + + void run() { + final int x = doCall10Int32(N); + if (x != N * (N - 1) * 10 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int32x20 extends BenchmarkBase { + Int32x20() : super("FfiCall.Int32x20"); + + void run() { + final int x = doCall20Int32(N); + if (x != N * (N - 1) * 20 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int64x01 extends BenchmarkBase { + Int64x01() : super("FfiCall.Int64x01"); + + void run() { + final int x = doCall1Int64(N); + if (x != N * (N - 1) / 2 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int64x02 extends BenchmarkBase { + Int64x02() : super("FfiCall.Int64x02"); + + void run() { + final int x = doCall2Int64(N); + if (x != N * (N - 1) * 2 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int64x04 extends BenchmarkBase { + Int64x04() : super("FfiCall.Int64x04"); + + void run() { + final int x = doCall4Int64(N); + if (x != N * (N - 1) * 4 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int64x10 extends BenchmarkBase { + Int64x10() : super("FfiCall.Int64x10"); + + void run() { + final int x = doCall10Int64(N); + if (x != N * (N - 1) * 10 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int64x20 extends BenchmarkBase { + Int64x20() : super("FfiCall.Int64x20"); + + void run() { + final int x = doCall20Int64(N); + if (x != N * (N - 1) * 20 / 2) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Int64Mintx01 extends BenchmarkBase { + Int64Mintx01() : super("FfiCall.Int64Mintx01"); + + void run() { + final int x = doCall1Int64Mint(N); + if (x != 0x7FFFFFFF00000000 + N * 42) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Floatx01 extends BenchmarkBase { + Floatx01() : super("FfiCall.Floatx01"); + + void run() { + final double x = doCall1Float(N); + final double expected = N * (N - 1) / 2 + N * 42; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Floatx02 extends BenchmarkBase { + Floatx02() : super("FfiCall.Floatx02"); + + void run() { + final double x = doCall2Float(N); + final double expected = N * 55.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Floatx04 extends BenchmarkBase { + Floatx04() : super("FfiCall.Floatx04"); + + void run() { + final double x = doCall4Float(N); + final double expected = N * 55.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Floatx10 extends BenchmarkBase { + Floatx10() : super("FfiCall.Floatx10"); + + void run() { + final double x = doCall10Float(N); + final double expected = N * 55.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Floatx20 extends BenchmarkBase { + Floatx20() : super("FfiCall.Floatx20"); + + void run() { + final double x = doCall20Float(N); + final double expected = N * 220.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Doublex01 extends BenchmarkBase { + Doublex01() : super("FfiCall.Doublex01"); + + void run() { + final double x = doCall1Double(N); + final double expected = N * (N - 1) / 2 + N * 42; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Doublex02 extends BenchmarkBase { + Doublex02() : super("FfiCall.Doublex02"); + + void run() { + final double x = doCall2Double(N); + final double expected = N * 55.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Doublex04 extends BenchmarkBase { + Doublex04() : super("FfiCall.Doublex04"); + + void run() { + final double x = doCall4Double(N); + final double expected = N * 55.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Doublex10 extends BenchmarkBase { + Doublex10() : super("FfiCall.Doublex10"); + + void run() { + final double x = doCall10Double(N); + final double expected = N * 55.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class Doublex20 extends BenchmarkBase { + Doublex20() : super("FfiCall.Doublex20"); + + void run() { + final double x = doCall20Double(N); + final double expected = N * 220.0; + if (0.999 * expected > x && x > 1.001 * expected) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint8x01 extends BenchmarkBase { + PointerUint8x01() : super("FfiCall.PointerUint8x01"); + + Pointer pointer; + void setup() => pointer = Pointer.allocate(count: N + 1); + void teardown() => pointer.free(); + + void run() { + final Pointer x = doCall1PointerUint8(N, pointer); + if (x.address != pointer.address + N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint8x02 extends BenchmarkBase { + PointerUint8x02() : super("FfiCall.PointerUint8x02"); + + Pointer pointer, pointer2; + + void setup() { + pointer = Pointer.allocate(count: N + 1); + pointer2 = pointer.elementAt(1); + } + + void teardown() { + pointer.free(); + } + + void run() { + final Pointer x = doCall2PointerUint8(N, pointer, pointer2); + if (x.address != pointer.address + N * sizeOf()) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint8x04 extends BenchmarkBase { + PointerUint8x04() : super("FfiCall.PointerUint8x04"); + + Pointer pointer, pointer2, pointer3, pointer4; + + void setup() { + pointer = Pointer.allocate(count: N + 1); + pointer2 = pointer.elementAt(1); + pointer3 = pointer.elementAt(2); + pointer4 = pointer.elementAt(3); + } + + void teardown() { + pointer.free(); + } + + void run() { + final Pointer x = + doCall4PointerUint8(N, pointer, pointer2, pointer3, pointer4); + if (x.address != pointer.address + N * sizeOf()) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint8x10 extends BenchmarkBase { + PointerUint8x10() : super("FfiCall.PointerUint8x10"); + + Pointer pointer, + pointer2, + pointer3, + pointer4, + pointer5, + pointer6, + pointer7, + pointer8, + pointer9, + pointer10; + + void setup() { + pointer = Pointer.allocate(count: N + 1); + pointer2 = pointer.elementAt(1); + pointer3 = pointer.elementAt(2); + pointer4 = pointer.elementAt(3); + pointer5 = pointer.elementAt(4); + pointer6 = pointer.elementAt(5); + pointer7 = pointer.elementAt(6); + pointer8 = pointer.elementAt(7); + pointer9 = pointer.elementAt(8); + pointer10 = pointer.elementAt(9); + } + + void teardown() { + pointer.free(); + } + + void run() { + final Pointer x = doCall10PointerUint8( + N, + pointer, + pointer2, + pointer3, + pointer4, + pointer5, + pointer6, + pointer7, + pointer8, + pointer9, + pointer10); + if (x.address != pointer.address + N * sizeOf()) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint8x20 extends BenchmarkBase { + PointerUint8x20() : super("FfiCall.PointerUint8x20"); + + Pointer pointer, + pointer2, + pointer3, + pointer4, + pointer5, + pointer6, + pointer7, + pointer8, + pointer9, + pointer10, + pointer11, + pointer12, + pointer13, + pointer14, + pointer15, + pointer16, + pointer17, + pointer18, + pointer19, + pointer20; + + void setup() { + pointer = Pointer.allocate(count: N + 1); + pointer2 = pointer.elementAt(1); + pointer3 = pointer.elementAt(2); + pointer4 = pointer.elementAt(3); + pointer5 = pointer.elementAt(4); + pointer6 = pointer.elementAt(5); + pointer7 = pointer.elementAt(6); + pointer8 = pointer.elementAt(7); + pointer9 = pointer.elementAt(8); + pointer10 = pointer.elementAt(9); + pointer11 = pointer.elementAt(10); + pointer12 = pointer.elementAt(11); + pointer13 = pointer.elementAt(12); + pointer14 = pointer.elementAt(13); + pointer15 = pointer.elementAt(14); + pointer16 = pointer.elementAt(15); + pointer17 = pointer.elementAt(16); + pointer18 = pointer.elementAt(17); + pointer19 = pointer.elementAt(18); + pointer20 = pointer.elementAt(19); + } + + void teardown() { + pointer.free(); + } + + void run() { + final Pointer x = doCall20PointerUint8( + N, + pointer, + pointer2, + pointer3, + pointer4, + pointer5, + pointer6, + pointer7, + pointer8, + pointer9, + pointer10, + pointer11, + pointer12, + pointer13, + pointer14, + pointer15, + pointer16, + pointer17, + pointer18, + pointer19, + pointer20); + if (x.address != pointer.address + N * sizeOf()) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +// +// Main driver. +// + +main() { + final benchmarks = [ + () => Uint8x01(), + () => Uint16x01(), + () => Uint32x01(), + () => Uint64x01(), + () => Int8x01(), + () => Int16x01(), + () => Int32x01(), + () => Int32x02(), + () => Int32x04(), + () => Int32x10(), + () => Int32x20(), + () => Int64x01(), + () => Int64x02(), + () => Int64x04(), + () => Int64x10(), + () => Int64x20(), + () => Int64Mintx01(), + () => Floatx01(), + () => Floatx02(), + () => Floatx04(), + () => Floatx10(), + () => Floatx20(), + () => Doublex01(), + () => Doublex02(), + () => Doublex04(), + () => Doublex10(), + () => Doublex20(), + () => PointerUint8x01(), + () => PointerUint8x02(), + () => PointerUint8x04(), + () => PointerUint8x10(), + () => PointerUint8x20(), + ]; + benchmarks.forEach((benchmark) => benchmark().report()); +} diff --git a/benchmarks/FfiCall/dart/dlopen_helper.dart b/benchmarks/FfiCall/dart/dlopen_helper.dart new file mode 100644 index 00000000000..b0310022ecb --- /dev/null +++ b/benchmarks/FfiCall/dart/dlopen_helper.dart @@ -0,0 +1,58 @@ +// 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. + +import 'dart:ffi'; +import 'dart:io'; + +const kArm = "arm"; +const kArm64 = "arm64"; +const kIa32 = "ia32"; +const kX64 = "x64"; + +// https://stackoverflow.com/questions/45125516/possible-values-for-uname-m +final _unames = { + "arm": kArm, + "aarch64_be": kArm64, + "aarch64": kArm64, + "armv8b": kArm64, + "armv8l": kArm64, + "i386": kIa32, + "i686": kIa32, + "x86_64": kX64, +}; + +String _checkRunningMode(String architecture) { + // Check if we're running in 32bit mode. + final int pointerSize = sizeOf(); + if (pointerSize == 4 && architecture == kX64) return kIa32; + if (pointerSize == 4 && architecture == kArm64) return kArm; + + return architecture; +} + +String _architecture() { + final String uname = Process.runSync("uname", ["-m"]).stdout.trim(); + final String architecture = _unames[uname]; + if (architecture == null) + throw Exception("Unrecognized architecture: '$uname'"); + + // Check if we're running in 32bit mode. + return _checkRunningMode(architecture); +} + +String _platformPath(String name, {String path = ""}) { + if (Platform.isMacOS || Platform.isIOS) + return "${path}mac/${_architecture()}/lib$name.dylib"; + + if (Platform.isWindows) + return "${path}win/${_checkRunningMode(kX64)}/$name.dll"; + + // Unknown platforms default to Unix implementation. + return "${path}linux/${_architecture()}/lib$name.so"; +} + +DynamicLibrary dlopenPlatformSpecific(String name, {String path}) { + final String fullPath = _platformPath(name, path: path); + return DynamicLibrary.open(fullPath); +} diff --git a/benchmarks/FfiCall/dart/native/.gitignore b/benchmarks/FfiCall/dart/native/.gitignore new file mode 100644 index 00000000000..4baa5ba98e9 --- /dev/null +++ b/benchmarks/FfiCall/dart/native/.gitignore @@ -0,0 +1,5 @@ +# 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. + +out/ diff --git a/benchmarks/FfiCall/dart/native/Makefile b/benchmarks/FfiCall/dart/native/Makefile new file mode 100644 index 00000000000..5e5d716e836 --- /dev/null +++ b/benchmarks/FfiCall/dart/native/Makefile @@ -0,0 +1,60 @@ +# 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(37531): Remove this makefile and build with sdk instead when +# benchmark runner gets support for that. + +CC=gcc +CCARM=arm-linux-gnueabihf-gcc +CCARM64=aarch64-linux-gnu-gcc +CFLAGS=-Wall -g -O -fPIC + +# Bump this whenever the benchmark is updated. +VERSION=1 + +.PHONY: all clean + +all: out/linux/x64/libnative_functions.so out/linux/ia32/libnative_functions.so out/linux/arm64/libnative_functions.so out/linux/arm/libnative_functions.so + +cipd: + cipd create -name dart/benchmarks/fficall -in out -install-mode copy -tag version:$(VERSION) + +clean: + rm -rf *.o *.so out + +out/linux/x64: + mkdir -p out/linux/x64 + +out/linux/x64/native_functions.o: native_functions.c | out/linux/x64 + $(CC) $(CFLAGS) -c -o $@ native_functions.c + +out/linux/x64/libnative_functions.so: out/linux/x64/native_functions.o + $(CC) $(CFLAGS) -s -shared -o $@ out/linux/x64/native_functions.o + +out/linux/ia32: + mkdir -p out/linux/ia32 + +out/linux/ia32/native_functions.o: native_functions.c | out/linux/ia32 + $(CC) $(CFLAGS) -m32 -c -o $@ native_functions.c + +out/linux/ia32/libnative_functions.so: out/linux/ia32/native_functions.o + $(CC) $(CFLAGS) -m32 -s -shared -o $@ out/linux/ia32/native_functions.o + +out/linux/arm64: + mkdir -p out/linux/arm64 + +out/linux/arm64/native_functions.o: native_functions.c | out/linux/arm64 + $(CCARM64) $(CFLAGS) -c -o $@ native_functions.c + +out/linux/arm64/libnative_functions.so: out/linux/arm64/native_functions.o + $(CCARM64) $(CFLAGS) -s -shared -o $@ out/linux/arm64/native_functions.o + +out/linux/arm: + mkdir -p out/linux/arm + +out/linux/arm/native_functions.o: native_functions.c | out/linux/arm + $(CCARM) $(CFLAGS) -c -o $@ native_functions.c + +out/linux/arm/libnative_functions.so: out/linux/arm/native_functions.o + $(CCARM) $(CFLAGS) -s -shared -o $@ out/linux/arm/native_functions.o diff --git a/benchmarks/FfiCall/dart/native/native_functions.c b/benchmarks/FfiCall/dart/native/native_functions.c new file mode 100644 index 00000000000..9fcee4d357c --- /dev/null +++ b/benchmarks/FfiCall/dart/native/native_functions.c @@ -0,0 +1,132 @@ +// 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. + +#include + +uint8_t Function1Uint8(uint8_t x) { return x + 42; } + +uint16_t Function1Uint16(uint16_t x) { return x + 42; } + +uint32_t Function1Uint32(uint32_t x) { return x + 42; } + +uint64_t Function1Uint64(uint64_t x) { return x + 42; } + +int8_t Function1Int8(int8_t x) { return x + 42; } + +int16_t Function1Int16(int16_t x) { return x + 42; } + +int32_t Function1Int32(int32_t x) { return x + 42; } + +int32_t Function2Int32(int32_t a, int32_t b) { + return a + b; +} + +int32_t Function4Int32(int32_t a, int32_t b, int32_t c, int32_t d) { + return a + b + c + d; +} + +int32_t Function10Int32(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, + int32_t f, int32_t g, int32_t h, int32_t i, int32_t j) { + return a + b + c + d + e + f + g + h + i + j; +} + +int32_t Function20Int32(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, + int32_t f, int32_t g, int32_t h, int32_t i, int32_t j, + int32_t k, int32_t l, int32_t m, int32_t n, int32_t o, + int32_t p, int32_t q, int32_t r, int32_t s, int32_t t) { + return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + + p + q + r + s + t; +} + +int64_t Function1Int64(int64_t x) { return x + 42; } + +int64_t Function2Int64(int64_t a, int64_t b) { + return a + b; +} + +int64_t Function4Int64(int64_t a, int64_t b, int64_t c, int64_t d) { + return a + b + c + d; +} + +int64_t Function10Int64(int64_t a, int64_t b, int64_t c, int64_t d, int64_t e, + int64_t f, int64_t g, int64_t h, int64_t i, int64_t j) { + return a + b + c + d + e + f + g + h + i + j; +} + +int64_t Function20Int64(int64_t a, int64_t b, int64_t c, int64_t d, int64_t e, + int64_t f, int64_t g, int64_t h, int64_t i, int64_t j, + int64_t k, int64_t l, int64_t m, int64_t n, int64_t o, + int64_t p, int64_t q, int64_t r, int64_t s, int64_t t) { + return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + + p + q + r + s + t; +} + +float Function1Float(float x) { return x + 42.0f; } + +float Function2Float(float a, float b) { + return a + b; +} + +float Function4Float(float a, float b, float c, float d) { + return a + b + c + d; +} + +float Function10Float(float a, float b, float c, float d, float e, float f, + float g, float h, float i, float j) { + return a + b + c + d + e + f + g + h + i + j; +} + +float Function20Float(float a, float b, float c, float d, float e, float f, + float g, float h, float i, float j, float k, float l, + float m, float n, float o, float p, float q, float r, + float s, float t) { + return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + + q + r + s + t; +} + +double Function1Double(double x) { return x + 42.0; } + +double Function2Double(double a, double b) { + return a + b; +} + +double Function4Double(double a, double b, double c, double d) { + return a + b + c + d; +} + +double Function10Double(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j) { + return a + b + c + d + e + f + g + h + i + j; +} + +double Function20Double(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, + double k, double l, double m, double n, double o, + double p, double q, double r, double s, double t) { + return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + + p + q + r + s + t; +} + +uint8_t *Function1PointerUint8(uint8_t *a) { return a + 1; } + +uint8_t *Function2PointerUint8(uint8_t *a, uint8_t *b) { return a + 1; } + +uint8_t *Function4PointerUint8(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d) { + return a + 1; +} + +uint8_t *Function10PointerUint8(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d, + uint8_t *e, uint8_t *f, uint8_t *g, uint8_t *h, + uint8_t *i, uint8_t *j) { + return a + 1; +} + +uint8_t *Function20PointerUint8(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d, + uint8_t *e, uint8_t *f, uint8_t *g, uint8_t *h, + uint8_t *i, uint8_t *j, uint8_t *k, uint8_t *l, + uint8_t *m, uint8_t *n, uint8_t *o, uint8_t *p, + uint8_t *q, uint8_t *r, uint8_t *s, + uint8_t *t) { + return a + 1; +} diff --git a/benchmarks/FfiMemory/dart/FfiMemory.dart b/benchmarks/FfiMemory/dart/FfiMemory.dart new file mode 100644 index 00000000000..39c053f73dc --- /dev/null +++ b/benchmarks/FfiMemory/dart/FfiMemory.dart @@ -0,0 +1,431 @@ +// 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. + +// Micro-benchmarks for ffi memory stores and loads. +// +// 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 'package:benchmark_harness/benchmark_harness.dart'; + +// +// Pointer store. +// + +void doStoreInt8(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreUint8(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreInt16(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreUint16(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreInt32(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreUint32(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreInt64(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreUint64(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1); + } +} + +void doStoreFloat(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1.0); + } +} + +void doStoreDouble(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(1.0); + } +} + +void doStorePointer( + Pointer> pointer, int length, Pointer data) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(data); + } +} + +void doStoreInt64Mint(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).store(0x7FFFFFFFFFFFFFFF); + } +} + +// +// Pointer load. +// + +int doLoadInt8(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadUint8(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadInt16(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadUint16(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadInt32(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadUint32(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadInt64(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +int doLoadUint64(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +double doLoadFloat(Pointer pointer, int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +double doLoadDouble(Pointer pointer, int length) { + double x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +// Aggregates pointers through aggregrating their addresses. +int doLoadPointer(Pointer> pointer, int length) { + Pointer x; + int address_xor = 0; + for (int i = 0; i < length; i++) { + x = pointer.elementAt(i).load(); + address_xor ^= x.address; + } + return address_xor; +} + +int doLoadInt64Mint(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load(); + } + return x; +} + +// +// Benchmark fixtures. +// + +// Number of repeats: 1000 +// * CPU: Intel(R) Xeon(R) Gold 6154 +// * Architecture: x64 +// * 48000 - 125000 us (without optimizations) +// * 14 - ??? us (expected with optimizations, on par with typed data) +// * Architecture: SimDBC64 +// * 52000 - 130000 us (without optimizations) +// * 300 - ??? us (expected with optimizations, on par with typed data) +const N = 1000; + +class PointerInt8 extends BenchmarkBase { + Pointer pointer; + PointerInt8() : super("FfiMemory.PointerInt8"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreInt8(pointer, N); + final int x = doLoadInt8(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint8 extends BenchmarkBase { + Pointer pointer; + PointerUint8() : super("FfiMemory.PointerUint8"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreUint8(pointer, N); + final int x = doLoadUint8(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerInt16 extends BenchmarkBase { + Pointer pointer; + PointerInt16() : super("FfiMemory.PointerInt16"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreInt16(pointer, N); + final int x = doLoadInt16(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint16 extends BenchmarkBase { + Pointer pointer; + PointerUint16() : super("FfiMemory.PointerUint16"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreUint16(pointer, N); + final int x = doLoadUint16(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerInt32 extends BenchmarkBase { + Pointer pointer; + PointerInt32() : super("FfiMemory.PointerInt32"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreInt32(pointer, N); + final int x = doLoadInt32(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint32 extends BenchmarkBase { + Pointer pointer; + PointerUint32() : super("FfiMemory.PointerUint32"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreUint32(pointer, N); + final int x = doLoadUint32(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerInt64 extends BenchmarkBase { + Pointer pointer; + PointerInt64() : super("FfiMemory.PointerInt64"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreInt64(pointer, N); + final int x = doLoadInt64(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerUint64 extends BenchmarkBase { + Pointer pointer; + PointerUint64() : super("FfiMemory.PointerUint64"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreUint64(pointer, N); + final int x = doLoadUint64(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerFloat extends BenchmarkBase { + Pointer pointer; + PointerFloat() : super("FfiMemory.PointerFloat"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreFloat(pointer, N); + final double x = doLoadFloat(pointer, N); + if (0.99 * N > x || x > 1.01 * N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerDouble extends BenchmarkBase { + Pointer pointer; + PointerDouble() : super("FfiMemory.PointerDouble"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreDouble(pointer, N); + final double x = doLoadDouble(pointer, N); + if (0.99 * N > x || x > 1.01 * N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerPointer extends BenchmarkBase { + Pointer> pointer; + Pointer data; + PointerPointer() : super("FfiMemory.PointerPointer"); + + void setup() { + pointer = Pointer.allocate(count: N); + data = Pointer.allocate(); + } + + void teardown() { + pointer.free(); + data.free(); + } + + void run() { + doStorePointer(pointer, N, data); + final int x = doLoadPointer(pointer, N); + if (x != 0 || x == data.address) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +class PointerInt64Mint extends BenchmarkBase { + Pointer pointer; + PointerInt64Mint() : super("FfiMemory.PointerInt64Mint"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreInt64Mint(pointer, N); + final int x = doLoadInt64Mint(pointer, N); + // Using overflow semantics in aggregation. + if (x != -N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +// +// Main driver. +// + +main() { + final benchmarks = [ + () => PointerInt8(), + () => PointerUint8(), + () => PointerInt16(), + () => PointerUint16(), + () => PointerInt32(), + () => PointerUint32(), + () => PointerInt64(), + () => PointerInt64Mint(), + () => PointerUint64(), + () => PointerFloat(), + () => PointerDouble(), + () => PointerPointer(), + ]; + benchmarks.forEach((benchmark) => benchmark().report()); +} diff --git a/benchmarks/FfiStruct/dart/FfiStruct.dart b/benchmarks/FfiStruct/dart/FfiStruct.dart new file mode 100644 index 00000000000..ede799b26fa --- /dev/null +++ b/benchmarks/FfiStruct/dart/FfiStruct.dart @@ -0,0 +1,120 @@ +// 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. + +// Micro-benchmark for ffi struct field stores and loads. +// +// Only tests a single field because the FfiMemory benchmark already tests loads +// and stores of different field sizes. + +import 'dart:ffi'; + +import 'package:benchmark_harness/benchmark_harness.dart'; + +// +// Struct field store (plus Pointer elementAt and load). +// + +void doStoreInt32(Pointer pointer, int length) { + for (int i = 0; i < length; i++) { + pointer.elementAt(i).load().c = 1; + } +} + +// +// Struct field load (plus Pointer elementAt and load). +// + +int doLoadInt32(Pointer pointer, int length) { + int x = 0; + for (int i = 0; i < length; i++) { + x += pointer.elementAt(i).load().c; + } + return x; +} + +// +// Benchmark fixture. +// + +// Number of repeats: 1000 +// * CPU: Intel(R) Xeon(R) Gold 6154 +// * Architecture: x64 +// * 150000 - 465000 us (without optimizations) +// * 14 - ??? us (expected with optimizations, on par with typed data) +const N = 1000; + +class FieldLoadStore extends BenchmarkBase { + Pointer pointer; + FieldLoadStore() : super("FfiStruct.FieldLoadStore"); + + void setup() => pointer = Pointer.allocate(count: N); + void teardown() => pointer.free(); + + void run() { + doStoreInt32(pointer, N); + final int x = doLoadInt32(pointer, N); + if (x != N) { + throw Exception("$name: Unexpected result: $x"); + } + } +} + +// +// Main driver. +// + +main() { + final benchmarks = [ + () => FieldLoadStore(), + ]; + benchmarks.forEach((benchmark) => benchmark().report()); +} + +// +// Test struct. +// +class VeryLargeStruct extends Struct { + @Int8() + int a; + + @Int16() + int b; + + @Int32() + int c; + + @Int64() + int d; + + @Uint8() + int e; + + @Uint16() + int f; + + @Uint32() + int g; + + @Uint64() + int h; + + @IntPtr() + int i; + + @Double() + double j; + + @Float() + double k; + + Pointer parent; + + @IntPtr() + int numChildren; + + Pointer children; + + @Int8() + int smallLastField; +} diff --git a/tests/ffi/function_structs_test.dart b/tests/ffi/function_structs_test.dart index 875f621847d..bf41a08529d 100644 --- a/tests/ffi/function_structs_test.dart +++ b/tests/ffi/function_structs_test.dart @@ -105,11 +105,11 @@ void testFunctionWithVeryLargeStruct() { struct.smallLastField = 1; } vls1.parent = vls2.addressOf; - vls1.numChidlren = 2; + vls1.numChildren = 2; vls1.children = vls1.addressOf; vls2.parent = vls2.addressOf; vls2.parent = ffi.nullptr.cast(); - vls2.numChidlren = 0; + vls2.numChildren = 0; vls2.children = ffi.nullptr.cast(); int result = f(vls1.addressOf); diff --git a/tests/ffi/very_large_struct.dart b/tests/ffi/very_large_struct.dart index c7bb261b353..36202f64525 100644 --- a/tests/ffi/very_large_struct.dart +++ b/tests/ffi/very_large_struct.dart @@ -2,8 +2,6 @@ // 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. -library FfiTestCoordinateBare; - import 'dart:ffi'; /// Large sample struct for dart:ffi library. @@ -44,7 +42,7 @@ class VeryLargeStruct extends Struct { Pointer parent; @IntPtr() - int numChidlren; + int numChildren; Pointer children; diff --git a/tools/bots/try_benchmarks.sh b/tools/bots/try_benchmarks.sh index c463b12b7cc..e30eeb2b3d0 100755 --- a/tools/bots/try_benchmarks.sh +++ b/tools/bots/try_benchmarks.sh @@ -222,6 +222,10 @@ EOF out/ReleaseIA32/run_vm_tests GenKernelKernelLoadKernel out/ReleaseIA32/run_vm_tests KernelServiceCompileAll out/ReleaseIA32/dart --profile-period=10000 --packages=.packages benchmarks/Example/dart/Example.dart + out/ReleaseIA32/dart benchmarks/FfiBoringssl/dart/FfiBoringssl.dart + out/ReleaseIA32/dart benchmarks/FfiCall/dart/FfiCall.dart + out/ReleaseIA32/dart benchmarks/FfiMemory/dart/FfiMemory.dart + out/ReleaseIA32/dart benchmarks/FfiStruct/dart/FfiStruct.dart cd .. rm -rf tmp elif [ "$command" = linux-x64-build ] ||