[benchmarks/ffi] Memory access with asTypedList

Adds benchmarks using `asTypedList` on Pointers to investigate from
where it pays of to do construct a TypedData to do memory access
instead of on the Pointer directly.

x64 JIT results:
- Memory access on TypedData (int8) is ~2,5 faster than on Pointer.
FfiMemory.PointerInt8(RunTime): 72.23885718413639 us.
FfiMemory.PointerInt8TypedDataReuse(RunTime): 28.239710263614928 us.

- Constructing the TypedData for 1000 reads + 1000 writes takes ~20%
  of the total runtime, 400 reads or writes on TypedData, 160 reads
  or writes on Pointer.
FfiMemory.PointerInt8TypedDataNew(RunTime): 34.345480148372026 us.
FfiMemory.PointerInt8TypedDataReuse(RunTime): 28.239710263614928 us.

x64 AOT results:
FfiMemory.PointerInt8(RunTime): 13.862134213116345 us.
FfiMemory.PointerInt8TypedDataNew(RunTime): 26.149628021913365 us.
FfiMemory.PointerInt8TypedDataReuse(RunTime): 24.73322780505299 us.
- Using asTypedList is slower. (Some optimizations fail to kick in.)

Change-Id: I401edb88baf3a3c5094ecae808f122adf258da28
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/186289
Reviewed-by: Clement Skau <cskau@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Daco Harkes 2021-02-23 12:41:49 +00:00 committed by commit-bot@chromium.org
parent a8b42efa1a
commit c7422d5277
2 changed files with 144 additions and 0 deletions

View file

@ -10,6 +10,7 @@
// Dart with a specific marshalling and unmarshalling of data.
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
@ -24,6 +25,12 @@ void doStoreInt8(Pointer<Int8> pointer, int length) {
}
}
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;
@ -103,6 +110,14 @@ int doLoadInt8(Pointer<Int8> pointer, int length) {
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++) {
@ -224,6 +239,62 @@ class PointerInt8 extends BenchmarkBase {
}
}
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');
@ -449,6 +520,8 @@ class PointerInt64Mint extends BenchmarkBase {
void main() {
final benchmarks = [
() => PointerInt8(),
() => PointerInt8TypedDataNew(),
() => PointerInt8TypedDataReuse(),
() => PointerUint8(),
() => PointerInt16(),
() => PointerUint16(),

View file

@ -12,6 +12,7 @@
// @dart=2.9
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
@ -26,6 +27,12 @@ void doStoreInt8(Pointer<Int8> pointer, int length) {
}
}
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;
@ -105,6 +112,14 @@ int doLoadInt8(Pointer<Int8> pointer, int length) {
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++) {
@ -226,6 +241,60 @@ class PointerInt8 extends BenchmarkBase {
}
}
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');
@ -451,6 +520,8 @@ class PointerInt64Mint extends BenchmarkBase {
void main() {
final benchmarks = [
() => PointerInt8(),
() => PointerInt8TypedDataNew(),
() => PointerInt8TypedDataReuse(),
() => PointerUint8(),
() => PointerInt16(),
() => PointerUint16(),