From 4b7bf65be07689b01367aaba89481374801935bc Mon Sep 17 00:00:00 2001 From: buybackoff Date: Sun, 16 Feb 2020 23:09:56 +0100 Subject: [PATCH] Fix mimalloc callback, add remaining API and tests/benches --- dotnet/build/common.props | 2 +- dotnet/src/Spreads.Native/Mem.cs | 367 +++++++++++--- dotnet/tests/Spreads.Native.Run/Program.cs | 4 +- .../Spreads.Native.Run.csproj | 2 +- .../Spreads.Native.Tests.NuGet.csproj | 1 + .../tests/Spreads.Native.Tests/Benchmark.cs | 154 +++--- dotnet/tests/Spreads.Native.Tests/MemTests.cs | 409 +++++++++++++++- .../Spreads.Native.Tests.csproj | 1 + rs/Cargo.toml | 3 +- rs/spreads-mimalloc-sys/build.rs | 1 + rs/spreads-mimalloc-sys/mimalloc_wrapper.h | 8 +- rs/spreads-mimalloc-sys/src/mimalloc.rs | 116 ++++- rs/src/mem_allocation.rs | 451 ++++++++++++++++-- 13 files changed, 1311 insertions(+), 208 deletions(-) diff --git a/dotnet/build/common.props b/dotnet/build/common.props index f852cb7..04acef9 100644 --- a/dotnet/build/common.props +++ b/dotnet/build/common.props @@ -14,7 +14,7 @@ 1 - 215 + 216 diff --git a/dotnet/src/Spreads.Native/Mem.cs b/dotnet/src/Spreads.Native/Mem.cs index 0438232..e6982e9 100644 --- a/dotnet/src/Spreads.Native/Mem.cs +++ b/dotnet/src/Spreads.Native/Mem.cs @@ -1,11 +1,55 @@ using System; using System.Runtime.InteropServices; +using System.Security; +using System.Threading.Tasks; + +// ReSharper disable UnusedMember.Global namespace Spreads.Native { - public unsafe class Mem + [SuppressUnmanagedCodeSecurity] + public class Mem { private const string NativeLibraryName = UnsafeEx.NativeLibraryName; + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public unsafe delegate void DeferredFreeFun(bool force, ulong heartbeat, void* arg); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public unsafe delegate void OutputFun([MarshalAs(UnmanagedType.LPStr)] string msg, void* arg); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public unsafe delegate void ErrorFun(int err, void* arg); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] + public unsafe delegate bool BlockVisitFun(void* heap, void* area, void* block, UIntPtr blockSize, void* arg); + + + public enum Option + { + // stable options + ShowSrrors, + ShowStats, + OptionVerbose, + + // the following options are experimental + EagerCommit, + EagerRegionCommit, + ResetDecommits, + LargeOsPages, // implies eager commit + ReserveHugeOsPages, + SegmentCache, + PageReset, + AbandonedPageReset, + SegmentReset, + EagerCommitDelay, + ResetDelay, + UseNumaNodes, + OsTag, + MaxErrors, + Last, + EagerPageCommit = EagerCommit + } /// /// Allocate zero-initialized elements of bytes. @@ -15,14 +59,14 @@ public unsafe class Mem /// The size of each element. /// [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_calloc", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Calloc(uint count, uint size); + public static extern unsafe byte* Calloc(UIntPtr count, UIntPtr size); /// /// Allocate bytes. Returns a pointer to the allocated memory or NULL if out of memory. Returns a unique pointer if called with size 0. /// /// The number of bytes to allocate. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_malloc", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Malloc(uint size); + public static extern unsafe byte* Malloc(UIntPtr size); /// /// Re-allocate memory to bytes. @@ -35,30 +79,29 @@ public unsafe class Mem /// pointer to previously allocated memory (or NULL). /// the new required size in bytes. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_realloc", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Realloc(byte* p, uint newsize); + public static extern unsafe byte* Realloc(byte* p, UIntPtr newsize); /// /// Free previously allocated memory. The pointer p must have been allocated before (or be NULL). /// /// pointer to free, or NULL. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_free", CallingConvention = CallingConvention.Cdecl)] - public static extern void Free(byte* p); - + public static extern unsafe void Free(byte* p); [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_malloc_small", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* MallocSmall(uint size); + public static extern unsafe byte* MallocSmall(UIntPtr size); [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_zalloc_small", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* ZallocSmall(uint size); + public static extern unsafe byte* ZallocSmall(UIntPtr size); /// /// Allocate zero-initialized size bytes. Returns a pointer to newly allocated zero initialized memory, or NULL if out of memory. /// /// The size in bytes. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_zalloc", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Zalloc(uint size); + public static extern unsafe byte* Zalloc(UIntPtr size); /// /// Allocate count elements of size bytes. @@ -69,7 +112,7 @@ public unsafe class Mem /// The size of each element. /// [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_mallocn", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Mallocn(uint count, uint size); + public static extern unsafe byte* Mallocn(UIntPtr count, UIntPtr size); /// /// Re-allocate memory to count elements of size bytes. @@ -81,7 +124,7 @@ public unsafe class Mem /// The size of each element. /// [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_reallocn", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Reallocn(byte* p, uint count, uint size); + public static extern unsafe byte* Reallocn(byte* p, UIntPtr count, UIntPtr size); /// /// Re-allocate memory to bytes. @@ -94,9 +137,8 @@ public unsafe class Mem /// pointer to previously allocated memory (or NULL). /// the new required size in bytes. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_reallocf", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Reallocf(byte* p, uint newsize); - - + public static extern unsafe byte* Reallocf(byte* p, UIntPtr newsize); + /// /// Try to re-allocate memory to bytes in place. /// Returns a pointer to the re-allocated memory of @@ -110,9 +152,8 @@ public unsafe class Mem /// the new required size in bytes. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_expand", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Expand(byte* p, uint newsize); - - + public static extern unsafe byte* Expand(byte* p, UIntPtr newsize); + /// /// Returns the available bytes in the memory block, or 0 if p was NULL. /// The returned size can be used to call successfully. @@ -121,8 +162,7 @@ public unsafe class Mem /// Pointer to previously allocated memory (or NULL) [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_usable_size", CallingConvention = CallingConvention.Cdecl)] - public static extern ulong UsableSize(byte* p); - + public static extern unsafe ulong UsableSize(byte* p); /// /// Returns the size n that will be allocated, where n >= size. @@ -132,7 +172,7 @@ public unsafe class Mem /// The minimal required size in bytes. [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_good_size", CallingConvention = CallingConvention.Cdecl)] - public static extern ulong GoodSize(uint size); + public static extern ulong GoodSize(UIntPtr size); /// /// Eagerly free memory. Regular code should not have to call this function. @@ -143,82 +183,301 @@ public unsafe class Mem /// If true, aggressively return memory to the OS (can be expensive!). [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_collect", CallingConvention = CallingConvention.Cdecl)] public static extern void Collect(bool force); - - + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_mialloc_version", CallingConvention = CallingConvention.Cdecl)] public static extern int MimallocVersion(); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_malloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* MallocAligned(UIntPtr size, UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_malloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* MallocAlignedAt(UIntPtr size, UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_zalloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* ZallocAligned(UIntPtr size, UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_zalloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* ZallocAlignedAt(UIntPtr size, UIntPtr alignment, UIntPtr offset); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_calloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* CallocAligned(UIntPtr count, UIntPtr size, UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_calloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* CallocAlignedAt(UIntPtr count, UIntPtr size, UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_realloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* ReallocAligned(byte* p, UIntPtr newsize, UIntPtr alignment); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_malloc_aligned", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_realloc_aligned_at", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* MallocAligned(uint size, uint alignment); + public static extern unsafe byte* ReallocAlignedAt(byte* p, UIntPtr newsize, UIntPtr alignment, + UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_rezalloc", CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* Rezalloc(byte* p, UIntPtr newsize); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_recalloc", CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* Recalloc(byte* p, UIntPtr newcount, UIntPtr size); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_malloc_aligned_at", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_rezalloc_aligned", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* MallocAlignedAt(uint size, uint alignment, uint offset); + public static extern unsafe byte* RezallocAligned(byte* p, UIntPtr newsize, UIntPtr alignment); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_rezalloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* RezallocAlignedAt(byte* p, UIntPtr newsize, UIntPtr alignment, + UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_recalloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* RecallocAligned(byte* p, UIntPtr newcount, UIntPtr size, + UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_recalloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* RecallocAlignedAt(byte* p, UIntPtr newcount, UIntPtr size, + UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_stats_reset", + CallingConvention = CallingConvention.Cdecl)] + public static extern void StatsReset(); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_zalloc_aligned", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_stats_merge", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* ZallocAligned(uint size, uint alignment); + public static extern void StatsMerge(); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_stats_print", + CallingConvention = CallingConvention.Cdecl)] + public static extern void StatsPrint(); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_zalloc_aligned_at", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_stats_print_out", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* ZallocAlignedAt(uint size, uint alignment, uint offset); + public static extern unsafe void StatsPrintOut(OutputFun outputFun, void* arg); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_register_deferred_free", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void RegisterDeferredFree(DeferredFreeFun deferredFree, void* arg); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_calloc_aligned", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_register_output", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* CallocAligned(uint count, uint size, uint alignment); + public static extern unsafe void RegisterOutput(OutputFun outputFun, void* arg); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_register_error", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void RegisterError(OutputFun outputFun, void* arg); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_calloc_aligned_at", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_is_enabled", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* CallocAlignedAt(uint count, uint size, uint alignment, uint offset); + public static extern bool OptionIsEnabled(Option option); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_enable", + CallingConvention = CallingConvention.Cdecl)] + public static extern void OptionEnable(Option option); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_realloc_aligned", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_disable", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* ReallocAligned(byte* p, uint newsize, uint alignment); + public static extern void OptionDisable(Option option); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_set_enabled", + CallingConvention = CallingConvention.Cdecl)] + public static extern void OptionSetEnabled(Option option, bool enabled); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_realloc_aligned_at", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_set_enabled_default", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* ReallocAlignedAt(byte* p, uint newsize, uint alignment, - uint offset); + public static extern void OptionSetEnabledDefault(Option option, bool enabled); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_get", + CallingConvention = CallingConvention.Cdecl)] + public static extern int OptionGet(Option option); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_rezalloc", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Rezalloc(byte* p, uint newsize); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_set", + CallingConvention = CallingConvention.Cdecl)] + public static extern void OptionSet(Option option, int value); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_option_set_default", + CallingConvention = CallingConvention.Cdecl)] + public static extern void OptionSetDefault(Option option, int value); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_recalloc", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* Recalloc(byte* p, uint newcount, uint size); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_new", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void* HeapNew(); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_delete", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void HeapDelete(void* heap); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_rezalloc_aligned", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_destroy", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* RezallocAligned(byte* p, uint newsize, uint alignment); + public static extern unsafe void HeapDestroy(void* heap); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_set_default", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void* HeapSetDefault(void* heap); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_rezalloc_aligned_at", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_get_default", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* RezallocAlignedAt(byte* p, uint newsize, uint alignment, - uint offset); + public static extern unsafe void* HeapGetDefault(); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_get_backing", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void* HeapGetBacking(); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_recalloc_aligned", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_visit_blocks", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* RecallocAligned(byte* p, uint newcount, uint size, - uint alignment); + public static extern unsafe bool HeapVisitBlocks(void* heap, bool visitAllBlocks, BlockVisitFun visitor, + void* arg); + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_is_in_heap_region", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe bool IsInHeapRegion(byte* p); - [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_recalloc_aligned_at", + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_collect", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe void HeapCollect(void* heap, bool force); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_malloc", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapMalloc(void* heap, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_zalloc", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapZalloc(void* heap, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_calloc", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapCalloc(void* heap, UIntPtr count, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_mallocn", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapMallocn(void* heap, UIntPtr count, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_malloc_small", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapMallocSmall(void* heap, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_realloc", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRealloc(void* heap, byte* p, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_reallocn", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapReallocn(void* heap, byte* p, UIntPtr count, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_reallocf", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapReallocf(void* heap, byte* p, UIntPtr newsize); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_strdup", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapStrdup(void* heap, byte* s); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_strndup", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapStrndup(void* heap, byte* s, UIntPtr n); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_realpath", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRealpath(void* heap, byte* fname, byte* resolvedName); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_malloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapMallocAligned(void* heap, UIntPtr size, UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_malloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapMallocAlignedAt(void* heap, UIntPtr size, UIntPtr alignment, + UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_zalloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapZallocAligned(void* heap, UIntPtr size, UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_zalloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapZallocAlignedAt(void* heap, UIntPtr size, UIntPtr alignment, + UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_calloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapCallocAligned(void* heap, UIntPtr count, UIntPtr size, + UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_calloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapCallocAlignedAt(void* heap, UIntPtr count, UIntPtr size, + UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_realloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapReallocAligned(void* heap, byte* p, UIntPtr newsize, + UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_realloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapReallocAlignedAt(void* heap, byte* p, UIntPtr newsize, + UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_rezalloc", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRezalloc(void* heap, byte* p, UIntPtr newsize); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_recalloc", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRecalloc(void* heap, byte* p, UIntPtr newcount, UIntPtr size); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_rezalloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRezallocAligned(void* heap, byte* p, UIntPtr newsize, + UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_rezalloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRezallocAlignedAt(void* heap, byte* p, UIntPtr newsize, + UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_recalloc_aligned", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRecallocAligned(void* heap, byte* p, UIntPtr newcount, + UIntPtr size, UIntPtr alignment); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_recalloc_aligned_at", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe byte* HeapRecallocAlignedAt(void* heap, byte* p, UIntPtr newcount, + UIntPtr size, UIntPtr alignment, UIntPtr offset); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_contains_block", + CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe bool HeapContainsBlock(void* heap, byte* p); + + [DllImport(NativeLibraryName, EntryPoint = "spreads_mem_heap_check_owned", CallingConvention = CallingConvention.Cdecl)] - public static extern byte* RecallocAlignedAt(byte* p, uint newcount, uint size, - uint alignment, uint offset); + public static extern unsafe bool HeapCheckOwned(void* heap, byte* p); + + + // public static Task Gc = Task.Run(async () => + // { + // while (true) + // { + // try + // { + // Collect(true); + // Console.WriteLine("COLLECTED"); + // } + // catch + // { + // // TODO log + // } + // + // await Task.Delay(500); + // } + // }); } } \ No newline at end of file diff --git a/dotnet/tests/Spreads.Native.Run/Program.cs b/dotnet/tests/Spreads.Native.Run/Program.cs index f1b38d4..7787011 100644 --- a/dotnet/tests/Spreads.Native.Run/Program.cs +++ b/dotnet/tests/Spreads.Native.Run/Program.cs @@ -25,8 +25,8 @@ private static void Main(string[] args) // var summary = BenchmarkRunner.Run(); - var test = new Tests.VecTests(); - test.ForEachBench(); + var test = new Tests.MemTests(); + test.MimallocAllocFreePerf(); //var offset = UnsafeExTests.Helper.ElemOffset; //var size = UnsafeExTests.Helper.ElemSize; diff --git a/dotnet/tests/Spreads.Native.Run/Spreads.Native.Run.csproj b/dotnet/tests/Spreads.Native.Run/Spreads.Native.Run.csproj index 580467e..56a6106 100644 --- a/dotnet/tests/Spreads.Native.Run/Spreads.Native.Run.csproj +++ b/dotnet/tests/Spreads.Native.Run/Spreads.Native.Run.csproj @@ -6,7 +6,7 @@ true 7.2 false - AnyCPU + x64 true Exe diff --git a/dotnet/tests/Spreads.Native.Tests.NuGet/Spreads.Native.Tests.NuGet.csproj b/dotnet/tests/Spreads.Native.Tests.NuGet/Spreads.Native.Tests.NuGet.csproj index be8e314..d5bbe73 100644 --- a/dotnet/tests/Spreads.Native.Tests.NuGet/Spreads.Native.Tests.NuGet.csproj +++ b/dotnet/tests/Spreads.Native.Tests.NuGet/Spreads.Native.Tests.NuGet.csproj @@ -18,6 +18,7 @@ + diff --git a/dotnet/tests/Spreads.Native.Tests/Benchmark.cs b/dotnet/tests/Spreads.Native.Tests/Benchmark.cs index 7b9b306..742016d 100644 --- a/dotnet/tests/Spreads.Native.Tests/Benchmark.cs +++ b/dotnet/tests/Spreads.Native.Tests/Benchmark.cs @@ -12,8 +12,11 @@ namespace Spreads.Native.Tests { - // Copy from Spreads.Core, do not edit here + // TODO median for Dump() MOPS + /// + /// A utility to benchmark code snippets inside a using block. + /// public static class Benchmark { /// @@ -23,7 +26,9 @@ public static class Benchmark private static Stopwatch _sw; private static bool _headerIsPrinted; - private static readonly ConcurrentDictionary> Stats = new ConcurrentDictionary>(); + + private static readonly ConcurrentDictionary> Stats = + new ConcurrentDictionary>(); /// /// Returns an structure that starts benchmarking and stops it when its Dispose method is called. @@ -43,15 +48,42 @@ public static Stat Run(string caseName, long innerLoopCount = 1, bool silent = f return stat; } + private static double NaiveMedian(ArraySegment sourceNumbers) + { + //Framework 2.0 version of this method. there is an easier way in F4 + if (sourceNumbers == null || sourceNumbers.Count == 0) + throw new Exception("Median of empty array not defined."); + + //make sure the list is sorted, but use a new array + double[] sortedPNumbers = sourceNumbers.ToArray(); + Array.Sort(sortedPNumbers); + + //get the median + int size = sortedPNumbers.Length; + int mid = size / 2; + double median = (size % 2 != 0) + ? (double) sortedPNumbers[mid] + : ((double) sortedPNumbers[mid] + (double) sortedPNumbers[mid - 1]) / 2; + return median; + } + private static void PrintHeader(string summary, string caller, int? caseLength = null, string unit = null) { - var len = caseLength ?? 20; var caseDahes = new string('-', len + 1); - var dashes = $"{caseDahes,-21}|{new string('-', 8),8}:|{new string('-', 9),9}:|{new string('-', 6),6}:|{new string('-', 6),6}:|{new string('-', 6),6}:|{new string('-', 8),8}:"; + var dashes = + $"{caseDahes,-21}|{new string('-', 8),8}:|{new string('-', 9),9}:|{new string('-', 6),6}:|{new string('-', 6),6}:|{new string('-', 6),6}:|{new string('-', 8),8}:"; Console.WriteLine(); - if (!string.IsNullOrWhiteSpace(caller)) { Console.WriteLine($"**{caller}**"); } - if (!string.IsNullOrWhiteSpace(summary)) { Console.WriteLine($"*{summary}*"); } + if (!string.IsNullOrWhiteSpace(caller)) + { + Console.WriteLine($"**{caller}**"); + } + + if (!string.IsNullOrWhiteSpace(summary)) + { + Console.WriteLine($"*{summary}*"); + } + Console.WriteLine(); Console.WriteLine(GetHeader(caseLength, unit)); Console.WriteLine(dashes); @@ -71,7 +103,7 @@ internal static string GetHeader(int? caseLength = null, string unit = null) /// /// A description of the benchmark that is printed above the table. /// Overwrite default MOPS unit of measure - public static void Dump(string summary = "", [CallerMemberName]string caller = "", string unit = null) + public static void Dump(string summary = "", [CallerMemberName] string caller = "", string unit = null) { var maxLength = Stats.Keys.Select(k => k.Length).Max(); @@ -91,25 +123,28 @@ public static void Dump(string summary = "", [CallerMemberName]string caller = " Stat GetAverages(KeyValuePair> kvp) { if (kvp.Value == null) throw new ArgumentException($"Null stat list for the case: {kvp.Key}"); - if (kvp.Value.Count == 0) throw new InvalidOperationException($"Empty stat list for the case: {kvp.Key}"); + if (kvp.Value.Count == 0) + throw new InvalidOperationException($"Empty stat list for the case: {kvp.Key}"); var skip = kvp.Value.Count > 1 ? (kvp.Value.Count >= 10 ? 3 : 1) : 0; var values = kvp.Value.Skip(skip).ToList(); - var elapsed = values.Select(l => l._statSnapshot._elapsed).Average(); - var gc0 = values.Select(l => l._statSnapshot._gc0).Average(); - var gc1 = values.Select(l => l._statSnapshot._gc1).Average(); - var gc2 = values.Select(l => l._statSnapshot._gc2).Average(); - var memory = values.Select(l => l._statSnapshot._memory).Average(); + var elapsed = + NaiveMedian( + new ArraySegment(values.Select(l => (double) l._statSnapshot.Elapsed).ToArray())); + var gc0 = values.Select(l => l._statSnapshot.Gc0).Average(); + var gc1 = values.Select(l => l._statSnapshot.Gc1).Average(); + var gc2 = values.Select(l => l._statSnapshot.Gc2).Average(); + var memory = values.Select(l => l._statSnapshot.Memory).Average(); var result = kvp.Value.First(); - result._statSnapshot._elapsed = (long)elapsed; - result._statSnapshot._gc0 = gc0; - result._statSnapshot._gc1 = gc1; - result._statSnapshot._gc2 = gc2; - result._statSnapshot._memory = memory; + result._statSnapshot.Elapsed = (long) elapsed; + result._statSnapshot.Gc0 = gc0; + result._statSnapshot.Gc1 = gc1; + result._statSnapshot.Gc2 = gc2; + result._statSnapshot.Memory = memory; return result; } @@ -120,37 +155,37 @@ Stat GetAverages(KeyValuePair> kvp) /// public struct Stat : IDisposable { - internal readonly string _caseName; - internal Stopwatch _stopwatch; - internal long _innerLoopCount; - internal StatSnapshot _statSnapshot; + public string CaseName { get; } + public Stopwatch Stopwatch { get; } + public long InnerLoopCount { get; } + public StatSnapshot _statSnapshot; internal bool _silent; private readonly string _unit; internal Stat(string caseName, Stopwatch sw, long innerLoopCount, bool silent = false, string unit = null) { - _caseName = caseName; - _stopwatch = sw; - _innerLoopCount = innerLoopCount; + CaseName = caseName; + Stopwatch = sw; + InnerLoopCount = innerLoopCount; _silent = silent; _unit = unit; - _statSnapshot = new StatSnapshot(_stopwatch, true); + _statSnapshot = new StatSnapshot(Stopwatch, true); } /// public void Dispose() { - var statEntry = new StatSnapshot(_stopwatch, false); - Interlocked.Exchange(ref _sw, _stopwatch); + var statEntry = new StatSnapshot(Stopwatch, false); + Interlocked.Exchange(ref _sw, Stopwatch); - _statSnapshot._elapsed = statEntry._elapsed; - _statSnapshot._gc0 = statEntry._gc0 - _statSnapshot._gc0 - 2; - _statSnapshot._gc1 = statEntry._gc1 - _statSnapshot._gc1 - 2; - _statSnapshot._gc2 = statEntry._gc2 - _statSnapshot._gc2 - 2; - _statSnapshot._memory = statEntry._memory - _statSnapshot._memory; + _statSnapshot.Elapsed = statEntry.Elapsed; + _statSnapshot.Gc0 = statEntry.Gc0 - _statSnapshot.Gc0; + _statSnapshot.Gc1 = statEntry.Gc1 - _statSnapshot.Gc1; + _statSnapshot.Gc2 = statEntry.Gc2 - _statSnapshot.Gc2; + _statSnapshot.Memory = statEntry.Memory - _statSnapshot.Memory; - var list = Stats.GetOrAdd(_caseName, (s1) => new List()); + var list = Stats.GetOrAdd(CaseName, (s1) => new List()); list.Add(this); if (!_silent && !ForceSilence) @@ -160,6 +195,7 @@ public void Dispose() PrintHeader(null, null, unit: _unit); _headerIsPrinted = true; } + Console.WriteLine(ToString()); } } @@ -168,29 +204,36 @@ public void Dispose() /// Million operations per second. /// // ReSharper disable once InconsistentNaming - public double MOPS => Math.Round((_innerLoopCount * 0.001) / _statSnapshot._elapsed, 3); + public double MOPS => Math.Round((InnerLoopCount * 0.001) / (_statSnapshot.Elapsed / 10000.0), 3); + + internal StatSnapshot StatSnapshot + { + get { return _statSnapshot; } + } /// public override string ToString() { - var trimmedCaseName = _caseName.Length > 20 ? _caseName.Substring(0, 17) + "..." : _caseName; - return $"{trimmedCaseName,-20} |{MOPS,8:f2} | {_statSnapshot._elapsed,5} ms | {_statSnapshot._gc0,5:f1} | {_statSnapshot._gc1,5:f1} | {_statSnapshot._gc2,5:f1} | {_statSnapshot._memory / (1024 * 1024.0),5:f3} MB"; + var trimmedCaseName = CaseName.Length > 20 ? CaseName.Substring(0, 17) + "..." : CaseName; + return + $"{trimmedCaseName,-20} |{MOPS,8:f2} | {_statSnapshot.Elapsed / 10000.0:N0} ms | {_statSnapshot.Gc0,5:f1} | {_statSnapshot.Gc1,5:f1} | {_statSnapshot.Gc2,5:f1} | {_statSnapshot.Memory / (1024 * 1024.0),5:f3} MB"; } internal string ToString(int caseAlignmentLength) { - var paddedCaseName = _caseName.PadRight(caseAlignmentLength); - return $"{paddedCaseName,-20} |{MOPS,8:f2} | {_statSnapshot._elapsed,5} ms | {_statSnapshot._gc0,5:f1} | {_statSnapshot._gc1,5:f1} | {_statSnapshot._gc2,5:f1} | {_statSnapshot._memory / (1024 * 1024.0),5:f3} MB"; + var paddedCaseName = CaseName.PadRight(caseAlignmentLength); + return + $"{paddedCaseName,-20} |{MOPS,8:f2} | {_statSnapshot.Elapsed / 10000.0:N0} ms | {_statSnapshot.Gc0,5:f1} | {_statSnapshot.Gc1,5:f1} | {_statSnapshot.Gc2,5:f1} | {_statSnapshot.Memory / (1024 * 1024.0),5:f3} MB"; } } - internal struct StatSnapshot + public struct StatSnapshot { - internal long _elapsed; - internal double _gc0; - internal double _gc1; - internal double _gc2; - internal double _memory; + public long Elapsed; + public double Gc0; + public double Gc1; + public double Gc2; + public double Memory; public StatSnapshot(Stopwatch sw, bool start) { @@ -200,26 +243,27 @@ public StatSnapshot(Stopwatch sw, bool start) { // end of measurement, first stop timer then collect/count sw.Stop(); - _elapsed = sw.ElapsedMilliseconds; + Elapsed = sw.Elapsed.Ticks; // NB we exclude forced GC from counters, // by measuring memory before forced GC we could // calculate uncollected garbage - _memory = GC.GetTotalMemory(false); + Memory = GC.GetTotalMemory(false); } - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); + //GC.Collect(2, GCCollectionMode.Forced, true); + //GC.WaitForPendingFinalizers(); + //GC.Collect(2, GCCollectionMode.Forced, true); + //GC.WaitForPendingFinalizers(); + - _gc0 = GC.CollectionCount(0); - _gc1 = GC.CollectionCount(1); - _gc2 = GC.CollectionCount(2); + Gc0 = GC.CollectionCount(0); + Gc1 = GC.CollectionCount(1); + Gc2 = GC.CollectionCount(2); if (start) { - _memory = GC.GetTotalMemory(false); + Memory = GC.GetTotalMemory(false); // start timer after collecting GC stat sw.Restart(); } diff --git a/dotnet/tests/Spreads.Native.Tests/MemTests.cs b/dotnet/tests/Spreads.Native.Tests/MemTests.cs index f471440..7a15a97 100644 --- a/dotnet/tests/Spreads.Native.Tests/MemTests.cs +++ b/dotnet/tests/Spreads.Native.Tests/MemTests.cs @@ -1,4 +1,10 @@ using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using HdrHistogram; using NUnit.Framework; namespace Spreads.Native.Tests @@ -18,82 +24,437 @@ public void CouldGetMimallocVersion() [Test] public void CouldAllocFree() { - var p = Mem.Malloc(123); - p = Mem.Realloc(p, 456); - p = Mem.Reallocf(p, 789); + var p = Mem.Malloc((UIntPtr) 123); + p = Mem.Realloc(p, (UIntPtr) 456); + p = Mem.Reallocf(p, (UIntPtr) 789); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.Calloc(1, 2); + p = Mem.Calloc((UIntPtr) 1, (UIntPtr) 2); Assert.IsTrue(p != null); Console.WriteLine(Mem.UsableSize(p)); - p = Mem.Recalloc(p, 10, 3); + p = Mem.Recalloc(p, (UIntPtr) 10, (UIntPtr) 3); Assert.IsTrue(p != null); Console.WriteLine(Mem.UsableSize(p)); - p = Mem.Expand(p, (uint) Mem.UsableSize(p)); + p = Mem.Expand(p, (UIntPtr) Mem.UsableSize(p)); Assert.IsTrue(p != null); Mem.Free(p); Mem.Collect(false); - p = Mem.MallocSmall(42); + p = Mem.MallocSmall((UIntPtr) 42); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.ZallocSmall(42); + p = Mem.ZallocSmall((UIntPtr) 42); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.Zalloc(42000); + p = Mem.Zalloc((UIntPtr) 42000); Assert.IsTrue(p != null); - p = Mem.Rezalloc(p, 100000); + p = Mem.Rezalloc(p, (UIntPtr) 100000); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.Mallocn(10, 20); + p = Mem.Mallocn((UIntPtr) 10, (UIntPtr) 20); Assert.IsTrue(p != null); - p = Mem.Reallocn(p, 100, 20); + p = Mem.Reallocn(p, (UIntPtr) 100, (UIntPtr) 20); Assert.IsTrue(p != null); Mem.Free(p); Mem.Collect(true); - p = Mem.MallocAligned(16 * 17, 16); + p = Mem.MallocAligned((UIntPtr) (16 * 17), (UIntPtr) 16); Assert.IsTrue(p != null); Console.WriteLine(Mem.UsableSize(p)); - p = Mem.ReallocAligned(p, 16 * 30, 16); + p = Mem.ReallocAligned(p, (UIntPtr) (16 * 30), (UIntPtr) 16); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.MallocAlignedAt(3 + 16 * 17, 16, 3); + p = Mem.MallocAlignedAt((UIntPtr) (3 + 16 * 17), (UIntPtr) 16, (UIntPtr) 3); Assert.IsTrue(p != null); - p = Mem.ReallocAlignedAt(p, 3 + 16 * 34, 16, 3); + p = Mem.ReallocAlignedAt(p, (UIntPtr) (3 + 16 * 34), (UIntPtr) 16, (UIntPtr) 3); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.ZallocAligned(16 * 17, 16); + p = Mem.ZallocAligned((UIntPtr) (16 * 17), (UIntPtr) 16); Assert.IsTrue(p != null); - p = Mem.RezallocAligned(p, 16 * 30, 16); + p = Mem.RezallocAligned(p, (UIntPtr) (16 * 30), (UIntPtr) 16); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.ZallocAlignedAt(3 + 16 * 17, 16, 3); + p = Mem.ZallocAlignedAt((UIntPtr) (3 + 16 * 17), (UIntPtr) 16, (UIntPtr) 3); Assert.IsTrue(p != null); - p = Mem.RezallocAlignedAt(p, 3 + 16 * 34, 16, 3); + p = Mem.RezallocAlignedAt(p, (UIntPtr) (3 + 16 * 34), (UIntPtr) 16, (UIntPtr) 3); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.CallocAligned(17, 16, 16); + p = Mem.CallocAligned((UIntPtr) 17, (UIntPtr) 16, (UIntPtr) 16); Assert.IsTrue(p != null); - p = Mem.RecallocAligned(p, 34, 16, 16); + p = Mem.RecallocAligned(p, (UIntPtr) 34, (UIntPtr) 16, (UIntPtr) 16); Assert.IsTrue(p != null); Mem.Free(p); - p = Mem.CallocAlignedAt(17, 16, 16, 3); + p = Mem.CallocAlignedAt((UIntPtr) 17, (UIntPtr) 16, (UIntPtr) 16, (UIntPtr) 3); Assert.IsTrue(p != null); - p = Mem.RecallocAlignedAt(p, 34, 16, 16, 3); + p = Mem.RecallocAlignedAt(p, (UIntPtr) 34, (UIntPtr) 16, (UIntPtr) 16, (UIntPtr) 3); Assert.IsTrue(p != null); Mem.Free(p); } + + [Test] + public void CouldAllocFreeHeap() + { + var h = Mem.HeapNew(); + + var p = Mem.HeapMalloc(h, (UIntPtr) 123); + p = Mem.HeapRealloc(h, p, (UIntPtr) 456); + p = Mem.HeapReallocf(h, p, (UIntPtr) 789); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapCalloc(h, (UIntPtr) 1, (UIntPtr) 2); + Assert.IsTrue(p != null); + Console.WriteLine(Mem.UsableSize(p)); + p = Mem.HeapRecalloc(h, p, (UIntPtr) 10, (UIntPtr) 3); + Assert.IsTrue(p != null); + Console.WriteLine(Mem.UsableSize(p)); + p = Mem.Expand(p, (UIntPtr) Mem.UsableSize(p)); + Assert.IsTrue(p != null); + Mem.Free(p); + + Mem.HeapCollect(h, false); + + p = Mem.HeapMallocSmall(h, (UIntPtr) 42); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapZalloc(h, (UIntPtr) 42000); + Assert.IsTrue(p != null); + p = Mem.HeapRezalloc(h, p, (UIntPtr) 100000); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapMallocn(h, (UIntPtr) 10, (UIntPtr) 20); + Assert.IsTrue(p != null); + p = Mem.HeapReallocn(h, p, (UIntPtr) 100, (UIntPtr) 20); + Assert.IsTrue(p != null); + Mem.Free(p); + + Mem.HeapCollect(h, true); + + p = Mem.HeapMallocAligned(h, (UIntPtr) (16 * 17), (UIntPtr) 16); + Assert.IsTrue(p != null); + Console.WriteLine(Mem.UsableSize(p)); + p = Mem.HeapReallocAligned(h, p, (UIntPtr) (16 * 30), (UIntPtr) 16); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapMallocAlignedAt(h, (UIntPtr) (3 + 16 * 17), (UIntPtr) 16, (UIntPtr) 3); + Assert.IsTrue(p != null); + p = Mem.HeapReallocAlignedAt(h, p, (UIntPtr) (3 + 16 * 34), (UIntPtr) 16, (UIntPtr) 3); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapZallocAligned(h, (UIntPtr) (16 * 17), (UIntPtr) 16); + Assert.IsTrue(p != null); + p = Mem.HeapRezallocAligned(h, p, (UIntPtr) (16 * 30), (UIntPtr) 16); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapZallocAlignedAt(h, (UIntPtr) (3 + 16 * 17), (UIntPtr) 16, (UIntPtr) 3); + Assert.IsTrue(p != null); + p = Mem.HeapRezallocAlignedAt(h, p, (UIntPtr) (3 + 16 * 34), (UIntPtr) 16, (UIntPtr) 3); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapCallocAligned(h, (UIntPtr) 10, (UIntPtr) 16, (UIntPtr) 8); + Assert.IsTrue(p != null); + p = Mem.HeapRecallocAligned(h, p, (UIntPtr) 20, (UIntPtr) 16, (UIntPtr) 8); + Assert.IsTrue(p != null); + Mem.Free(p); + + p = Mem.HeapCallocAlignedAt(h, (UIntPtr) 17, (UIntPtr) 16, (UIntPtr) 16, (UIntPtr) 3); + Assert.IsTrue(p != null); + p = Mem.HeapRecallocAlignedAt(h, p, (UIntPtr) 34, (UIntPtr) 16, (UIntPtr) 16, (UIntPtr) 3); + Assert.IsTrue(p != null); + Mem.Free(p); + + Mem.HeapDestroy(h); + } + + [Test, Explicit] + public void MimallocAllocFreePerf() + { + var h = Mem.HeapNew(); + + Mem.OptionSetEnabled(Mem.Option.EagerCommit, true); + Mem.OptionSetEnabled(Mem.Option.LargeOsPages, true); + Mem.OptionSetEnabled(Mem.Option.ResetDecommits, true); + Mem.OptionSetEnabled(Mem.Option.PageReset, true); + Mem.OptionSetEnabled(Mem.Option.SegmentReset, true); + Mem.OptionSetEnabled(Mem.Option.AbandonedPageReset, true); + Mem.OptionSet(Mem.Option.ResetDelay, 0); + Mem.OptionSetEnabled(Mem.Option.EagerRegionCommit, true); + + Mem.RegisterOutput((str, arg) => { Console.Write(str); }, null); + + Assert.IsTrue(Mem.OptionIsEnabled(Mem.Option.PageReset)); + + var count = 100_000L; + var size = 32 * 4096; + IntPtr[] ptrs = new IntPtr[count]; + + for (int r = 0; r < 4; r++) + { + Task.Factory.StartNew(() => + { + using (Benchmark.Run("Alloc" + r, (long) (count * size / 1000.0))) + { + for (int i = 0; i < count; i++) + { + ptrs[i] = (IntPtr) Mem.HeapMalloc(h, (UIntPtr) size); + if ((long) (ptrs[i]) % 4096 != 0) + { + Assert.Fail(((long) (ptrs[i]) % 4096).ToString()); + // Console.WriteLine((long)(ptrs[i]) % 4096); + } + + for (int j = 0; j < size; j += 4096) ((byte*) ptrs[i])[j] = 0; + ((byte*) ptrs[i])[size - 1] = 0; + } + } + } + , TaskCreationOptions.LongRunning).Wait(); + + Task.Factory.StartNew(() => + { + using (Benchmark.Run("Free" + r, (long) (count * size / 1000.0))) + { + for (long i = count - 1; i >= 0; i--) + { + Mem.Free((byte*) ptrs[i]); + } + + // Mem.HeapCollect(h, true); + } + } + , TaskCreationOptions.LongRunning).Wait(); + } + + Mem.StatsPrint(); + + // long x = 0; + // while (true) + // { + // x++; + // + // ptrs[0] = (IntPtr) Spreads.Native.Mem.Malloc((UIntPtr) size); + // Mem.Free((byte*) ptrs[0]); + // if (x == long.MaxValue) + // break; + // + // Thread.Sleep(1); + // } + // + // // Spreads.Native.Mem.Collect(false); + // + // + // Thread.Sleep(10000000); + } + + + [Test, Explicit] + public void MimallocAllocFreeCallPerf() + { + Mem.OptionSetEnabled(Mem.Option.EagerCommit, true); + Mem.OptionSetEnabled(Mem.Option.LargeOsPages, true); + Mem.OptionSetEnabled(Mem.Option.ResetDecommits, true); + Mem.OptionSetEnabled(Mem.Option.PageReset, true); + Mem.OptionSetEnabled(Mem.Option.SegmentReset, true); + Mem.OptionSetEnabled(Mem.Option.AbandonedPageReset, true); + // Mem.OptionSet(Mem.Option.ResetDelay, 0); + Mem.OptionSetEnabled(Mem.Option.EagerRegionCommit, true); + + var h = Mem.HeapNew(); + + var count = 100_000L; + var size = 32 * 4096; + IntPtr[] ptrs = new IntPtr[count]; + + for (int r = 0; r < 4; r++) + { + using (Benchmark.Run("AllocFree" + r, count)) + { + for (int i = 0; i < count; i++) + { + ptrs[i] = (IntPtr) Mem.HeapMalloc(h, (UIntPtr) size); + for (int j = 0; j < size; j += 4096) ((byte*) ptrs[i])[j] = 0; + ((byte*) ptrs[i])[size - 1] = 0; + Mem.Free((byte*) ptrs[i]); + } + } + } + + Mem.StatsPrint(); + } + + + [Test, Explicit] + public void MimallocAllocFreeCallLatency() + { + Mem.OptionSetEnabled(Mem.Option.EagerCommit, true); + Mem.OptionSetEnabled(Mem.Option.LargeOsPages, true); + Mem.OptionSetEnabled(Mem.Option.ResetDecommits, true); + Mem.OptionSetEnabled(Mem.Option.PageReset, true); + Mem.OptionSetEnabled(Mem.Option.SegmentReset, true); + Mem.OptionSetEnabled(Mem.Option.AbandonedPageReset, true); + // Mem.OptionSet(Mem.Option.ResetDelay, 0); + Mem.OptionSetEnabled(Mem.Option.EagerRegionCommit, true); + + Mem.RegisterOutput((str, arg) => { Console.Write(str); }, null); + + var rng = new Random(); + var allocated = 0L; + + var h = Mem.HeapNew(); + + var count = 100_000L; + var size = 32 * 4096; + IntPtr[] ptrs = new IntPtr[count]; + + for (int r = 0; r < 4; r++) + { + var histogram = new HdrHistogram.LongHistogram(1, 1000000, 1); + + using (Benchmark.Run("AllocFree" + r, count)) + { + for (int i = 0; i < count; i++) + { + var x = rng.NextDouble(); + var start = Stopwatch.GetTimestamp(); + if (allocated < 1000) + Allocate(); + else if (allocated > 2000) + Free(); + else if (((2000 - allocated) / 1000.0 * x) > 0.5) + Allocate(); + else + Free(); + + var time = Stopwatch.GetTimestamp() - start; + + histogram.RecordValue(time); + + void Allocate() + { + ptrs[allocated] = (IntPtr) Mem.HeapMalloc(h, (UIntPtr) size); + for (int j = 0; j < size; j += 4096) ((byte*) ptrs[allocated])[j] = 0; + ((byte*) ptrs[allocated])[size - 1] = 0; + allocated++; + } + + void Free() + { + Mem.Free((byte*) ptrs[allocated - 1]); + allocated--; + } + } + } + + histogram.OutputPercentileDistribution(Console.Out, + outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToMicroseconds); + } + + Mem.StatsPrint(); + } + + [Test, Explicit] + public unsafe void MarshalAllocFreePerf() + { + var count = 100_000L; + var size = 32 * 4096; + IntPtr[] ptrs = new IntPtr[count]; + + for (int r = 0; r < 4; r++) + { + using (Benchmark.Run("Alloc" + r, (long) (count * size / 1000.0))) + { + for (int i = 0; i < count; i++) + { + ptrs[i] = Marshal.AllocHGlobal(size); + + // It's unaligned + // if ((long)(ptrs[i]) % 4096 != 0) + // Assert.Fail(((long) (ptrs[i]) % 4096).ToString()); + + for (int j = 0; j < size; j += 4096) ((byte*) ptrs[i])[j] = 0; + ((byte*) ptrs[i])[size - 1] = 0; + } + } + + using (Benchmark.Run("Free" + r, (long) (count * size / 1000.0))) + { + for (int i = 0; i < count; i++) + { + Marshal.FreeHGlobal(ptrs[i]); + } + } + } + } + + [Test, Explicit] + public unsafe void VirtualAllocFreePerf() + { + var count = 100_000L; + var size = 16 * 4096; + IntPtr[] ptrs = new IntPtr[count]; + + for (int r = 0; r < 3; r++) + { + using (Benchmark.Run("Alloc" + r, (long) (count * size / 1000.0))) + { + for (int i = 0; i < count; i++) + { + ptrs[i] = Kernel32.VirtualAlloc(IntPtr.Zero, (uint) size, + Kernel32.Consts.MEM_COMMIT | Kernel32.Consts.MEM_RESERVE, Kernel32.Consts.PAGE_READWRITE); + + if ((long) (ptrs[i]) % 4096 != 0) + Assert.Fail(((long) (ptrs[i]) % 4096).ToString()); + + for (int j = 0; j < size; j += 4096) ((byte*) ptrs[i])[j] = 0; + ((byte*) ptrs[i])[size - 1] = 0; + } + } + + using (Benchmark.Run("Free" + r, (long) (count * size / 1000.0))) + { + for (int i = 0; i < count; i++) + { + Kernel32.VirtualFree(ptrs[i], 0, Kernel32.Consts.MEM_RELEASE); + } + } + } + } + + public static class Kernel32 + { + [DllImport("Kernel32", SetLastError = true)] + [SuppressUnmanagedCodeSecurity] + internal static extern IntPtr VirtualAlloc([In] IntPtr lpAddress, [In] uint dwSize, + [In] int flAllocationType, [In] int flProtect); + + [DllImport("Kernel32", SetLastError = true)] + [SuppressUnmanagedCodeSecurity] + internal static extern bool VirtualFree([In] IntPtr lpAddress, [In] uint dwSize, [In] int dwFreeType); + + public static class Consts + { + public const int MEM_COMMIT = 0x00001000; + public const int MEM_RESERVE = 0x00002000; + public const int MEM_RELEASE = 0x8000; + public const int PAGE_READWRITE = 0x04; + } + } } } \ No newline at end of file diff --git a/dotnet/tests/Spreads.Native.Tests/Spreads.Native.Tests.csproj b/dotnet/tests/Spreads.Native.Tests/Spreads.Native.Tests.csproj index 5992d04..8b8bc86 100644 --- a/dotnet/tests/Spreads.Native.Tests/Spreads.Native.Tests.csproj +++ b/dotnet/tests/Spreads.Native.Tests/Spreads.Native.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/rs/Cargo.toml b/rs/Cargo.toml index 797de2b..793de2d 100644 --- a/rs/Cargo.toml +++ b/rs/Cargo.toml @@ -32,6 +32,7 @@ members = [ debug = true [profile.release] -debug = false +opt-level = 3 lto = true +debug = false panic = 'abort' diff --git a/rs/spreads-mimalloc-sys/build.rs b/rs/spreads-mimalloc-sys/build.rs index f14d9ae..0bd22d5 100644 --- a/rs/spreads-mimalloc-sys/build.rs +++ b/rs/spreads-mimalloc-sys/build.rs @@ -7,6 +7,7 @@ fn main() { // .use_core() // .ctypes_prefix("libc") // .whitelist_function("mi_.*") + // .size_t_is_usize(true) // .rustfmt_bindings(true) // .generate() // .expect("Unable to generate bindings"); diff --git a/rs/spreads-mimalloc-sys/mimalloc_wrapper.h b/rs/spreads-mimalloc-sys/mimalloc_wrapper.h index 9503bb3..910a9c8 100644 --- a/rs/spreads-mimalloc-sys/mimalloc_wrapper.h +++ b/rs/spreads-mimalloc-sys/mimalloc_wrapper.h @@ -120,13 +120,13 @@ mi_decl_nodiscard mi_decl_export size_t mi_good_size(size_t size) mi_attr_no // Internals // ------------------------------------------------------ -typedef void (mi_cdecl mi_deferred_free_fun)(bool force, unsigned long long heartbeat, void* arg); +typedef void mi_deferred_free_fun(bool force, unsigned long long heartbeat, void* arg); mi_decl_export void mi_register_deferred_free(mi_deferred_free_fun* deferred_free, void* arg) mi_attr_noexcept; -typedef void (mi_cdecl mi_output_fun)(const char* msg, void* arg); +typedef void mi_output_fun(const char* msg, void* arg); mi_decl_export void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept; -typedef void (mi_cdecl mi_error_fun)(int err, void* arg); +typedef void mi_error_fun(int err, void* arg); mi_decl_export void mi_register_error(mi_error_fun* fun, void* arg); mi_decl_export void mi_collect(bool force) mi_attr_noexcept; @@ -239,7 +239,7 @@ typedef struct mi_heap_area_s { size_t block_size; // size in bytes of each block } mi_heap_area_t; -typedef bool (mi_cdecl mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); +typedef bool mi_block_visit_fun(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg); diff --git a/rs/spreads-mimalloc-sys/src/mimalloc.rs b/rs/spreads-mimalloc-sys/src/mimalloc.rs index c030c3a..33450b7 100644 --- a/rs/spreads-mimalloc-sys/src/mimalloc.rs +++ b/rs/spreads-mimalloc-sys/src/mimalloc.rs @@ -51,23 +51,21 @@ extern "C" { extern "C" { pub fn mi_good_size(size: usize) -> usize; } +pub type mi_deferred_free_fun = ::core::option::Option< + unsafe extern "C" fn(force: bool, heartbeat: libc::c_ulonglong, arg: *mut libc::c_void), +>; extern "C" { - pub fn mi_register_deferred_free( - deferred_free: ::core::option::Option, - arg: *mut libc::c_void, - ); + pub fn mi_register_deferred_free(deferred_free: mi_deferred_free_fun, arg: *mut libc::c_void); } +pub type mi_output_fun = + ::core::option::Option; extern "C" { - pub fn mi_register_output( - out: ::core::option::Option, - arg: *mut libc::c_void, - ); + pub fn mi_register_output(out: mi_output_fun, arg: *mut libc::c_void); } +pub type mi_error_fun = + ::core::option::Option; extern "C" { - pub fn mi_register_error( - fun: ::core::option::Option, - arg: *mut libc::c_void, - ); + pub fn mi_register_error(fun: mi_error_fun, arg: *mut libc::c_void); } extern "C" { pub fn mi_collect(force: bool); @@ -85,10 +83,7 @@ extern "C" { pub fn mi_stats_print(out: *mut libc::c_void); } extern "C" { - pub fn mi_stats_print_out( - out: ::core::option::Option, - arg: *mut libc::c_void, - ); + pub fn mi_stats_print_out(out: mi_output_fun, arg: *mut libc::c_void); } extern "C" { pub fn mi_process_init(); @@ -100,10 +95,7 @@ extern "C" { pub fn mi_thread_done(); } extern "C" { - pub fn mi_thread_stats_print_out( - out: ::core::option::Option, - arg: *mut libc::c_void, - ); + pub fn mi_thread_stats_print_out(out: mi_output_fun, arg: *mut libc::c_void); } extern "C" { pub fn mi_malloc_aligned(size: usize, alignment: usize) -> *mut libc::c_void; @@ -386,11 +378,93 @@ extern "C" { extern "C" { pub fn mi_check_owned(p: *const libc::c_void) -> bool; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct mi_heap_area_s { + pub blocks: *mut libc::c_void, + pub reserved: usize, + pub committed: usize, + pub used: usize, + pub block_size: usize, +} +#[test] +fn bindgen_test_layout_mi_heap_area_s() { + assert_eq!( + ::core::mem::size_of::(), + 40usize, + concat!("Size of: ", stringify!(mi_heap_area_s)) + ); + assert_eq!( + ::core::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(mi_heap_area_s)) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).blocks as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(mi_heap_area_s), + "::", + stringify!(blocks) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).reserved as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(mi_heap_area_s), + "::", + stringify!(reserved) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).committed as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(mi_heap_area_s), + "::", + stringify!(committed) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).used as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(mi_heap_area_s), + "::", + stringify!(used) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).block_size as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(mi_heap_area_s), + "::", + stringify!(block_size) + ) + ); +} +pub type mi_heap_area_t = mi_heap_area_s; +pub type mi_block_visit_fun = ::core::option::Option< + unsafe extern "C" fn( + heap: *const mi_heap_t, + area: *const mi_heap_area_t, + block: *mut libc::c_void, + block_size: usize, + arg: *mut libc::c_void, + ) -> bool, +>; extern "C" { pub fn mi_heap_visit_blocks( heap: *const mi_heap_t, visit_all_blocks: bool, - visitor: ::core::option::Option bool>, + visitor: mi_block_visit_fun, arg: *mut libc::c_void, ) -> bool; } diff --git a/rs/src/mem_allocation.rs b/rs/src/mem_allocation.rs index b1fa516..6f04167 100644 --- a/rs/src/mem_allocation.rs +++ b/rs/src/mem_allocation.rs @@ -216,7 +216,7 @@ pub extern "C" fn spreads_mem_good_size(size: usize) -> usize { #[no_mangle] pub extern "C" fn spreads_mem_register_deferred_free( - deferred_free: ::core::option::Option, + deferred_free: mi_deferred_free_fun, arg: *mut libc::c_void, ) { unsafe { @@ -225,20 +225,14 @@ pub extern "C" fn spreads_mem_register_deferred_free( } #[no_mangle] -pub extern "C" fn spreads_mem_register_output( - out: ::core::option::Option, - arg: *mut libc::c_void, -) { +pub extern "C" fn spreads_mem_register_output(out: mi_output_fun, arg: *mut libc::c_void) { unsafe { return mi_register_output(out, arg); } } #[no_mangle] -pub extern "C" fn spreads_mem_register_error( - fun: ::core::option::Option, - arg: *mut libc::c_void, -) { +pub extern "C" fn spreads_mem_register_error(fun: mi_error_fun, arg: *mut libc::c_void) { unsafe { return mi_register_error(fun, arg); } @@ -258,6 +252,32 @@ pub extern "C" fn spreads_mem_mialloc_version() -> libc::c_int { } } +#[no_mangle] +pub extern "C" fn spreads_mem_stats_reset() { + unsafe { + return mi_stats_reset(); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_stats_merge() { + unsafe { + return mi_stats_merge(); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_stats_print() { + unsafe { + return mi_stats_print(0 as *mut libc::c_void); // out is ignored + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_stats_print_out(out: mi_output_fun, arg: *mut libc::c_void) { + unsafe { + return mi_stats_print_out(out, arg); + } +} + #[no_mangle] pub extern "C" fn spreads_mem_process_init() { unsafe { @@ -277,10 +297,7 @@ pub extern "C" fn spreads_mem_thread_done() { } } #[no_mangle] -pub extern "C" fn spreads_mem_thread_stats_print_out( - out: ::core::option::Option, - arg: *mut libc::c_void, -) { +pub extern "C" fn spreads_mem_thread_stats_print_out(out: mi_output_fun, arg: *mut libc::c_void) { unsafe { return mi_thread_stats_print_out(out, arg); } @@ -360,19 +377,6 @@ pub extern "C" fn spreads_mem_realloc_aligned_at( } } -#[no_mangle] -pub extern "C" fn spreads_mem_heap_get_default() -> *mut mi_heap_t { - unsafe { - return mi_heap_get_default(); - } -} -#[no_mangle] -pub extern "C" fn spreads_mem_heap_get_backing() -> *mut mi_heap_t { - unsafe { - return mi_heap_get_backing(); - } -} - #[no_mangle] pub extern "C" fn spreads_mem_rezalloc(p: *mut libc::c_void, newsize: usize) -> *mut libc::c_void { unsafe { @@ -440,25 +444,6 @@ pub extern "C" fn spreads_mem_check_owned(p: *const libc::c_void) -> bool { return mi_check_owned(p); } } -#[no_mangle] -pub extern "C" fn spreads_mem_heap_visit_blocks( - heap: *const mi_heap_t, - visit_all_blocks: bool, - visitor: ::core::option::Option bool>, - arg: *mut libc::c_void, -) -> bool { - unsafe { - return mi_heap_visit_blocks(heap, visit_all_blocks, visitor, arg); - } -} - -#[no_mangle] -pub extern "C" fn spreads_mem_is_in_heap_region(p: *const libc::c_void) -> bool { - unsafe { - return mi_is_in_heap_region(p); - } -} - #[no_mangle] pub extern "C" fn spreads_mem_reserve_huge_os_pages_interleave( pages: usize, @@ -508,6 +493,7 @@ pub extern "C" fn spreads_mem_option_disable(option: mi_option_t) { return mi_option_disable(option); } } + #[no_mangle] pub extern "C" fn spreads_mem_option_set_enabled(option: mi_option_t, enable: bool) { unsafe { @@ -539,9 +525,354 @@ pub extern "C" fn spreads_mem_option_set_default(option: mi_option_t, value: lib } } +#[no_mangle] +pub extern "C" fn spreads_mem_heap_new() -> *mut mi_heap_t { + unsafe { + return mi_heap_new(); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_delete(heap: *mut mi_heap_t) { + unsafe { + return mi_heap_delete(heap); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_destroy(heap: *mut mi_heap_t) { + unsafe { + return mi_heap_destroy(heap); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_set_default(heap: *mut mi_heap_t) -> *mut mi_heap_t { + unsafe { + return mi_heap_set_default(heap); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_heap_get_default() -> *mut mi_heap_t { + unsafe { + return mi_heap_get_default(); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_heap_get_backing() -> *mut mi_heap_t { + unsafe { + return mi_heap_get_backing(); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_heap_visit_blocks( + heap: *const mi_heap_t, + visit_all_blocks: bool, + visitor: mi_block_visit_fun, + arg: *mut libc::c_void, +) -> bool { + unsafe { + return mi_heap_visit_blocks(heap, visit_all_blocks, visitor, arg); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_is_in_heap_region(p: *const libc::c_void) -> bool { + unsafe { + return mi_is_in_heap_region(p); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_heap_collect(heap: *mut mi_heap_t, force: bool) { + unsafe { + return mi_heap_collect(heap, force); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_malloc(heap: *mut mi_heap_t, size: usize) -> *mut libc::c_void { + unsafe { + return mi_heap_malloc(heap, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_zalloc(heap: *mut mi_heap_t, size: usize) -> *mut libc::c_void { + unsafe { + return mi_heap_zalloc(heap, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_calloc( + heap: *mut mi_heap_t, + count: usize, + size: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_calloc(heap, count, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_mallocn( + heap: *mut mi_heap_t, + count: usize, + size: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_mallocn(heap, count, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_malloc_small( + heap: *mut mi_heap_t, + size: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_malloc_small(heap, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_realloc( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_realloc(heap, p, newsize); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_reallocn( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + count: usize, + size: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_reallocn(heap, p, count, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_reallocf( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_reallocf(heap, p, newsize); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_strdup( + heap: *mut mi_heap_t, + s: *const libc::c_char, +) -> *mut libc::c_char { + unsafe { + return mi_heap_strdup(heap, s); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_strndup( + heap: *mut mi_heap_t, + s: *const libc::c_char, + n: usize, +) -> *mut libc::c_char { + unsafe { + return mi_heap_strndup(heap, s, n); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_realpath( + heap: *mut mi_heap_t, + fname: *const libc::c_char, + resolved_name: *mut libc::c_char, +) -> *mut libc::c_char { + unsafe { + return mi_heap_realpath(heap, fname, resolved_name); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_malloc_aligned( + heap: *mut mi_heap_t, + size: usize, + alignment: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_malloc_aligned(heap, size, alignment); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_malloc_aligned_at( + heap: *mut mi_heap_t, + size: usize, + alignment: usize, + offset: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_malloc_aligned_at(heap, size, alignment, offset); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_zalloc_aligned( + heap: *mut mi_heap_t, + size: usize, + alignment: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_zalloc_aligned(heap, size, alignment); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_zalloc_aligned_at( + heap: *mut mi_heap_t, + size: usize, + alignment: usize, + offset: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_zalloc_aligned_at(heap, size, alignment, offset); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_calloc_aligned( + heap: *mut mi_heap_t, + count: usize, + size: usize, + alignment: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_calloc_aligned(heap, count, size, alignment); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_calloc_aligned_at( + heap: *mut mi_heap_t, + count: usize, + size: usize, + alignment: usize, + offset: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_calloc_aligned_at(heap, count, size, alignment, offset); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_realloc_aligned( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, + alignment: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_realloc_aligned(heap, p, newsize, alignment); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_realloc_aligned_at( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, + alignment: usize, + offset: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_realloc_aligned_at(heap, p, newsize, alignment, offset); + } +} + +#[no_mangle] +pub extern "C" fn spreads_mem_heap_rezalloc( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_rezalloc(heap, p, newsize); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_recalloc( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newcount: usize, + size: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_recalloc(heap, p, newcount, size); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_rezalloc_aligned( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, + alignment: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_rezalloc_aligned(heap, p, newsize, alignment); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_rezalloc_aligned_at( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newsize: usize, + alignment: usize, + offset: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_rezalloc_aligned_at(heap, p, newsize, alignment, offset); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_recalloc_aligned( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newcount: usize, + size: usize, + alignment: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_recalloc_aligned(heap, p, newcount, size, alignment); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_recalloc_aligned_at( + heap: *mut mi_heap_t, + p: *mut libc::c_void, + newcount: usize, + size: usize, + alignment: usize, + offset: usize, +) -> *mut libc::c_void { + unsafe { + return mi_heap_recalloc_aligned_at(heap, p, newcount, size, alignment, offset); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_contains_block( + heap: *mut mi_heap_t, + p: *const libc::c_void, +) -> bool { + unsafe { + return mi_heap_contains_block(heap, p); + } +} +#[no_mangle] +pub extern "C" fn spreads_mem_heap_check_owned( + heap: *mut mi_heap_t, + p: *const libc::c_void, +) -> bool { + unsafe { + return mi_heap_check_owned(heap, p); + } +} + #[cfg(test)] mod tests { + extern crate alloc; + use super::*; + use alloc::boxed::Box; #[test] fn it_frees_allocated_memory() { @@ -581,5 +912,35 @@ mod tests { fn it_could_call_mimalloc() { let x = spreads_mem_malloc(10); spreads_mem_free(x); + spreads_mem_collect(true); + } + + #[test] + fn it_could_print_stats() { + spreads_mem_stats_print(); + } + + // A list of C functions that are being imported + extern "C" { + pub fn printf(format: *const u8, ...) -> i32; + } + + #[no_mangle] + pub extern "C" fn test_print_out(msg: *const libc::c_char, arg: *mut libc::c_void) { + unsafe { + printf(msg as *const u8); + } + } + + #[test] + fn it_could_print_out_stats() { + // spreads_mem_register_output(Option::Some(test_print_out), 0 as *mut libc::c_void); + // spreads_mem_option_set(mi_option_e_mi_option_show_stats, 0); + // spreads_mem_option_set_default(mi_option_e_mi_option_show_stats, 0); + // spreads_mem_option_set_enabled_default(mi_option_e_mi_option_show_stats, false); + spreads_mem_option_disable(mi_option_e_mi_option_show_stats); + spreads_mem_option_disable(mi_option_e_mi_option_verbose); + // spreads_mem_option_set_enabled(mi_option_e_mi_option_show_stats, false); + spreads_mem_stats_print_out(Option::Some(test_print_out), 0 as *mut libc::c_void); } }