[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 <dacoharkes@google.com> Reviewed-by: Jonas Termansen <sortie@google.com> Auto-Submit: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
committed by
Jonas Termansen
parent
c1bb024479
commit
fbf13f561f
@@ -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 = {
|
||||
|
||||
@@ -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<int>();
|
||||
}
|
||||
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> data, int length, Pointer<EVP_MD> 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<Bytes> result =
|
||||
Pointer<Uint8>.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; // Data in C memory that we want to digest.
|
||||
|
||||
void setup() {
|
||||
data = Pointer<Uint8>.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<Data> dataInC = Pointer<Uint8>.allocate(count: L).cast();
|
||||
copyFromUint8ListToTarget(data, dataInC.load());
|
||||
hash(dataInC, L, hashAlgorithm);
|
||||
dataInC.free();
|
||||
}
|
||||
|
||||
void teardown() {}
|
||||
|
||||
void run() {
|
||||
final Pointer<Data> dataInC = Pointer<Uint8>.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());
|
||||
}
|
||||
@@ -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<EVP_MD> Function() EVP_sha512 =
|
||||
ssl.lookupFunction<Pointer<EVP_MD> Function(), Pointer<EVP_MD> 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<EVP_MD_CTX> Function() EVP_MD_CTX_new = ssl.lookupFunction<
|
||||
Pointer<EVP_MD_CTX> Function(),
|
||||
Pointer<EVP_MD_CTX> 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>) EVP_MD_CTX_free = ssl.lookupFunction<
|
||||
Void Function(Pointer<EVP_MD_CTX>),
|
||||
void Function(Pointer<EVP_MD_CTX>)>('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<EVP_MD_CTX>, Pointer<EVP_MD>) EVP_DigestInit =
|
||||
ssl.lookupFunction<Int32 Function(Pointer<EVP_MD_CTX>, Pointer<EVP_MD>),
|
||||
int Function(Pointer<EVP_MD_CTX>, Pointer<EVP_MD>)>('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<EVP_MD_CTX>, Pointer<Data>, int) EVP_DigestUpdate =
|
||||
ssl.lookupFunction<
|
||||
Int32 Function(Pointer<EVP_MD_CTX>, Pointer<Data>, IntPtr),
|
||||
int Function(
|
||||
Pointer<EVP_MD_CTX>, Pointer<Data>, 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<EVP_MD_CTX>, Pointer<Bytes>, Pointer<Uint32>)
|
||||
EVP_DigestFinal = ssl.lookupFunction<
|
||||
Int32 Function(Pointer<EVP_MD_CTX>, Pointer<Bytes>, Pointer<Uint32>),
|
||||
int Function(Pointer<EVP_MD_CTX>, Pointer<Bytes>,
|
||||
Pointer<Uint32>)>('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>) EVP_MD_CTX_size = ssl.lookupFunction<
|
||||
IntPtr Function(Pointer<EVP_MD_CTX>),
|
||||
int Function(Pointer<EVP_MD_CTX>)>('EVP_MD_CTX_size');
|
||||
@@ -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<IntPtr>();
|
||||
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);
|
||||
}
|
||||
@@ -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/
|
||||
@@ -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
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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<EVP_MD> {}
|
||||
|
||||
/// digest context.
|
||||
class EVP_MD_CTX extends Struct<EVP_MD_CTX> {}
|
||||
|
||||
/// Type for `void*` used to represent opaque data.
|
||||
class Data extends Struct<Data> {
|
||||
static Data fromUint8Pointer(Pointer<Uint8> p) => p.cast<Data>().load();
|
||||
|
||||
Pointer<Uint8> asUint8Pointer() => this.addressOf.cast();
|
||||
}
|
||||
|
||||
/// Type for `uint8_t*` used to represent byte data.
|
||||
class Bytes extends Struct<Bytes> {
|
||||
static Data fromUint8Pointer(Pointer<Uint8> p) => p.cast<Data>().load();
|
||||
|
||||
Pointer<Uint8> asUint8Pointer() => this.addressOf.cast();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<IntPtr>();
|
||||
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);
|
||||
}
|
||||
@@ -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/
|
||||
@@ -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
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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<Int8> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint8(Pointer<Uint8> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt16(Pointer<Int16> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint16(Pointer<Uint16> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt32(Pointer<Int32> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint32(Pointer<Uint32> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt64(Pointer<Int64> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint64(Pointer<Uint64> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreFloat(Pointer<Float> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreDouble(Pointer<Double> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void doStorePointer(
|
||||
Pointer<Pointer<Int8>> pointer, int length, Pointer<Int8> data) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(data);
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt64Mint(Pointer<Int64> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).store(0x7FFFFFFFFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Pointer load.
|
||||
//
|
||||
|
||||
int doLoadInt8(Pointer<Int8> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint8(Pointer<Uint8> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadInt16(Pointer<Int16> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint16(Pointer<Uint16> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadInt32(Pointer<Int32> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint32(Pointer<Uint32> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadInt64(Pointer<Int64> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint64(Pointer<Uint64> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
double doLoadFloat(Pointer<Float> pointer, int length) {
|
||||
double x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<double>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
double doLoadDouble(Pointer<Double> pointer, int length) {
|
||||
double x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<double>();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// Aggregates pointers through aggregrating their addresses.
|
||||
int doLoadPointer(Pointer<Pointer<Int8>> pointer, int length) {
|
||||
Pointer<Int8> 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<Int64> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<int>();
|
||||
}
|
||||
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<Int8> 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<Uint8> 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<Int16> 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<Uint16> 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<Int32> 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<Uint32> 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<Int64> 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<Uint64> 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<Float> 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<Double> 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<Int8>> pointer;
|
||||
Pointer<Int8> 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<Int64> 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());
|
||||
}
|
||||
@@ -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<VeryLargeStruct> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer.elementAt(i).load<VeryLargeStruct>().c = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Struct field load (plus Pointer elementAt and load).
|
||||
//
|
||||
|
||||
int doLoadInt32(Pointer<VeryLargeStruct> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer.elementAt(i).load<VeryLargeStruct>().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<VeryLargeStruct> 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<VeryLargeStruct> {
|
||||
@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<VeryLargeStruct> parent;
|
||||
|
||||
@IntPtr()
|
||||
int numChildren;
|
||||
|
||||
Pointer<VeryLargeStruct> children;
|
||||
|
||||
@Int8()
|
||||
int smallLastField;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<VeryLargeStruct> {
|
||||
Pointer<VeryLargeStruct> parent;
|
||||
|
||||
@IntPtr()
|
||||
int numChidlren;
|
||||
int numChildren;
|
||||
|
||||
Pointer<VeryLargeStruct> children;
|
||||
|
||||
|
||||
@@ -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 ] ||
|
||||
|
||||
Reference in New Issue
Block a user