|  | // 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. | 
|  |  | 
|  | // @dart=2.9 | 
|  |  | 
|  | import 'dart:ffi'; | 
|  | import 'dart:typed_data'; | 
|  |  | 
|  | import 'package:ffi/ffi.dart'; | 
|  | import 'package:benchmark_harness/benchmark_harness.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; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | 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; | 
|  | PointerInt8TypedDataNew() : super('FfiMemory.PointerInt8TypedDataNew'); | 
|  |  | 
|  | @override | 
|  | void setup() { | 
|  | pointer = calloc(N); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void teardown() { | 
|  | calloc.free(pointer); | 
|  | pointer = null; | 
|  | } | 
|  |  | 
|  | @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'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class PointerInt8TypedDataReuse extends BenchmarkBase { | 
|  | Pointer<Int8> pointer; | 
|  | Int8List typedData; | 
|  | PointerInt8TypedDataReuse() : super('FfiMemory.PointerInt8TypedDataReuse'); | 
|  |  | 
|  | @override | 
|  | void setup() { | 
|  | pointer = calloc(N); | 
|  | typedData = pointer.asTypedList(N); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void teardown() { | 
|  | calloc.free(pointer); | 
|  | pointer = null; | 
|  | typedData = null; | 
|  | } | 
|  |  | 
|  | @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; | 
|  | 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; | 
|  | 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; | 
|  | 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; | 
|  | 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; | 
|  | 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'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class PointerUint32Unaligned extends BenchmarkBase { | 
|  | Pointer<Uint32> pointer; | 
|  | Pointer<Uint32> unalignedPointer; | 
|  | 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; | 
|  | 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; | 
|  | 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'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class PointerUintPtr extends BenchmarkBase { | 
|  | Pointer<UintPtr> pointer; | 
|  | 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; | 
|  | 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; | 
|  | 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; | 
|  | Pointer<Int8> data; | 
|  | 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; | 
|  | 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() { | 
|  | final benchmarks = [ | 
|  | () => PointerInt8(), | 
|  | () => PointerInt8TypedDataNew(), | 
|  | () => PointerInt8TypedDataReuse(), | 
|  | () => PointerUint8(), | 
|  | () => PointerInt16(), | 
|  | () => PointerUint16(), | 
|  | () => PointerInt32(), | 
|  | () => PointerUint32(), | 
|  | () => PointerUint32Unaligned(), | 
|  | () => PointerInt64(), | 
|  | () => PointerInt64Mint(), | 
|  | () => PointerUint64(), | 
|  | () => PointerUintPtr(), | 
|  | () => PointerFloat(), | 
|  | () => PointerDouble(), | 
|  | () => PointerPointer(), | 
|  | ]; | 
|  | for (final benchmark in benchmarks) { | 
|  | benchmark().report(); | 
|  | } | 
|  | } |