mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:28:02 +00:00
[vm/ffi] Migrate benchmarks/Ffi* to CallocAllocator
This CL does not yet roll `package:ffi` to use `Allocator`, because that breaks the checked in Dart in Flutter in g3. Instead, this copies `_CallocAllocator` from `package:ffi` into the benchmarks. (We need a copy per benchmark such that file-copying before running benchmarks works properly.) The copies can be deleted when we can update `package:ffi` in the DEPS file to contain `_CallocAllocator`. New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705 Issue: https://github.com/dart-lang/sdk/issues/44621 Issue: https://github.com/dart-lang/sdk/issues/38721 Change-Id: I546de7ec65ceb6f05644a5f269b83f64656892e5 Cq-Include-Trybots: luci.dart.try:benchmark-linux-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178995 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
parent
c7306aaf41
commit
c36028b002
16 changed files with 984 additions and 92 deletions
|
@ -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> data, int length, Pointer<EVP_MD> hashAlgorithm) {
|
|||
EVP_DigestInit(context, hashAlgorithm);
|
||||
EVP_DigestUpdate(context, data, length);
|
||||
final int resultSize = EVP_MD_CTX_size(context);
|
||||
final Pointer<Bytes> result = allocate<Uint8>(count: resultSize).cast();
|
||||
final Pointer<Bytes> result = calloc<Uint8>(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<Uint8>(count: L).cast();
|
||||
data = calloc<Uint8>(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<Data> dataInC = allocate<Uint8>(count: L).cast();
|
||||
final Pointer<Data> dataInC = calloc<Uint8>(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<Data> dataInC = allocate<Uint8>(count: L).cast();
|
||||
final Pointer<Data> dataInC = calloc<Uint8>(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');
|
||||
}
|
||||
|
|
109
benchmarks/FfiBoringssl/dart/calloc.dart
Normal file
109
benchmarks/FfiBoringssl/dart/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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> data, int length, Pointer<EVP_MD> hashAlgorithm) {
|
|||
EVP_DigestInit(context, hashAlgorithm);
|
||||
EVP_DigestUpdate(context, data, length);
|
||||
final int resultSize = EVP_MD_CTX_size(context);
|
||||
final Pointer<Bytes> result = allocate<Uint8>(count: resultSize).cast();
|
||||
final Pointer<Bytes> result = calloc<Uint8>(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<Uint8>(count: L).cast();
|
||||
data = calloc<Uint8>(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<Data> dataInC = allocate<Uint8>(count: L).cast();
|
||||
final Pointer<Data> dataInC = calloc<Uint8>(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<Data> dataInC = allocate<Uint8>(count: L).cast();
|
||||
final Pointer<Data> dataInC = calloc<Uint8>(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');
|
||||
}
|
||||
|
|
111
benchmarks/FfiBoringssl/dart2/calloc.dart
Normal file
111
benchmarks/FfiBoringssl/dart2/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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<Uint8> 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
|
||||
|
|
109
benchmarks/FfiCall/dart/calloc.dart
Normal file
109
benchmarks/FfiCall/dart/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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<Uint8> 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
|
||||
|
|
111
benchmarks/FfiCall/dart2/calloc.dart
Normal file
111
benchmarks/FfiCall/dart2/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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() {
|
||||
|
|
109
benchmarks/FfiMemory/dart/calloc.dart
Normal file
109
benchmarks/FfiMemory/dart/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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() {
|
||||
|
|
111
benchmarks/FfiMemory/dart2/calloc.dart
Normal file
111
benchmarks/FfiMemory/dart2/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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() {
|
||||
|
|
109
benchmarks/FfiStruct/dart/calloc.dart
Normal file
109
benchmarks/FfiStruct/dart/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
Pointer<T> 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();
|
|
@ -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() {
|
||||
|
|
111
benchmarks/FfiStruct/dart2/calloc.dart
Normal file
111
benchmarks/FfiStruct/dart2/calloc.dart
Normal file
|
@ -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<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('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<WinHeapFreeNative, WinHeapFree>('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<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
|
||||
Pointer<T> 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();
|
Loading…
Reference in a new issue