dart-sdk/benchmarks/FfiMemory/dart/FfiMemory.dart

605 lines
13 KiB
Dart
Raw Normal View History

// 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 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:ffi/ffi.dart';
//
// Pointer store.
//
void doStoreInt8(Pointer<Int8> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreInt8TypedData(Int8List typedData, int length) {
for (int i = 0; i < length; i++) {
typedData[i] = 1;
}
}
void doStoreUint8(Pointer<Uint8> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreInt16(Pointer<Int16> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreUint16(Pointer<Uint16> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreInt32(Pointer<Int32> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreUint32(Pointer<Uint32> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreInt64(Pointer<Int64> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreUint64(Pointer<Uint64> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
[vm/ffi] ABI-specific integers This CL adds support for users defining integers which are mapped to differing sizes and signedness based on the application binary interface the Dart VM is running on. Notable implementation design decisions: - ABIs are open world, so that adding an ABI to the Dart VM does not break existing definitions. Thus, we only figure out in the VM that we're missing a mapping. We throw compile-time errors. - In AOT, these show up in the precompilation step. - In JIT, these show up as `_CompileTimeError` at runtime. Note that these can be caught. So in subsequent compilation steps we need to ensure that we also throw the same compile-time error. - We match on the call-sites (streaming_flowgraph_builder) rather than method bodies (kernel_to_il) of AbiSpecific loads and stores so that we can compile for the int-size of the call site. API design decisions: https://github.com/dart-lang/sdk/issues/42563#issuecomment-981774001 Closes: https://github.com/dart-lang/sdk/issues/42563 TEST=tests/ffi_2/abi_*_test.dart TEST=tests/ffi/function_*_generated_test.dart TEST=tests/ffi/vmspecific_static_checks_test.dart Change-Id: I8c8df36fab939b6fb614c5f1ee8e1bf46b6e9521 Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try,analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,benchmark-linux-try,dart-sdk-linux-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-canary-linux-debug-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-fuchsia-release-x64-try,vm-kernel-checked-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-linux-debug-x64c-try,vm-kernel-mac-debug-x64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-nnbd-win-release-ia32-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-android-release-arm64c-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221501 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2021-12-16 22:07:00 +00:00
void doStoreUintPtr(Pointer<UintPtr> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreFloat(Pointer<Float> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1.0;
}
}
void doStoreDouble(Pointer<Double> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1.0;
}
}
void doStorePointer(
Pointer<Pointer<Int8>> pointer, int length, Pointer<Int8> data) {
for (int i = 0; i < length; i++) {
pointer[i] = data;
}
}
void doStoreInt64Mint(Pointer<Int64> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 0x7FFFFFFFFFFFFFFF;
}
}
//
// Pointer load.
//
int doLoadInt8(Pointer<Int8> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadInt8TypedData(Int8List typedData, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += typedData[i];
}
return x;
}
int doLoadUint8(Pointer<Uint8> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadInt16(Pointer<Int16> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadUint16(Pointer<Uint16> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadInt32(Pointer<Int32> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadUint32(Pointer<Uint32> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadInt64(Pointer<Int64> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
int doLoadUint64(Pointer<Uint64> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
[vm/ffi] ABI-specific integers This CL adds support for users defining integers which are mapped to differing sizes and signedness based on the application binary interface the Dart VM is running on. Notable implementation design decisions: - ABIs are open world, so that adding an ABI to the Dart VM does not break existing definitions. Thus, we only figure out in the VM that we're missing a mapping. We throw compile-time errors. - In AOT, these show up in the precompilation step. - In JIT, these show up as `_CompileTimeError` at runtime. Note that these can be caught. So in subsequent compilation steps we need to ensure that we also throw the same compile-time error. - We match on the call-sites (streaming_flowgraph_builder) rather than method bodies (kernel_to_il) of AbiSpecific loads and stores so that we can compile for the int-size of the call site. API design decisions: https://github.com/dart-lang/sdk/issues/42563#issuecomment-981774001 Closes: https://github.com/dart-lang/sdk/issues/42563 TEST=tests/ffi_2/abi_*_test.dart TEST=tests/ffi/function_*_generated_test.dart TEST=tests/ffi/vmspecific_static_checks_test.dart Change-Id: I8c8df36fab939b6fb614c5f1ee8e1bf46b6e9521 Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try,analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,benchmark-linux-try,dart-sdk-linux-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-canary-linux-debug-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-fuchsia-release-x64-try,vm-kernel-checked-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-linux-debug-x64c-try,vm-kernel-mac-debug-x64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-nnbd-win-release-ia32-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-android-release-arm64c-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221501 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2021-12-16 22:07:00 +00:00
int doLoadUintPtr(Pointer<UintPtr> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
double doLoadFloat(Pointer<Float> pointer, int length) {
double x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
double doLoadDouble(Pointer<Double> pointer, int length) {
double x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
// Aggregates pointers through aggregating 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[i];
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[i];
}
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)
const N = 1000;
class PointerInt8 extends BenchmarkBase {
Pointer<Int8> pointer = nullptr;
PointerInt8() : super('FfiMemory.PointerInt8');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreInt8(pointer, N);
final int x = doLoadInt8(pointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
class PointerInt8TypedDataNew extends BenchmarkBase {
Pointer<Int8> pointer = nullptr;
PointerInt8TypedDataNew() : super('FfiMemory.PointerInt8TypedDataNew');
@override
void setup() {
pointer = calloc(N);
}
@override
void teardown() {
calloc.free(pointer);
pointer = nullptr;
}
@override
void run() {
final typedData = pointer.asTypedList(N);
doStoreInt8TypedData(typedData, N);
final int x = doLoadInt8TypedData(typedData, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x, expected $N');
}
}
}
final emptyTypedData = Int8List(0);
class PointerInt8TypedDataReuse extends BenchmarkBase {
Pointer<Int8> pointer = nullptr;
Int8List typedData = emptyTypedData;
PointerInt8TypedDataReuse() : super('FfiMemory.PointerInt8TypedDataReuse');
@override
void setup() {
pointer = calloc(N);
typedData = pointer.asTypedList(N);
}
@override
void teardown() {
calloc.free(pointer);
pointer = nullptr;
typedData = emptyTypedData;
}
@override
void run() {
doStoreInt8TypedData(typedData, N);
final int x = doLoadInt8TypedData(typedData, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x, expected $N');
}
}
}
class PointerUint8 extends BenchmarkBase {
Pointer<Uint8> pointer = nullptr;
PointerUint8() : super('FfiMemory.PointerUint8');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
PointerInt16() : super('FfiMemory.PointerInt16');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
PointerUint16() : super('FfiMemory.PointerUint16');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
PointerInt32() : super('FfiMemory.PointerInt32');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
PointerUint32() : super('FfiMemory.PointerUint32');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreUint32(pointer, N);
final int x = doLoadUint32(pointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
2021-03-16 20:17:31 +00:00
class PointerUint32Unaligned extends BenchmarkBase {
Pointer<Uint32> pointer = nullptr;
Pointer<Uint32> unalignedPointer = nullptr;
PointerUint32Unaligned() : super('FfiMemory.PointerUint32Unaligned');
@override
void setup() {
pointer = calloc(N + 1);
unalignedPointer = Pointer.fromAddress(pointer.address + 1);
}
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreUint32(unalignedPointer, N);
final int x = doLoadUint32(unalignedPointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
class PointerInt64 extends BenchmarkBase {
Pointer<Int64> pointer = nullptr;
PointerInt64() : super('FfiMemory.PointerInt64');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
PointerUint64() : super('FfiMemory.PointerUint64');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreUint64(pointer, N);
final int x = doLoadUint64(pointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
[vm/ffi] ABI-specific integers This CL adds support for users defining integers which are mapped to differing sizes and signedness based on the application binary interface the Dart VM is running on. Notable implementation design decisions: - ABIs are open world, so that adding an ABI to the Dart VM does not break existing definitions. Thus, we only figure out in the VM that we're missing a mapping. We throw compile-time errors. - In AOT, these show up in the precompilation step. - In JIT, these show up as `_CompileTimeError` at runtime. Note that these can be caught. So in subsequent compilation steps we need to ensure that we also throw the same compile-time error. - We match on the call-sites (streaming_flowgraph_builder) rather than method bodies (kernel_to_il) of AbiSpecific loads and stores so that we can compile for the int-size of the call site. API design decisions: https://github.com/dart-lang/sdk/issues/42563#issuecomment-981774001 Closes: https://github.com/dart-lang/sdk/issues/42563 TEST=tests/ffi_2/abi_*_test.dart TEST=tests/ffi/function_*_generated_test.dart TEST=tests/ffi/vmspecific_static_checks_test.dart Change-Id: I8c8df36fab939b6fb614c5f1ee8e1bf46b6e9521 Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try,analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,benchmark-linux-try,dart-sdk-linux-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-canary-linux-debug-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-fuchsia-release-x64-try,vm-kernel-checked-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-linux-debug-x64c-try,vm-kernel-mac-debug-x64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-nnbd-win-release-ia32-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-android-release-arm64c-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221501 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2021-12-16 22:07:00 +00:00
class PointerUintPtr extends BenchmarkBase {
Pointer<UintPtr> pointer = nullptr;
PointerUintPtr() : super('FfiMemory.PointerUintPtr');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreUintPtr(pointer, N);
final int x = doLoadUintPtr(pointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
class PointerFloat extends BenchmarkBase {
Pointer<Float> pointer = nullptr;
PointerFloat() : super('FfiMemory.PointerFloat');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
PointerDouble() : super('FfiMemory.PointerDouble');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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 = nullptr;
Pointer<Int8> data = nullptr;
PointerPointer() : super('FfiMemory.PointerPointer');
@override
void setup() {
pointer = calloc(N);
data = calloc();
}
@override
void teardown() {
calloc.free(pointer);
calloc.free(data);
}
@override
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 = nullptr;
PointerInt64Mint() : super('FfiMemory.PointerInt64Mint');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
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.
//
void main(List<String> args) {
final benchmarks = [
PointerInt8.new,
PointerInt8TypedDataNew.new,
PointerInt8TypedDataReuse.new,
PointerUint8.new,
PointerInt16.new,
PointerUint16.new,
PointerInt32.new,
PointerUint32.new,
PointerUint32Unaligned.new,
PointerInt64.new,
PointerInt64Mint.new,
PointerUint64.new,
PointerUintPtr.new,
PointerFloat.new,
PointerDouble.new,
PointerPointer.new,
];
final filter = args.firstOrNull;
for (var constructor in benchmarks) {
final benchmark = constructor();
if (filter == null || benchmark.name.contains(filter)) {
benchmark.report();
}
}
}