diff --git a/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart b/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart index 70251945881..b4ba0c31893 100644 --- a/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart +++ b/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart @@ -11,6 +11,7 @@ import 'dart:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'digest.dart'; import 'types.dart'; @@ -48,11 +49,11 @@ String hash(Pointer data, int length, Pointer hashAlgorithm) { EVP_DigestInit(context, hashAlgorithm); EVP_DigestUpdate(context, data, length); final int resultSize = EVP_MD_CTX_size(context); - final Pointer result = allocate(count: resultSize).cast(); + final Pointer result = calloc(resultSize).cast(); EVP_DigestFinal(context, result, nullptr); EVP_MD_CTX_free(context); final String hash = base64Encode(toUint8List(result.ref, resultSize)); - free(result); + calloc.free(result); return hash; } @@ -83,14 +84,14 @@ class DigestCMemory extends BenchmarkBase { @override void setup() { - data = allocate(count: L).cast(); + data = calloc(L).cast(); copyFromUint8ListToTarget(inventData(L), data.ref); hash(data, L, hashAlgorithm); } @override void teardown() { - free(data); + calloc.free(data); } @override @@ -113,10 +114,10 @@ class DigestDartMemory extends BenchmarkBase { @override void setup() { data = inventData(L); - final Pointer dataInC = allocate(count: L).cast(); + final Pointer dataInC = calloc(L).cast(); copyFromUint8ListToTarget(data, dataInC.ref); hash(dataInC, L, hashAlgorithm); - free(dataInC); + calloc.free(dataInC); } @override @@ -124,10 +125,10 @@ class DigestDartMemory extends BenchmarkBase { @override void run() { - final Pointer dataInC = allocate(count: L).cast(); + final Pointer dataInC = calloc(L).cast(); copyFromUint8ListToTarget(data, dataInC.ref); final String result = hash(dataInC, L, hashAlgorithm); - free(dataInC); + calloc.free(dataInC); if (result != expectedHash) { throw Exception('$name: Unexpected result: $result'); } diff --git a/benchmarks/FfiBoringssl/dart/calloc.dart b/benchmarks/FfiBoringssl/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart b/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart index 7db1f91c14f..6bd064aa3c6 100644 --- a/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart +++ b/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart @@ -13,6 +13,7 @@ import 'dart:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'digest.dart'; import 'types.dart'; @@ -50,11 +51,11 @@ String hash(Pointer data, int length, Pointer hashAlgorithm) { EVP_DigestInit(context, hashAlgorithm); EVP_DigestUpdate(context, data, length); final int resultSize = EVP_MD_CTX_size(context); - final Pointer result = allocate(count: resultSize).cast(); + final Pointer result = calloc(resultSize).cast(); EVP_DigestFinal(context, result, nullptr); EVP_MD_CTX_free(context); final String hash = base64Encode(toUint8List(result.ref, resultSize)); - free(result); + calloc.free(result); return hash; } @@ -85,14 +86,14 @@ class DigestCMemory extends BenchmarkBase { @override void setup() { - data = allocate(count: L).cast(); + data = calloc(L).cast(); copyFromUint8ListToTarget(inventData(L), data.ref); hash(data, L, hashAlgorithm); } @override void teardown() { - free(data); + calloc.free(data); } @override @@ -115,10 +116,10 @@ class DigestDartMemory extends BenchmarkBase { @override void setup() { data = inventData(L); - final Pointer dataInC = allocate(count: L).cast(); + final Pointer dataInC = calloc(L).cast(); copyFromUint8ListToTarget(data, dataInC.ref); hash(dataInC, L, hashAlgorithm); - free(dataInC); + calloc.free(dataInC); } @override @@ -126,10 +127,10 @@ class DigestDartMemory extends BenchmarkBase { @override void run() { - final Pointer dataInC = allocate(count: L).cast(); + final Pointer dataInC = calloc(L).cast(); copyFromUint8ListToTarget(data, dataInC.ref); final String result = hash(dataInC, L, hashAlgorithm); - free(dataInC); + calloc.free(dataInC); if (result != expectedHash) { throw Exception('$name: Unexpected result: $result'); } diff --git a/benchmarks/FfiBoringssl/dart2/calloc.dart b/benchmarks/FfiBoringssl/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiBoringssl/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiCall/dart/FfiCall.dart b/benchmarks/FfiCall/dart/FfiCall.dart index c2392a1cffe..d33e12093d4 100644 --- a/benchmarks/FfiCall/dart/FfiCall.dart +++ b/benchmarks/FfiCall/dart/FfiCall.dart @@ -13,6 +13,7 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; import 'dlopen_helper.dart'; // @@ -1070,9 +1071,9 @@ class PointerUint8x01 extends BenchmarkBase { Pointer pointer = nullptr; @override - void setup() => pointer = allocate(count: N + 1); + void setup() => pointer = calloc(N + 1); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -1091,13 +1092,13 @@ class PointerUint8x02 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); } @override void teardown() { - free(pointer); + calloc.free(pointer); } @override @@ -1119,7 +1120,7 @@ class PointerUint8x04 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); pointer3 = pointer.elementAt(2); pointer4 = pointer.elementAt(3); @@ -1127,7 +1128,7 @@ class PointerUint8x04 extends BenchmarkBase { @override void teardown() { - free(pointer); + calloc.free(pointer); } @override @@ -1156,7 +1157,7 @@ class PointerUint8x10 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); pointer3 = pointer.elementAt(2); pointer4 = pointer.elementAt(3); @@ -1170,7 +1171,7 @@ class PointerUint8x10 extends BenchmarkBase { @override void teardown() { - free(pointer); + calloc.free(pointer); } @override @@ -1219,7 +1220,7 @@ class PointerUint8x20 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); pointer3 = pointer.elementAt(2); pointer4 = pointer.elementAt(3); @@ -1243,7 +1244,7 @@ class PointerUint8x20 extends BenchmarkBase { @override void teardown() { - free(pointer); + calloc.free(pointer); } @override diff --git a/benchmarks/FfiCall/dart/calloc.dart b/benchmarks/FfiCall/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiCall/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiCall/dart2/FfiCall.dart b/benchmarks/FfiCall/dart2/FfiCall.dart index fa67dce201c..3da29b4a6b3 100644 --- a/benchmarks/FfiCall/dart2/FfiCall.dart +++ b/benchmarks/FfiCall/dart2/FfiCall.dart @@ -15,6 +15,7 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; import 'dlopen_helper.dart'; // @@ -1072,9 +1073,9 @@ class PointerUint8x01 extends BenchmarkBase { Pointer pointer; @override - void setup() => pointer = allocate(count: N + 1); + void setup() => pointer = calloc(N + 1); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -1092,13 +1093,13 @@ class PointerUint8x02 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); } @override void teardown() { - free(pointer); + calloc.free(pointer); } @override @@ -1117,7 +1118,7 @@ class PointerUint8x04 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); pointer3 = pointer.elementAt(2); pointer4 = pointer.elementAt(3); @@ -1125,7 +1126,7 @@ class PointerUint8x04 extends BenchmarkBase { @override void teardown() { - free(pointer); + calloc.free(pointer); } @override @@ -1154,7 +1155,7 @@ class PointerUint8x10 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); pointer3 = pointer.elementAt(2); pointer4 = pointer.elementAt(3); @@ -1168,7 +1169,7 @@ class PointerUint8x10 extends BenchmarkBase { @override void teardown() { - free(pointer); + calloc.free(pointer); } @override @@ -1217,7 +1218,7 @@ class PointerUint8x20 extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N + 1); + pointer = calloc(N + 1); pointer2 = pointer.elementAt(1); pointer3 = pointer.elementAt(2); pointer4 = pointer.elementAt(3); @@ -1241,7 +1242,7 @@ class PointerUint8x20 extends BenchmarkBase { @override void teardown() { - free(pointer); + calloc.free(pointer); } @override diff --git a/benchmarks/FfiCall/dart2/calloc.dart b/benchmarks/FfiCall/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiCall/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiMemory/dart/FfiMemory.dart b/benchmarks/FfiMemory/dart/FfiMemory.dart index d9ee4872544..423fa740181 100644 --- a/benchmarks/FfiMemory/dart/FfiMemory.dart +++ b/benchmarks/FfiMemory/dart/FfiMemory.dart @@ -14,6 +14,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Pointer store. // @@ -210,9 +212,9 @@ class PointerInt8 extends BenchmarkBase { PointerInt8() : super('FfiMemory.PointerInt8'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -229,9 +231,9 @@ class PointerUint8 extends BenchmarkBase { PointerUint8() : super('FfiMemory.PointerUint8'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -248,9 +250,9 @@ class PointerInt16 extends BenchmarkBase { PointerInt16() : super('FfiMemory.PointerInt16'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -267,9 +269,9 @@ class PointerUint16 extends BenchmarkBase { PointerUint16() : super('FfiMemory.PointerUint16'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -286,9 +288,9 @@ class PointerInt32 extends BenchmarkBase { PointerInt32() : super('FfiMemory.PointerInt32'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -305,9 +307,9 @@ class PointerUint32 extends BenchmarkBase { PointerUint32() : super('FfiMemory.PointerUint32'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -324,9 +326,9 @@ class PointerInt64 extends BenchmarkBase { PointerInt64() : super('FfiMemory.PointerInt64'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -343,9 +345,9 @@ class PointerUint64 extends BenchmarkBase { PointerUint64() : super('FfiMemory.PointerUint64'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -362,9 +364,9 @@ class PointerFloat extends BenchmarkBase { PointerFloat() : super('FfiMemory.PointerFloat'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -381,9 +383,9 @@ class PointerDouble extends BenchmarkBase { PointerDouble() : super('FfiMemory.PointerDouble'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -402,14 +404,14 @@ class PointerPointer extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N); - data = allocate(); + pointer = calloc(N); + data = calloc(); } @override void teardown() { - free(pointer); - free(data); + calloc.free(pointer); + calloc.free(data); } @override @@ -427,9 +429,9 @@ class PointerInt64Mint extends BenchmarkBase { PointerInt64Mint() : super('FfiMemory.PointerInt64Mint'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { diff --git a/benchmarks/FfiMemory/dart/calloc.dart b/benchmarks/FfiMemory/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiMemory/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiMemory/dart2/FfiMemory.dart b/benchmarks/FfiMemory/dart2/FfiMemory.dart index 11fbea26819..d8d41e2d387 100644 --- a/benchmarks/FfiMemory/dart2/FfiMemory.dart +++ b/benchmarks/FfiMemory/dart2/FfiMemory.dart @@ -16,6 +16,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Pointer store. // @@ -212,9 +214,9 @@ class PointerInt8 extends BenchmarkBase { PointerInt8() : super('FfiMemory.PointerInt8'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -231,9 +233,9 @@ class PointerUint8 extends BenchmarkBase { PointerUint8() : super('FfiMemory.PointerUint8'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -250,9 +252,9 @@ class PointerInt16 extends BenchmarkBase { PointerInt16() : super('FfiMemory.PointerInt16'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -269,9 +271,9 @@ class PointerUint16 extends BenchmarkBase { PointerUint16() : super('FfiMemory.PointerUint16'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -288,9 +290,9 @@ class PointerInt32 extends BenchmarkBase { PointerInt32() : super('FfiMemory.PointerInt32'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -307,9 +309,9 @@ class PointerUint32 extends BenchmarkBase { PointerUint32() : super('FfiMemory.PointerUint32'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -326,9 +328,9 @@ class PointerInt64 extends BenchmarkBase { PointerInt64() : super('FfiMemory.PointerInt64'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -345,9 +347,9 @@ class PointerUint64 extends BenchmarkBase { PointerUint64() : super('FfiMemory.PointerUint64'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -364,9 +366,9 @@ class PointerFloat extends BenchmarkBase { PointerFloat() : super('FfiMemory.PointerFloat'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -383,9 +385,9 @@ class PointerDouble extends BenchmarkBase { PointerDouble() : super('FfiMemory.PointerDouble'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { @@ -404,14 +406,14 @@ class PointerPointer extends BenchmarkBase { @override void setup() { - pointer = allocate(count: N); - data = allocate(); + pointer = calloc(N); + data = calloc(); } @override void teardown() { - free(pointer); - free(data); + calloc.free(pointer); + calloc.free(data); } @override @@ -429,9 +431,9 @@ class PointerInt64Mint extends BenchmarkBase { PointerInt64Mint() : super('FfiMemory.PointerInt64Mint'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { diff --git a/benchmarks/FfiMemory/dart2/calloc.dart b/benchmarks/FfiMemory/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiMemory/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiStruct/dart/FfiStruct.dart b/benchmarks/FfiStruct/dart/FfiStruct.dart index 26768c5816d..a4a8f149630 100644 --- a/benchmarks/FfiStruct/dart/FfiStruct.dart +++ b/benchmarks/FfiStruct/dart/FfiStruct.dart @@ -12,6 +12,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Struct field store (plus Pointer elementAt and load). // @@ -50,9 +52,9 @@ class FieldLoadStore extends BenchmarkBase { FieldLoadStore() : super('FfiStruct.FieldLoadStore'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { diff --git a/benchmarks/FfiStruct/dart/calloc.dart b/benchmarks/FfiStruct/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiStruct/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiStruct/dart2/FfiStruct.dart b/benchmarks/FfiStruct/dart2/FfiStruct.dart index a11e8ab6b3f..6f4e17ce6be 100644 --- a/benchmarks/FfiStruct/dart2/FfiStruct.dart +++ b/benchmarks/FfiStruct/dart2/FfiStruct.dart @@ -14,6 +14,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Struct field store (plus Pointer elementAt and load). // @@ -52,9 +54,9 @@ class FieldLoadStore extends BenchmarkBase { FieldLoadStore() : super('FfiStruct.FieldLoadStore'); @override - void setup() => pointer = allocate(count: N); + void setup() => pointer = calloc(N); @override - void teardown() => free(pointer); + void teardown() => calloc.free(pointer); @override void run() { diff --git a/benchmarks/FfiStruct/dart2/calloc.dart b/benchmarks/FfiStruct/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiStruct/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, 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(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator();