Repository: allisterb/jemalloc.NET Branch: master Commit: 73123d43a9fd Files: 79 Total size: 484.1 KB Directory structure: gitextract_gm5xlq0e/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── build.cmd ├── jemalloc.Api/ │ ├── Buffer.cs │ ├── BufferHelpers.cs │ ├── Extensions/ │ │ └── TypeExtensions.cs │ ├── ExtentHooks.cs │ ├── FixedBuffer.cs │ ├── FixedBufferAllocation.cs │ ├── FixedBufferEnumerator.cs │ ├── GDI.cs │ ├── GenericMath.cs │ ├── HugeArray.cs │ ├── HugeBuffer.cs │ ├── HugeBufferEnumerator.cs │ ├── IBufferAllocation.cs │ ├── JemApi.cs │ ├── JemPinnable.cs │ ├── JemUtil.cs │ ├── MemoryRef.cs │ ├── NDArray.cs │ ├── SafeArray.cs │ ├── SafeBuffer.cs │ ├── SafeBufferEnumerator.cs │ ├── Utf8Buffer.cs │ ├── jemalloc.Api.csproj │ └── jemalloc.cs ├── jemalloc.Benchmarks/ │ ├── BenchmarkStatisticColumn.cs │ ├── Benchmarks/ │ │ ├── FixedBufferVsManagedArray.cs │ │ ├── HugeNativeVsManagedArray.cs │ │ ├── MallocVsArray.cs │ │ ├── SafeVsManagedArray.cs │ │ └── Vector.cs │ ├── Design.md │ ├── JemBenchmark.cs │ ├── JemBenchmarkAttribute.cs │ ├── JemBenchmarkJobAttribute.cs │ ├── JemBenchmarkParam.cs │ ├── JemStatisticColumn.cs │ ├── ProcessStatisticColumn.cs │ ├── TestUDT.cs │ └── jemalloc.Benchmarks.csproj ├── jemalloc.Bindings/ │ ├── App.config │ ├── JemallocLibrary.cs │ ├── Program.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── jemalloc-win-msvc.h │ ├── jemalloc.Bindings.csproj │ └── packages.config ├── jemalloc.Buffers/ │ ├── JemPinnable.cs │ ├── NativeHelpers.cs │ ├── NativeMemory.cs │ └── jemalloc.Buffers.csproj ├── jemalloc.Cli/ │ ├── Options.cs │ ├── Program.cs │ ├── Properties/ │ │ └── PublishProfiles/ │ │ └── ReleaseProfile.pubxml │ └── jemalloc.Cli.csproj ├── jemalloc.Examples/ │ ├── TestUDT.cs │ └── jemalloc.Examples.csproj ├── jemalloc.NET.sln ├── jemalloc.StressTests/ │ ├── FixedBufferStressTests.cs │ └── jemalloc.StressTests.csproj ├── jemalloc.Tests/ │ ├── BufferTests.cs │ ├── FixedBufferTests.cs │ ├── HugeArrayTests.cs │ ├── MallCtlTests.cs │ ├── MallocConfTests.cs │ ├── MallocMessageTests.cs │ ├── MallocTests.cs │ ├── SafeArrayTests.cs │ ├── UDTTests.cs │ ├── Utf8BufferTests.cs │ ├── VectorTests.cs │ ├── jemalloc.Tests.csproj │ └── jemallocTest.cs └── jembench.cmd ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc /BenchmarkDotNet.Artifacts/results /jemalloc.Cli/BenchmarkDotNet.Artifacts/results /jemalloc.Cli/Properties/launchSettings.json /*.ppm ================================================ FILE: .gitmodules ================================================ [submodule "jemalloc"] path = jemalloc url = https://github.com/allisterb/jemalloc.git ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Allister Beharry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # jemalloc.NET: A native memory manager for .NET ![jembench](https://lh3.googleusercontent.com/QtV_Eviddx-ORMhVoU7L6N5aC5t8AQKqq_Un-GN8vtpAal2myXTM8G8zWnQUnV9STwtu7UewFzgNU4v2bjZHskicjV5xFFO_078aIJu842O3fbEZ4kD1jjQ5ffIArPafQ_860zxRaw=w1224-h693-no) Get the latest 0.2.x release from the [releases](https://github.com/allisterb/jemalloc.NET/releases) page. jemalloc.NET is a .NET API over the [jemalloc](http://jemalloc.net/) native memory allocator and provides .NET applications with efficient data structures backed by native memory for large scale in-memory computation scenarios. jemalloc is "a general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support" that is [widely used](https://github.com/jemalloc/jemalloc/wiki/Background#adoption) in the industry, particularly in applications that must [scale and utilize](http://highscalability.com/blog/2015/3/17/in-memory-computing-at-aerospike-scale-when-to-choose-and-ho.html) large amounts of memory. In addition to its fragmentation and concurrency optimizations, jemalloc provides an array of developer options for debugging, monitoring and tuning allocations that makes it a great choice for use in developing memory-intensive applications. The jemalloc.NET project provides: * A low-level .NET API over the native jemalloc API functions like je_malloc, je_calloc, je_free, je_mallctl... * A safety-focused high-level .NET API providing data structures like arrays backed by native memory allocated using jemalloc together with management features like reference counting and acceleration features like SIMD vectorized operations via the `Vector` .NET type. * A benchmark CLI program: `jembench` which uses the excellent [BenchmarkDotNet](http://benchmarkdotnet.org/index.htm) library for easy and accurate benchmarking operations on native data structures vs managed objects using different parameters. The high-level .NET API makes use of newly introduced C# and .NET features and classes like ref returns, `Span`, `Vector`, and `Unsafe` from the `System.Runtime.CompilerServices.Unsafe` libraries, for working effectively with pointers to managed and unmanaged memory. Data structures provided by the high-level API are more efficient than managed .NET arrays and objects at the scale of millions of elements, and memory allocation is much more resistant to fragmentation, while still providing necessary safety features like array bounds checking. Large .NET arrays must be allocated on the Large Object Heap and are not relocatable which leads to fragmentation and lower performance. In a [benchmark](https://github.com/allisterb/jemalloc.NET/blob/5d00a6969faf828261ce8f28492b0df2e7297dad/jemalloc.Benchmarks/Benchmarks/MallocVsArray.cs#L136) specifically designed to fragment managed and unmanaged memory using arrays of `Int32`, jemalloc.NET performs a order of magnitude better that the .NET managed heap allocator at the cost of higher memory utilization: ``` ini BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726) Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8 Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC .NET Core SDK=2.1.2 [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT Job=JemBenchmark Jit=RyuJit Platform=X64 Runtime=Core AllowVeryLargeObjects=True Toolchain=InProcessToolchain RunStrategy=ColdStart ``` | Method | Parameter | Mean | Error | StdDev | PrivateMemory | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated | |--------------------------------------------------------------------------------------------- |---------- |----------:|----------:|----------:|--------------:|-------:|---------:|------------:|------------:|------------:|------------:| | 'Run an allocation pattern that won't fragment the Large Object Heap.' | 100 | 763.74 ms | 7.536 ms | 7.049 ms | 42.1 MB | 1.00 | 0.00 | 101000.0000 | 101000.0000 | 101000.0000 | 45436.08 MB | | 'Run an allocation pattern that fragments the Large Object Heap.' | 100 | 786.95 ms | 15.568 ms | 19.689 ms | 76.0 MB | 1.03 | 0.03 | 101000.0000 | 101000.0000 | 101000.0000 | 45436.09 MB | | 'Run an allocation pattern that fragments the Large Object Heap with regular GC compaction.' | 100 | 790.30 ms | 15.392 ms | 19.466 ms | 75.9 MB | 1.03 | 0.03 | 111000.0000 | 111000.0000 | 111000.0000 | 45436.08 MB | | 'Run an allocation pattern that won't fragment the unmanaged heap.' | 100 | 59.63 ms | 2.205 ms | 6.502 ms | 52.6 MB | 0.08 | 0.01 | 1000.0000 | 1000.0000 | 1000.0000 | 1.97 MB | | 'Run an allocation pattern that fragments the unmanaged heap.' | 100 | 69.48 ms | 1.376 ms | 2.810 ms | 90.7 MB | 0.09 | 0.00 | 1000.0000 | 1000.0000 | 1000.0000 | 2.59 MB | You can run this benchmark with the jembench command: `jembench malloc --fragment 100 -u -b --cold-start` In the following `jembench` benchmark, simply filling an array is more or less the same across different kinds of memory and scales linearly depending on the size of the array, but *allocating* and filling a `UInt64[]` managed array of size 10000000 and 100000000 is more than 2x slower than using an equivalent native array provided by jemalloc.NET: ``` ini BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726) Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8 Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC .NET Core SDK=2.1.2 [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT Job=JemBenchmark Jit=RyuJit Platform=X64 Runtime=Core AllowVeryLargeObjects=True Toolchain=InProcessToolchain RunStrategy=Throughput ``` | Method | Parameter | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | |-------------------------------------------------------------------------------- |---------- |-----------:|----------:|----------:|-----------:|---------:|---------:|---------:|------------:| | **'Fill a managed array with a single value.'** | **10000000** | **9.059 ms** | **0.1745 ms** | **0.4777 ms** | **8.913 ms** | **-** | **-** | **-** | **208 B** | | 'Fill a SafeArray on the system unmanaged heap with a single value.' | 10000000 | 8.715 ms | 0.1682 ms | 0.2466 ms | 8.623 ms | - | - | - | 208 B | | 'Create and Fill a managed array with a single value.' | 10000000 | 32.867 ms | 0.9156 ms | 1.3420 ms | 32.175 ms | 142.8571 | 142.8571 | 142.8571 | 80000769 B | | 'Create and Fill a SafeArray on the system unmanaged heap with a single value.' | 10000000 | 13.809 ms | 0.2679 ms | 0.2506 ms | 13.727 ms | - | - | - | 192 B | | **'Fill a managed array with a single value.'** | **100000000** | **90.326 ms** | **1.7718 ms** | **2.4253 ms** | **89.468 ms** | **-** | **-** | **-** | **208 B** | | 'Fill a SafeArray on the system unmanaged heap with a single value.' | 100000000 | 88.377 ms | 0.9775 ms | 0.8665 ms | 88.505 ms | - | - | - | 208 B | | 'Create and Fill a managed array with a single value.' | 100000000 | 310.880 ms | 5.9732 ms | 8.1762 ms | 306.952 ms | 125.0000 | 125.0000 | 125.0000 | 800000624 B | | 'Create and Fill a SafeArray on the system unmanaged heap with a single value.' | 100000000 | 137.288 ms | 0.9710 ms | 0.9083 ms | 137.111 ms | - | - | - | 192 B | You can run this benchmark with the command `jembench array --fill 10000000 100000000 -l -u`. In this case we see that using the managed array of size 10 million elements allocated 800 MB on the managed heap while using the native array did not cause any allocations on the managed heap for the array data. Avoiding the managed heap for very large but simple data structures like arrays is a key optimizarion for apps that do large-scale in-memory computation. Managed .NET arays are also limited to `Int32` indexing and a maximum size of about 2.15 billion elements. jemalloc.NET provides huge arrays through the `HugeArray` class which allows you to access all available memory as a flat contiguous buffer using array semantics. In the next benchmark `jembench hugearray --fill -i 4200000000`: ``` ini BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726) Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8 Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC .NET Core SDK=2.1.2 [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT Job=JemBenchmark Jit=RyuJit Platform=X64 Runtime=Core AllowVeryLargeObjects=True Toolchain=InProcessToolchain RunStrategy=ColdStart TargetCount=7 WarmupCount=-1 ``` | Method | Parameter | Mean | Error | StdDev | Allocated | |------------------------------------------------------------------------------- |----------- |--------:|---------:|---------:|-------------:| | 'Fill a managed array with the maximum size [2146435071] with a single value.' | 4200000000 | 3.177 s | 0.1390 s | 0.0617 s | 8585740456 B | | 'Fill a HugeArray on the system unmanaged heap with a single value.' | 4200000000 | 4.029 s | 3.2233 s | 1.4312 s | 0 B | an `Int32[]` of maximum size can be allocated and filled in 3.2s. This array consumes 8.6GB on the managed heap. But a jemalloc.NET `HugeArray` of nearly double the size at 4.2 billion elements can be allocated in only 4 s and again consumes no memory on the managed heap. The only limit on the size of a `HugeArray` is the available system memory. Perhaps the killer feature of the [recently introduced](https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/) `Span` class in .NET is its ability to efficiently zero-copy reinterpret numeric data structures (`Int32, Int64` and their siblings) into other structures like the `Vector` SIMD-enabled data types introduced in 2016. `Vector` types are special in that the .NET RyuJIT compiler can compile operations on Vectors to use SIMD instructions like SSE, SSE2, and AVX for parallelizing operations on data on a single CPU core. Using the SIMD-enabled `SafeBuffer.VectorMultiply(n)` method provided by the jemalloc.NET API yields a more than 12x speedup for a simple in-place multiplication of a `UInt64[]` array of 10 million elements, compared to the unoptimized linear approach, allowing the operation to complete in 60 ms: ``` ini BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726) Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8 Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC .NET Core SDK=2.1.2 [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT Job=JemBenchmark Jit=RyuJit Platform=X64 Runtime=Core AllowVeryLargeObjects=True Toolchain=InProcessToolchain RunStrategy=Throughput ``` | Method | Parameter | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated | |-------------------------------------------------------------------- |---------- |----------:|----------:|---------:|------------:|--------:|------------:| | 'Multiply all values of a managed array with a single value.' | 10000000 | 761.10 ms | 10.367 ms | 9.190 ms | 254250.0000 | 62.5000 | 800000304 B | | 'Vector multiply all values of a native array with a single value.' | 10000000 | 59.23 ms | 1.170 ms | 1.149 ms | - | - | 360 B | For huge arrays of `UInt16[]` we see similar speedups: ``` ini BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726) Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8 Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC .NET Core SDK=2.1.2 [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT Job=JemBenchmark Jit=RyuJit Platform=X64 Runtime=Core AllowVeryLargeObjects=True Toolchain=InProcessToolchain RunStrategy=ColdStart TargetCount=1 ``` | Method | Parameter | Mean | Error | Gen 0 | Gen 1 | Allocated | |------------------------------------------------------------------------------------------------- |----------- |--------:|------:|--------------:|----------:|--------------:| | 'Multiply all values of a managed array with the maximum size [2146435071] with a single value.' | 4096000000 | 34.25 s | NA | 16375000.0000 | 3000.0000 | 51514441704 B | | 'Vector multiply all values of a native array with a single value.' | 4096000000 | 12.06 s | NA | - | - | 0 B | For a huge array with 4.1 billion `UInt16` values it takes 12 seconds to do a SIMD-enabled multiplication operation on all the elements of the array. This is still 3x the performance of doing the same non-vectorized operation on a managed array of half the size. Inside a .NET application, jemalloc.NET native arrays and data structures can be straightforwardly accessed by native libraries without the need to make additional copies or allocations. The goal of the jemalloc.NET project is to make accessible to .NET the kind of big-data in-memory numeric, scientific and other computing that typically would require coding in a low-level language like C/C++ or assembly. ## How it works Memory allocations are made using the jemalloc C functions like `je_malloc`, `je_calloc` etc, and returned as `IntPtr` pointers. Memory alignment is handled by jemalloc and memory allocations (called extents) are aligned to a multiple of the jemalloc page size e.g 4K. Inside extents, elements are aligned according to the .NET struct alignment specified using the `StructLayout` and other attributes. In the future the ability to manually align structures inside an extend and jemalloc's ability to manually align each extent (e.g using the `je_posix_memalign` function) may be exposed to alleviate potential [performance problems](http://adrianchadd.blogspot.se/2015/03/cache-line-aliasing-2-or-what-happens.html) with the default jemalloc page alignment. Each allocation pointer is tracked in a thread-safe allocations ledger together with a reference count. Each valid Jem.NET data structure has a pointer in the allocations ledger. There are other allocation ledgers that track details of different data structures like `FixedBufferAllocation`. When a pointer to an extent is released by a data stucture it is removed from all allocation ledgers. During the free operation the memory extent is filled with junk values by jemalloc as a safety feature however this feature and many other aspects of jemalloc can be turned off or on by the user. Any attempt to read or write data structure memory locations is guarded by checking that the data structure owns a valid pointer to the memory location. Since there can't be two pointers allocated with the same address at the same time, pointers act as a unique Id for tracking the active memory extents. Data structures are provided with reference counting methods like `Acquire()` and `Release()` to manage their lifetimes. Attempts to free or dispose of a data structure with a non-zero reference count will throw an exception. Unlike the stack-only `Span` you are allowed to use class fields and properties to hold Jem.NET data structures like `FixedBuffer` because `FixedBuffer` guards every access to memory by first checking if the memory extent it is assigned is still valid. However you must take responsibility for using the reference counting features correctly i.e when sharing data structures between methods and threads you should always call `FixedBuffer`.Acquire() to prevent a nethod from accidentally releasing the buffer's memory when it is in use. In data structures like `FixedBuffer` the primitive .NET types are always correctly aligned for SIMD vectorized operations and such operations like `FixedBuffer.VectorMultiply()` can be significatnly faster than the non-optimized variants. E.g. an in-place multiplication of a `FixedBuffer` is many times faster than the same operation on a .NET `Uint16[]`: ``` ini BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726) Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8 Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC .NET Core SDK=2.1.2 [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT Job=JemBenchmark Jit=RyuJit Platform=X64 Runtime=Core AllowVeryLargeObjects=True Toolchain=InProcessToolchain RunStrategy=Throughput ``` | Method | Parameter | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated | |-------------------------------------------------------------------- |---------- |----------:|----------:|----------:|------------:|-------:|------------:| | 'Multiply all values of a managed array with a single value.' | 10000000 | 606.33 ms | 4.1602 ms | 3.4740 ms | 228875.0000 | 4.1667 | 703125.3 KB | | 'Vector multiply all values of a native array with a single value.' | 10000000 | 36.40 ms | 0.5256 ms | 0.4659 ms | - | - | 1.32 KB | Pointers to data structure memory extents can be passed directly to unmanaged functions and libraries without the need for marshalling or more allocations. This is important when you want to build high-performance apps that use libraries written in C or assembly that process large amounts of in-memory data. ## Installation ### Requirements Currently only runs on 64bit Windows; support for Linux 64bit and other 64bit platforms supported by .NET Core will be added soon. #### Windows * The latest [.NET Core 2.0 x64 runtime](https://www.microsoft.com/net/download/thank-you/dotnet-runtime-2.0.3-windows-x64-installer) * The latest version of the [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572) ### Steps Grab the latest release from the [releases](https://github.com/allisterb/jemalloc.NET/releases) page and unzip to a folder. Type `jembench` to run the benchmark CLI program and you should see the program version and options printed. NuGet packagees can be found in x64\Release. The API library assembly files themselves are in x64\Release\netstandard2.0 Note that if using jemalloc.NET in your own projects you must put the native jemallocd.dll library somewhere where it can be located by the .NET runtime. You can create a post-build step to copy it to the output folder of your project or put it somewhere on your %PATH%. ## Usage ### High-level API Currently there are 4 implemented native memory data structures: ### 1. `FixedBuffer` `FixedBuffer` is a fixed-length array of data backed by memory on the unmanaged heap, that is implemented as .NET `struct`. The underlying data type of a `FixedBuffer` must be a [primitive](https://msdn.microsoft.com/en-us/library/system.type.isprimitive.aspx) type: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, or Single. ``` FixedBuffer buffer = new FixedBuffer(1000000); // Create an array of 1 million Int64 integers. buffer.Fill(100); long a = buffer[10]; //copy on read ref long b = ref buffer[10]; // no copy...b is an alias to the memory location of buffer[10] ``` Primitives types have a maximum width of 64bits and (assuming correct memory alignment) can be read and updated atomically on machines with a 64bit word-size. This eliminates the possibility of 'struct tearing', or struct fields being read or written to inconsistently by multiple concurrent operations, because reads and writes to `FixedBuffer` elements are only done directly to the underlying memory location for the data element. All properties and fields of `FixedBuffer` are readonly with only the index operator `[]` returing a `ref T` to the underlying data element. To use copy-on-read semantics simply create and assign variables to elements in the array. Use `ref` variables to get an alias to the memory location itself: FixedBuffer b = ...; int num = b[15]; num = 6; // b[15] is still 6 ref int r = ref b[15]; //get an alias to b[15] r = 23; // b[15] is now Although `FixedBuffer` is a .NET value type, only the metadata about the data structure is copied from one variable assignment to another. The actual data of a `FixedBuffer` resides at a single memory location only. Each data access to a `` element in a `FixedBuffer` from all copies of a `FixedBuffer` will read and write the same memory and makes the same guard checks against the same pointer in the allocations ledger. When the pointer used by a FixedBuffer is released then every copy of a `FixedBuffer` automatically becomes invalid. e.g: ``` FixedBuffer array = new FixedBuffer(100); array[16] = 1; FixedBuffer copy = array; int a = copy[16]; // a is 16; copy[5] = 99; int b = array[5]; // b is 99 copy.Free(); Invalidate the buffer from the copy int c = array[16]; //InvalidOperationException thrown here ``` You can create user-defined structures that contain `FixedBuffer` e.g public struct Employee : IEquatable { ... public DateTime? DOB { get; set; } public decimal Balance { get; set; } public FixedBuffer BonusPayments { get; set; } public FixedBuffer Photo { get; set; } //8 } The `FixedUtf8String` [type](https://github.com/allisterb/jemalloc.NET/blob/master/jemalloc.Api/FixedUtf8String.cs) is another `struct` that uses a `FixedBuffer` for storage of string data. ``` public readonly struct FixedUtf8String : IEquatable, IRetainable, IDisposable { ... #region Fields private readonly FixedBuffer buffer; private const int StringNotFound = -1; ... ``` Each `FixedBuffer` has a constructor from `T[]` which copies the data from the managed array to the unmanaged buffer allocated. ``` public static TestUDT MakeTestRecord(Random rng) { Guid _id = Guid.NewGuid(); string _ids = _id.ToString("N"); byte[] photo = new byte[4096]; rng.NextBytes(photo); return new TestUDT() { ID = _id, FirstName = new FixedUtf8String("Gavial-" + _id.ToString("D").Substring(0, 4)), LastName = new FixedUtf8String("Buxarinovich-" + _id.ToString("D").Substring(0, 4)), DOB = _ids.StartsWith("7") ? (DateTime?)null : DateTime.UtcNow, Balance = 2131m, Photo = new FixedBuffer(photo) }; } ``` Arrays of these kinds of user-defined structures can also be stored in native memory using the `SafeBuffer` class. ### 2. `SafeBuffer` `SafeBuffer` is a fixed-length array of data backed by memory on the unmanaged heap that inherits from the .NET `SafeHandle` class and is implemented as .NET `class` i.e a reference type. `SafeBuffer` is an abstract class that can be used as the base to create data-structures like arrays or trees that are backed by unmanaged memory. The implemented `SafeArray` class is the simplest concrete class that derives from `SafeBuffer`. `SafeArray` can contain both primitive types and user-defined structures e.g: ``` public struct Employee : IEquatable { public Guid ID { get; set; } public FixedUtf8String FirstName { get; set; } public FixedUtf8String LastName { get; set; } public DateTime? DOB { get; set; } public decimal Salary { get; set; } public FixedBuffer Bonuses { get; set; } public FixedBuffer Photo { get; set; } } ... public SafeArray employees = new SafeArray(100); ``` The only restrictions on the type `T` of a `SafeBuffer` is that it must be a struct and it must implement `IEquatable` ### 3. `FixedUtf8Buffer` `FixedUtf8Buffer`is a fixed-length immutable string type using UTF-8 encoding that is backed by memory on the unmanaged heap and is implemented as a .NET `struct` You can create user-defined structures that contain a `FixedUtf8Buffer` or create a `SafeBuffer` for representing arrays of stringd. Some commmon string operations are implemented like `IndexOf()` and `Substring()` and there is a constructor taking a .NET `String`. ### 4. `HugeBuffer` `HugeBuffer` is similar to `SafeBuffer` but with the ability to store and index up to `Int64.MaxValue` array elements. `HugeBuffer` lets you treat all available memory as a linear contiguous buffer. ### Tuning jemalloc The jemalloc allocator contains many parameters that can be adjusted to suit the workload and allocation patterns of the application. `Jem.Init(string conf)` `Jem.SetMallCtlInt32(int value)` ### jembench CLI Examples: * `jembench hugearray -l -u --math --cold-start -t 3 4096000000` Benchmark math operations on `HugeArray` arrays of size 4096000000 without benchmark warmup and only using 3 iterations of the target methods. Benchmarks on huge arrays can be lengthy so you should carefully choose the benchmark parameters affecting how long you want the benchmark to run, ## Building from source Currently build instuctions are only provided for Visual Studio 2017 on Windows but instructions for building on Linux will also be provided. jemalloc.NET is a 64-bit library only. ### Requirements [Visual Studio 2017 15.5](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes#15.5.1) with at least the following components: * C# 7.2 compiler * .NET Core 2.x SDK x64 * MSVC 2017 compiler toolset v141 or higher * Windows 10 SDK for Desktop C++ version 10.0.10.15603 or higher. Note that if you only have higher versions installed you will need to retarget the jemalloc MSVC project to your SDK version from Visual Studio. Per the instructions for building the native jemalloc library for Windows, you will also need Cygwin (32- or 64-bit )with the following packages: * autoconf * autogen * gcc * gawk * grep * sed Cygwin tools aren't actually used for compiling jemalloc but for generating the header files. jemalloc on Windows is built using MSVC. ### Steps 0. You must add the [.NET Core](https://dotnet.myget.org/gallery/dotnet-core) NuGet [feed](https://dotnet.myget.org/F/dotnet-core/api/v3/index.json) on MyGet and also the [CoreFxLab](https://dotnet.myget.org/gallery/dotnet-corefxlab) [feed](https://dotnet.myget.org/F/dotnet-core/api/v3/index.json) to your NuGet package sources. You can do this in Visual Studio 2017 from Tools->Options->NuGet Package Manager menu item. 1. Clone the project: `git clone https://github.com/alllisterb/jemalloc.NET` and init the submodules: `git submodule update --init --recursive` 2. Open a x64 Native Tools Command Prompt for VS 2017 and temporarily add `Cygwin\bin` to the PATH e.g `set PATH=%PATH%;C:\cygwin\bin`. Switch to the `jemalloc` subdirectory in your jemalloc.NET solution dir and run `sh -c "CC=cl ./autogen.sh"`. This will generate some files in the `jemalloc` subdirectory and only needs to be done once. 4. From a Visual Studio 2017 Developer Command prompt run `build.cmd`. Alternatively you can load the solution in Visual Studio and using the "Benchmark" solution configuration build the entire solution. 5. The solution should build without errors. 6. Run `jembench` from the solution folder to see the project version and help. ================================================ FILE: build.cmd ================================================ @echo off @setlocal set ERROR_CODE=0 dotnet restore jemalloc.NET.sln if not %ERRORLEVEL%==0 ( echo Error restoring NuGet packages for jemalloc.NET.sln. set ERROR_CODE=1 goto End ) if [%1]==[] ( msbuild jemalloc.NET.sln /p:Configuration=Benchmark /p:Platform=x64 ) else ( msbuild jemalloc.NET.sln /p:Configuration=Benchmark /p:Platform=x64;%* ) if not %ERRORLEVEL%==0 ( echo Error building jemalloc.NET.sln. set ERROR_CODE=2 goto End ) :End @endlocal exit /B %ERROR_CODE% ================================================ FILE: jemalloc.Api/Buffer.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; namespace jemalloc { public ref struct Buffer where T : struct { public Buffer(int length) { _Ptr = IntPtr.Zero; _Length = 0; _SizeInBytes = 0; _Timestamp = 0; _Span = new Span(); unsafe { _VoidPointer = (void*)_Ptr; } Allocate(length); } #region Properties public bool IsInvalid { get { return _Ptr == IntPtr.Zero; } } public int Length { get { if (IsInvalid) { return 0; } else { return _Length; } } } public ulong Size { get { if (IsInvalid) { return 0; } else { return _SizeInBytes; } } } public IntPtr Ptr { get { return _Ptr; } } public Span Span { get { if (IsInvalid) { throw new InvalidOperationException("The buffer is invalid."); } else { return _Span; } } } #endregion #region Methods private unsafe bool Allocate(int length) { _Ptr = Jem.Calloc((ulong) length, ElementSizeInBytes); if (_Ptr != IntPtr.Zero) { _Length = length; _SizeInBytes = (ulong)_Length * ElementSizeInBytes; _Timestamp = DateTime.Now.Ticks; _VoidPointer = _Ptr.ToPointer(); _Span = new Span(_VoidPointer, _Length); return true; } else return false; } public void Release() { if (IsInvalid) { return; } else if (Interlocked.Exchange(ref _Ptr, IntPtr.Zero) != IntPtr.Zero) { Jem.Free(_Ptr); } } public void Fill(T value) { if (IsInvalid) throw new InvalidOperationException("The buffer is invalid."); Span.Fill(value); } #endregion public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (IsInvalid) throw new InvalidOperationException("The buffer is invalid."); if (index >= (Length)) throw new IndexOutOfRangeException(); unsafe { return ref Unsafe.Add(ref Unsafe.AsRef(_VoidPointer), index); } } } #region Fields private static readonly Type ElementType = typeof(T); private static readonly ulong ElementSizeInBytes = (ulong) JemUtil.SizeOfStruct(); private ulong _SizeInBytes; private int _Length; private IntPtr _Ptr; private unsafe void* _VoidPointer; private long _Timestamp; private Span _Span; #endregion } } ================================================ FILE: jemalloc.Api/BufferHelpers.cs ================================================ //Based on SpanHelpers.cs // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; namespace jemalloc { internal static partial class BufferHelpers { public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); if (valueLength == 0) return 0; // A zero-length sequence is always treated as "found" at the start of the search space. byte valueHead = value; ref byte valueTail = ref Unsafe.Add(ref value, 1); int valueTailLength = valueLength - 1; int index = 0; for (; ; ) { Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; if (remainingSearchSpaceLength <= 0) break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. // Do a quick search for the first element of "value". int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength); if (relativeIndex == -1) break; index += relativeIndex; // Found the first element of "value". See if the tail matches. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength)) return index; // The tail matched. Return a successful find. index++; } return -1; } public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); if (valueLength == 0) return 0; // A zero-length sequence is always treated as "found" at the start of the search space. int index = -1; for (int i = 0; i < valueLength; i++) { var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); if (tempIndex != -1) { index = (index == -1 || index > tempIndex) ? tempIndex : index; } } return index; } private const ulong XorPowerOfTwoToHighByte = (0x07ul | 0x06ul << 8 | 0x05ul << 16 | 0x04ul << 24 | 0x03ul << 32 | 0x02ul << 40 | 0x01ul << 48) + 1; public static int IndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); if (valueLength == 0) return 0; // A zero-length sequence is always treated as "found" at the start of the search space. T valueHead = value; ref T valueTail = ref Unsafe.Add(ref value, 1); int valueTailLength = valueLength - 1; int index = 0; for (;;) { Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; if (remainingSearchSpaceLength <= 0) break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. // Do a quick search for the first element of "value". int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength); if (relativeIndex == -1) break; index += relativeIndex; // Found the first element of "value". See if the tail matches. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength)) return index; // The tail matched. Return a successful find. index++; } return -1; } public static unsafe int IndexOf(ref T searchSpace, T value, int length) where T : IEquatable { Debug.Assert(length >= 0); IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations while (length >= 8) { length -= 8; if (value.Equals(Unsafe.Add(ref searchSpace, index))) goto Found; if (value.Equals(Unsafe.Add(ref searchSpace, index + 1))) goto Found1; if (value.Equals(Unsafe.Add(ref searchSpace, index + 2))) goto Found2; if (value.Equals(Unsafe.Add(ref searchSpace, index + 3))) goto Found3; if (value.Equals(Unsafe.Add(ref searchSpace, index + 4))) goto Found4; if (value.Equals(Unsafe.Add(ref searchSpace, index + 5))) goto Found5; if (value.Equals(Unsafe.Add(ref searchSpace, index + 6))) goto Found6; if (value.Equals(Unsafe.Add(ref searchSpace, index + 7))) goto Found7; index += 8; } if (length >= 4) { length -= 4; if (value.Equals(Unsafe.Add(ref searchSpace, index))) goto Found; if (value.Equals(Unsafe.Add(ref searchSpace, index + 1))) goto Found1; if (value.Equals(Unsafe.Add(ref searchSpace, index + 2))) goto Found2; if (value.Equals(Unsafe.Add(ref searchSpace, index + 3))) goto Found3; index += 4; } while (length > 0) { if (value.Equals(Unsafe.Add(ref searchSpace, index))) goto Found; index += 1; length--; } return -1; Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return (int)(byte*)index; Found1: return (int)(byte*)(index + 1); Found2: return (int)(byte*)(index + 2); Found3: return (int)(byte*)(index + 3); Found4: return (int)(byte*)(index + 4); Found5: return (int)(byte*)(index + 5); Found6: return (int)(byte*)(index + 6); Found7: return (int)(byte*)(index + 7); } public static bool SequenceEqual(ref T first, ref T second, int length) where T : IEquatable { Debug.Assert(length >= 0); if (Unsafe.AreSame(ref first, ref second)) goto Equal; IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations while (length >= 8) { length -= 8; if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index))) goto NotEqual; if (!Unsafe.Add(ref first, index + 1).Equals(Unsafe.Add(ref second, index + 1))) goto NotEqual; if (!Unsafe.Add(ref first, index + 2).Equals(Unsafe.Add(ref second, index + 2))) goto NotEqual; if (!Unsafe.Add(ref first, index + 3).Equals(Unsafe.Add(ref second, index + 3))) goto NotEqual; if (!Unsafe.Add(ref first, index + 4).Equals(Unsafe.Add(ref second, index + 4))) goto NotEqual; if (!Unsafe.Add(ref first, index + 5).Equals(Unsafe.Add(ref second, index + 5))) goto NotEqual; if (!Unsafe.Add(ref first, index + 6).Equals(Unsafe.Add(ref second, index + 6))) goto NotEqual; if (!Unsafe.Add(ref first, index + 7).Equals(Unsafe.Add(ref second, index + 7))) goto NotEqual; index += 8; } if (length >= 4) { length -= 4; if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index))) goto NotEqual; if (!Unsafe.Add(ref first, index + 1).Equals(Unsafe.Add(ref second, index + 1))) goto NotEqual; if (!Unsafe.Add(ref first, index + 2).Equals(Unsafe.Add(ref second, index + 2))) goto NotEqual; if (!Unsafe.Add(ref first, index + 3).Equals(Unsafe.Add(ref second, index + 3))) goto NotEqual; index += 4; } while (length > 0) { if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index))) goto NotEqual; index += 1; length--; } Equal: return true; NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return false; } /// /// Implements the copy functionality used by Span and ReadOnlySpan. /// /// NOTE: Fast span implements TryCopyTo in corelib and therefore this implementation /// is only used by portable span. The code must live in code that only compiles /// for portable span which means either each individual span implementation /// of this shared code file. Other shared SpanHelper.X.cs files are compiled /// for both portable and fast span implementations. /// public static unsafe void CopyTo(ref T dst, int dstLength, ref T src, int srcLength) { Debug.Assert(dstLength != 0); IntPtr srcByteCount = Unsafe.ByteOffset(ref src, ref Unsafe.Add(ref src, srcLength)); IntPtr dstByteCount = Unsafe.ByteOffset(ref dst, ref Unsafe.Add(ref dst, dstLength)); IntPtr diff = Unsafe.ByteOffset(ref src, ref dst); bool isOverlapped = (sizeof(IntPtr) == sizeof(int)) ? (uint)diff < (uint)srcByteCount || (uint)diff > (uint)-(int)dstByteCount : (ulong)diff < (ulong)srcByteCount || (ulong)diff > (ulong)-(long)dstByteCount; if (!isOverlapped && !BufferHelpers.IsReferenceOrContainsReferences()) { ref byte dstBytes = ref Unsafe.As(ref dst); ref byte srcBytes = ref Unsafe.As(ref src); ulong byteCount = (ulong)srcByteCount; ulong index = 0; while (index < byteCount) { uint blockSize = (byteCount - index) > uint.MaxValue ? uint.MaxValue : (uint)(byteCount - index); Unsafe.CopyBlock( ref Unsafe.Add(ref dstBytes, (IntPtr)index), ref Unsafe.Add(ref srcBytes, (IntPtr)index), blockSize); index += blockSize; } } else { bool srcGreaterThanDst = (sizeof(IntPtr) == sizeof(int)) ? (uint)diff > (uint)-(int)dstByteCount : (ulong)diff > (ulong)-(long)dstByteCount; int direction = srcGreaterThanDst ? 1 : -1; int runCount = srcGreaterThanDst ? 0 : srcLength - 1; int loopCount = 0; for (; loopCount < (srcLength & ~7); loopCount += 8) { Unsafe.Add(ref dst, runCount + direction * 0) = Unsafe.Add(ref src, runCount + direction * 0); Unsafe.Add(ref dst, runCount + direction * 1) = Unsafe.Add(ref src, runCount + direction * 1); Unsafe.Add(ref dst, runCount + direction * 2) = Unsafe.Add(ref src, runCount + direction * 2); Unsafe.Add(ref dst, runCount + direction * 3) = Unsafe.Add(ref src, runCount + direction * 3); Unsafe.Add(ref dst, runCount + direction * 4) = Unsafe.Add(ref src, runCount + direction * 4); Unsafe.Add(ref dst, runCount + direction * 5) = Unsafe.Add(ref src, runCount + direction * 5); Unsafe.Add(ref dst, runCount + direction * 6) = Unsafe.Add(ref src, runCount + direction * 6); Unsafe.Add(ref dst, runCount + direction * 7) = Unsafe.Add(ref src, runCount + direction * 7); runCount += direction * 8; } if (loopCount < (srcLength & ~3)) { Unsafe.Add(ref dst, runCount + direction * 0) = Unsafe.Add(ref src, runCount + direction * 0); Unsafe.Add(ref dst, runCount + direction * 1) = Unsafe.Add(ref src, runCount + direction * 1); Unsafe.Add(ref dst, runCount + direction * 2) = Unsafe.Add(ref src, runCount + direction * 2); Unsafe.Add(ref dst, runCount + direction * 3) = Unsafe.Add(ref src, runCount + direction * 3); runCount += direction * 4; loopCount += 4; } for (; loopCount < srcLength; ++loopCount) { Unsafe.Add(ref dst, runCount) = Unsafe.Add(ref src, runCount); runCount += direction; } } } /// /// Computes "start + index * sizeof(T)", using the unsigned IntPtr-sized multiplication for 32 and 64 bits. /// /// Assumptions: /// Start and index are non-negative, and already pre-validated to be within the valid range of their containing Span. /// /// If the byte length (Span.Length * sizeof(T)) does an unsigned overflow (i.e. the buffer wraps or is too big to fit within the address space), /// the behavior is undefined. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IntPtr Add(this IntPtr start, int index) { Debug.Assert(start.ToInt64() >= 0); Debug.Assert(index >= 0); unsafe { if (sizeof(IntPtr) == sizeof(int)) { // 32-bit path. uint byteLength = (uint)index * (uint)Unsafe.SizeOf(); return (IntPtr)(((byte*)start) + byteLength); } else { // 64-bit path. ulong byteLength = (ulong)index * (ulong)Unsafe.SizeOf(); return (IntPtr)(((byte*)start) + byteLength); } } } /// /// Determine if a type is eligible for storage in unmanaged memory. /// Portable equivalent of RuntimeHelpers.IsReferenceOrContainsReferences{T}() /// public static bool IsReferenceOrContainsReferences() => PerTypeValues.IsReferenceOrContainsReferences; private static bool IsReferenceOrContainsReferencesCore(Type type) { if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references. return false; if (!type.GetTypeInfo().IsValueType) return true; // If type is a Nullable<> of something, unwrap it first. Type underlyingNullable = Nullable.GetUnderlyingType(type); if (underlyingNullable != null) type = underlyingNullable; if (type.GetTypeInfo().IsEnum) return false; foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields) { if (field.IsStatic) continue; if (IsReferenceOrContainsReferencesCore(field.FieldType)) return true; } return false; } public static bool IsReferenceOrContainsReferences(Type type, out FieldInfo referenceField) { referenceField = null; if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references. return false; if (!type.GetTypeInfo().IsValueType) return true; // If type is a Nullable<> of something, unwrap it first. Type underlyingNullable = Nullable.GetUnderlyingType(type); if (underlyingNullable != null) type = underlyingNullable; if (type.GetTypeInfo().IsEnum) return false; foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields) { if (field.IsStatic) continue; if (IsReferenceOrContainsReferences(field.FieldType, out referenceField)) { referenceField = field; return true; } } return false; } public static class PerTypeValues { // // Latch to ensure that excruciatingly expensive validation check for constructing a Span around a raw pointer is done // only once per type. // public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferencesCore(typeof(T)); public static readonly T[] EmptyArray = new T[0]; public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); // Array header sizes are a runtime implementation detail and aren't the same across all runtimes. (The CLR made a tweak after 4.5, and Mono has an extra Bounds pointer.) private static IntPtr MeasureArrayAdjustment() { T[] sampleArray = new T[1]; return Unsafe.ByteOffset(ref Unsafe.As>(sampleArray).Data, ref sampleArray[0]); } } public unsafe static void ClearLessThanPointerSized(byte* ptr, UIntPtr byteLength) { if (sizeof(UIntPtr) == sizeof(uint)) { Unsafe.InitBlockUnaligned(ptr, 0, (uint)byteLength); } else { // PERF: Optimize for common case of length <= uint.MaxValue ulong bytesRemaining = (ulong)byteLength; uint bytesToClear = (uint)(bytesRemaining & uint.MaxValue); Unsafe.InitBlockUnaligned(ptr, 0, bytesToClear); bytesRemaining -= bytesToClear; ptr += bytesToClear; // Clear any bytes > uint.MaxValue while (bytesRemaining > 0) { bytesToClear = (bytesRemaining >= uint.MaxValue) ? uint.MaxValue : (uint)bytesRemaining; Unsafe.InitBlockUnaligned(ptr, 0, bytesToClear); ptr += bytesToClear; bytesRemaining -= bytesToClear; } } } public static unsafe void ClearLessThanPointerSized(ref byte b, UIntPtr byteLength) { if (sizeof(UIntPtr) == sizeof(uint)) { Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength); } else { // PERF: Optimize for common case of length <= uint.MaxValue ulong bytesRemaining = (ulong)byteLength; uint bytesToClear = (uint)(bytesRemaining & uint.MaxValue); Unsafe.InitBlockUnaligned(ref b, 0, bytesToClear); bytesRemaining -= bytesToClear; long byteOffset = bytesToClear; // Clear any bytes > uint.MaxValue while (bytesRemaining > 0) { bytesToClear = (bytesRemaining >= uint.MaxValue) ? uint.MaxValue : (uint)bytesRemaining; ref byte bOffset = ref Unsafe.Add(ref b, (IntPtr)byteOffset); Unsafe.InitBlockUnaligned(ref bOffset, 0, bytesToClear); byteOffset += bytesToClear; bytesRemaining -= bytesToClear; } } } public unsafe static void ClearPointerSizedWithoutReferences(ref byte b, UIntPtr byteLength) { // TODO: Perhaps do switch casing to improve small size perf var i = IntPtr.Zero; while (i.LessThanEqual(byteLength - sizeof(Reg64))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = default(Reg64); i += sizeof(Reg64); } if (i.LessThanEqual(byteLength - sizeof(Reg32))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = default(Reg32); i += sizeof(Reg32); } if (i.LessThanEqual(byteLength - sizeof(Reg16))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = default(Reg16); i += sizeof(Reg16); } if (i.LessThanEqual(byteLength - sizeof(long))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = 0; i += sizeof(long); } // JIT: Should elide this if 64-bit if (sizeof(IntPtr) == sizeof(int)) { if (i.LessThanEqual(byteLength - sizeof(int))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = 0; i += sizeof(int); } } } public unsafe static void ClearPointerSizedWithReferences(ref IntPtr ip, UIntPtr pointerSizeLength) { // TODO: Perhaps do switch casing to improve small size perf var i = IntPtr.Zero; var n = IntPtr.Zero; while ((n = i + 8).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i + 0) = default(IntPtr); Unsafe.Add(ref ip, i + 1) = default(IntPtr); Unsafe.Add(ref ip, i + 2) = default(IntPtr); Unsafe.Add(ref ip, i + 3) = default(IntPtr); Unsafe.Add(ref ip, i + 4) = default(IntPtr); Unsafe.Add(ref ip, i + 5) = default(IntPtr); Unsafe.Add(ref ip, i + 6) = default(IntPtr); Unsafe.Add(ref ip, i + 7) = default(IntPtr); i = n; } if ((n = i + 4).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i + 0) = default(IntPtr); Unsafe.Add(ref ip, i + 1) = default(IntPtr); Unsafe.Add(ref ip, i + 2) = default(IntPtr); Unsafe.Add(ref ip, i + 3) = default(IntPtr); i = n; } if ((n = i + 2).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i + 0) = default(IntPtr); Unsafe.Add(ref ip, i + 1) = default(IntPtr); i = n; } if ((i + 1).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i) = default(IntPtr); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static bool LessThanEqual(this IntPtr index, UIntPtr length) { return (sizeof(UIntPtr) == sizeof(uint)) ? (int)index <= (int)length : (long)index <= (long)length; } [StructLayout(LayoutKind.Sequential, Size = 64)] private struct Reg64 { } [StructLayout(LayoutKind.Sequential, Size = 32)] private struct Reg32 { } [StructLayout(LayoutKind.Sequential, Size = 16)] private struct Reg16 { } } } ================================================ FILE: jemalloc.Api/Extensions/TypeExtensions.cs ================================================ /*Based on the CodeProject article by Yuri Astrakhan and Sasha Goldshtein: * https://www.codeproject.com/Articles/33382/Fast-Native-Structure-Reading-in-C-using-Dynamic-A **/ using System; using System.Collections.Generic; using System.Reflection; namespace jemalloc.Extensions { public static class TypeExtensions { /// /// Gets a value indicating whether a type (or type's element type) /// instance can be null in the underlying data store. /// /// A instance. /// True, if the type parameter is a closed generic nullable type; otherwise, False. /// Arrays of Nullable types are treated as Nullable types. public static bool IsNullable(this Type type) { while (type.IsArray) type = type.GetElementType(); return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>)); } /// /// Returns the underlying type argument of the specified type. /// /// A instance. /// /// The type argument of the type parameter, /// if the type parameter is a closed generic nullable type. /// The underlying Type if the type parameter is an enum type. /// Otherwise, the type itself. /// /// public static Type GetUnderlyingType(this Type type) { if (type == null) throw new ArgumentNullException("type"); if (type.IsNullable()) type = type.GetGenericArguments()[0]; if (type.IsEnum) type = Enum.GetUnderlyingType(type); return type; } /// /// Determines whether the specified types are considered equal. /// /// A instance. /// A type possible derived from the parent type /// True, when an object instance of the type child /// can be used as an object of the type parent; otherwise, false. /// Note that nullable types does not have a parent-child relation to it's underlying type. /// For example, the 'int?' type (nullable int) and the 'int' type /// aren't a parent and it's child. public static bool IsSameOrParent(Type parent, Type child) { if (parent == null) throw new ArgumentNullException("parent"); if (child == null) throw new ArgumentNullException("child"); if (parent == child || child.IsEnum && Enum.GetUnderlyingType(child) == parent || child.IsSubclassOf(parent)) { return true; } if (parent.IsInterface) { var interfaces = child.GetInterfaces(); foreach (var t in interfaces) if (t == parent) return true; } return false; } /// /// Recursively checks if the type with all of its members are expressable as a value type that may be cast to a pointer. /// Equivalent to what compiler does to check for CS0208 error of this statement: /// fixed (int* p = new int[5]) {} /// /// An unmanaged-type is any type that isnt a reference-type and doesnt contain reference-type fields /// at any level of nesting. In other words, an unmanaged-type is one of the following: /// * sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool. /// * Any enum-type. /// * Any pointer-type. /// * Any user-defined struct-type that contains fields of unmanaged-types only. /// /// Strings are not in that list, even though you can use them in structs. /// Fixed-size arrays of unmanaged-types are allowed. /// public static void ThrowIfNotUnmanagedType(this Type type) { ThrowIfNotUnmanagedType(type, new Stack(4)); } private static void ThrowIfNotUnmanagedType(Type type, Stack typesStack) { if ((!type.IsValueType && !type.IsPointer) || type.IsGenericType || type.IsGenericParameter || type.IsArray) throw new ArgumentException(String.Format("Type {0} is not an unmanaged type", type.FullName)); if (!type.IsPrimitive && !type.IsEnum && !type.IsPointer) for (var p = type.DeclaringType; p != null; p = p.DeclaringType) if (p.IsGenericTypeDefinition) throw new ArgumentException( String.Format("Type {0} contains a generic type definition declaring type {1}", type.FullName, p.FullName)); try { typesStack.Push(type); var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (var f in fields) if (!typesStack.Contains(f.FieldType)) ThrowIfNotUnmanagedType(f.FieldType); } catch (Exception ex) { throw new ArgumentException( String.Format("Error in subtype of type {0}. See InnerException.", type.FullName), ex); } finally { typesStack.Pop(); } } /// /// Substitutes the elements of an array of types for the type parameters /// of the current generic type definition and returns a Type object /// representing the resulting constructed type. /// /// A instance. /// An array of types to be substituted for /// the type parameters of the current generic type. /// A Type representing the constructed type formed by substituting /// the elements of for the type parameters /// of the current generic type. /// public static Type TranslateGenericParameters(this Type type, Type[] typeArguments) { // 'T paramName' case // if (type.IsGenericParameter) return typeArguments[type.GenericParameterPosition]; // 'List paramName' or something like that. // if (type.IsGenericType && type.ContainsGenericParameters) { Type[] genArgs = type.GetGenericArguments(); for (int i = 0; i < genArgs.Length; ++i) genArgs[i] = TranslateGenericParameters(genArgs[i], typeArguments); return type.GetGenericTypeDefinition().MakeGenericType(genArgs); } // Non-generic type. // return type; } } } ================================================ FILE: jemalloc.Api/ExtentHooks.cs ================================================ using System; using System.Runtime.InteropServices; using System.Security; namespace jemalloc { public unsafe partial class ExtentHooks : IDisposable { [StructLayout(LayoutKind.Explicit, Size = 72)] public partial struct __Internal { [FieldOffset(0)] internal global::System.IntPtr alloc; [FieldOffset(8)] internal global::System.IntPtr dalloc; [FieldOffset(16)] internal global::System.IntPtr destroy; [FieldOffset(24)] internal global::System.IntPtr commit; [FieldOffset(32)] internal global::System.IntPtr decommit; [FieldOffset(40)] internal global::System.IntPtr purge_lazy; [FieldOffset(48)] internal global::System.IntPtr purge_forced; [FieldOffset(56)] internal global::System.IntPtr split; [FieldOffset(64)] internal global::System.IntPtr merge; [SuppressUnmanagedCodeSecurity] [DllImport("jemalloc", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "??0extent_hooks_s@@QEAA@AEBU0@@Z")] internal static extern global::System.IntPtr cctor(global::System.IntPtr instance, global::System.IntPtr _0); } public global::System.IntPtr __Instance { get; protected set; } protected int __PointerAdjustment; internal static readonly global::System.Collections.Concurrent.ConcurrentDictionary NativeToManagedMap = new global::System.Collections.Concurrent.ConcurrentDictionary(); protected void*[] __OriginalVTables; protected bool __ownsNativeInstance; internal static global::jemalloc.ExtentHooks __CreateInstance(global::System.IntPtr native, bool skipVTables = false) { return new global::jemalloc.ExtentHooks(native.ToPointer(), skipVTables); } internal static global::jemalloc.ExtentHooks __CreateInstance(global::jemalloc.ExtentHooks.__Internal native, bool skipVTables = false) { return new global::jemalloc.ExtentHooks(native, skipVTables); } private static void* __CopyValue(global::jemalloc.ExtentHooks.__Internal native) { var ret = Marshal.AllocHGlobal(sizeof(global::jemalloc.ExtentHooks.__Internal)); *(global::jemalloc.ExtentHooks.__Internal*)ret = native; return ret.ToPointer(); } private ExtentHooks(global::jemalloc.ExtentHooks.__Internal native, bool skipVTables = false) : this(__CopyValue(native), skipVTables) { __ownsNativeInstance = true; NativeToManagedMap[__Instance] = this; } protected ExtentHooks(void* native, bool skipVTables = false) { if (native == null) return; __Instance = new global::System.IntPtr(native); } public ExtentHooks() { __Instance = Marshal.AllocHGlobal(sizeof(global::jemalloc.ExtentHooks.__Internal)); __ownsNativeInstance = true; NativeToManagedMap[__Instance] = this; } public ExtentHooks(global::jemalloc.ExtentHooks _0) { __Instance = Marshal.AllocHGlobal(sizeof(global::jemalloc.ExtentHooks.__Internal)); __ownsNativeInstance = true; NativeToManagedMap[__Instance] = this; *((global::jemalloc.ExtentHooks.__Internal*)__Instance) = *((global::jemalloc.ExtentHooks.__Internal*)_0.__Instance); } public void Dispose() { Dispose(disposing: true); } public virtual void Dispose(bool disposing) { if (__Instance == IntPtr.Zero) return; global::jemalloc.ExtentHooks __dummy; NativeToManagedMap.TryRemove(__Instance, out __dummy); if (__ownsNativeInstance) Marshal.FreeHGlobal(__Instance); __Instance = IntPtr.Zero; } } } ================================================ FILE: jemalloc.Api/FixedBuffer.cs ================================================ using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; namespace jemalloc { [StructLayout(LayoutKind.Sequential)] [DebuggerDisplay("{DebuggerDisplay(),nq}")] public readonly struct FixedBuffer : IDisposable, IRetainable, IEquatable>, IEnumerable where T : struct, IEquatable, IComparable, IConvertible { #region Constructors public FixedBuffer(int length) { _Ptr = IntPtr.Zero; _Length = 0; _SizeInBytes = 0; _Timestamp = 0; IsReadOnly = false; AllocateThreadId = 0; Rid = JemUtil.Rng.Next(0, 4096); if (length == 0) { throw new ArgumentException("FixedBuffer Length cannot be zero."); } ThrowIfTypeNotPrimitive(); long t = DateTime.UtcNow.Ticks; int th = Thread.CurrentThread.ManagedThreadId; _Ptr = Jem.AllocateFixedBuffer((ulong)length, ElementSizeInBytes, t, th, Rid); if (_Ptr != IntPtr.Zero) { _Length = length; _SizeInBytes = (ulong)_Length * ElementSizeInBytes; _Timestamp = t; AllocateThreadId = th; } else throw new OutOfMemoryException($"Could not allocate {(ulong)_Length * ElementSizeInBytes} bytes for {Name}"); } public FixedBuffer(int length, bool isReadOnly) : this(length) { IsReadOnly = true; } public FixedBuffer(T[] array) : this(array.Length) { ReadOnlySpan arraySpan = new ReadOnlySpan(array); arraySpan.CopyTo(this.WriteSpan); } public FixedBuffer(Span span) : this(span.Length) { span.CopyTo(this.WriteSpan); } public FixedBuffer(ReadOnlySpan span) : this(span.Length) { IsReadOnly = true; span.CopyTo(this.WriteSpan); } #endregion #region Implemented members public void Retain() { ThrowIfInvalid(); Jem.IncrementRefCount(_Ptr); } public bool Release() { ThrowIfInvalid(); if (RefCount == 0) { return false; } else { Jem.DecrementRefCount(_Ptr); return true; } } bool IEquatable>.Equals(FixedBuffer buffer) { ThrowIfInvalid(); return this._Ptr == buffer.Ptr && this.Length == buffer.Length && this._Timestamp == buffer.Timestamp && this.AllocateThreadId == buffer.AllocateThreadId && this.Rid == buffer.Rid; } public IEnumerator GetEnumerator() => new FixedBufferEnumerator(this); IEnumerator IEnumerable.GetEnumerator() => new FixedBufferEnumerator(this); #region Disposer void IDisposable.Dispose() { if (IsRetained) { throw new InvalidOperationException($"FixedBuffer<{typeof(T)}[{_Length}] has outstanding references."); } Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { Free(); } #endregion #endregion #region Properties #region Public public bool IsInvalid => _Ptr == IntPtr.Zero || !Jem.FixedBufferIsAllocatedWith(_Ptr, _SizeInBytes, _Timestamp, AllocateThreadId, Rid); public bool IsValid => !IsInvalid; public int RefCount { get { ThrowIfInvalid(); return Jem.GetRefCount(_Ptr); } } public bool IsRetained => RefCount > 0; public bool IsReadOnly { get; } public int Length { get { ThrowIfInvalid(); return _Length; } } public ulong Size { get { ThrowIfInvalid(); return _SizeInBytes; } } public unsafe ReadOnlySpan Span { get { ThrowIfInvalid(); return new ReadOnlySpan(_Ptr.ToPointer(), _Length); } } internal IntPtr Ptr { get { ThrowIfInvalid(); return _Ptr; } } #endregion internal long Timestamp { get { return _Timestamp; } } internal unsafe Span WriteSpan { get { ThrowIfInvalid(); return new Span(_Ptr.ToPointer(), _Length); } } #endregion #region Methods public void Acquire() => Retain(); public bool Free() { if (IsInvalid) { return false; } if(IsRetained) { return false; } return Jem.FreeFixedBuffer(_Ptr); } public void Fill(T value) { WriteSpan.Fill(value); } public void CopyTo(T[] array) { WriteSpan.CopyTo(new Span(array)); } public T[] CopyToArray() { T[] array = new T[Length]; WriteSpan.CopyTo(new Span(array)); return array; } public T[] CopyToArray(T[] array) { if (_Length != array.Length) { throw new ArgumentException($"Array length {array.Length} is not the same as length {_Length} of {Name}."); } else { Span.CopyTo(new Span(array)); return array; } } public T[] CopyToArrayAndFree() { T[] array = CopyToArray(); Free(); return array; } public bool EqualTo(T[] array) { ThrowIfInvalid(); if (_Length != array.Length) { return false; } else { ReadOnlySpan span = new ReadOnlySpan(array); bool ret = this.Span.SequenceEqual(span); return ret; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ReadOnlySpan AcquireSpan() { Acquire(); return new ReadOnlySpan((void*) _Ptr, (int)Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ReadOnlySpan AcquireSpan() where C : struct, IEquatable, IComparable, IConvertible { int size = checked((int)(Size / (ulong)JemUtil.SizeOfStruct())); if (size == 0) { throw new ArgumentException($"Type {typeof(T).Name} is too small to be reinterpreted as {typeof(C).Name}."); } else { Acquire(); return new ReadOnlySpan((void*)_Ptr, size); } } public ReadOnlySpan Slice(int start, int length) { return Span.Slice(start, length); } public unsafe void * PtrTo(int index) { return BufferHelpers.Add(_Ptr, index).ToPointer(); } public Vector AcquireAsSingleVector() { if (this._Length != Vector.Count) { throw new InvalidOperationException($"The length of the array must be {Vector.Count} elements to create a vector of type {JemUtil.CLRType().Name}."); } ReadOnlySpan span = AcquireSpan(); ReadOnlySpan> vector = span.NonPortableCast>(); return vector[0]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ReadOnlySpan> AcquireVectorSpan() { ThrowIfNotVectorizable(); ThrowIfInvalid(); return new ReadOnlySpan>(_Ptr.ToPointer(), _Length / JemUtil.VectorLength()); } public unsafe Span AcquireWriteSpan() { Acquire(); return new Span((void*)_Ptr, _Length); } public unsafe Span> AcquireVectorWriteSpan() { ThrowIfNotVectorizable(); Acquire(); return new Span>(_Ptr.ToPointer(), _Length / JemUtil.VectorLength()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe ref T Read(int index) { ref T ret = ref Unsafe.Add(ref Unsafe.AsRef(_Ptr.ToPointer()), index); return ref ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ref C Read(int index) where C : struct { //ref C ret = ref Unsafe.aAdd(ref Unsafe.AsRef(_Ptr.ToPointer()), index); //return re return ref Unsafe.AsRef(PtrTo(index)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Write(int index, ref C value) where C : struct { Unsafe.Write(PtrTo(index), value); } internal unsafe Span> WriteVectorSpan { get { ThrowIfInvalid(); return new Span>(_Ptr.ToPointer(), _Length / JemUtil.VectorLength()); } } internal void ThrowIfInvalid() { if (IsInvalid) { throw new InvalidOperationException($"{nameof(FixedBuffer)}({this._Length}) is invalid."); } } internal void ThrowIfIndexOutOfRange(int index) { if (index >= _Length) { throw new IndexOutOfRangeException($"Index {index} is greater than the maximum index of the buffer {_Length - 1}."); } else if (index < 0) { throw new IndexOutOfRangeException($"Index {index} is less than zero."); } } internal void ThrowIfRefCountNonZero() { if (0 > 0) { throw new InvalidOperationException($"{nameof(FixedBuffer)}({this._Length}) has RefCount ."); } } internal void ThrowIfReadOnly() { if (IsReadOnly) { throw new InvalidOperationException($"{nameof(FixedBuffer)}({this._Length}) is read-only."); } } internal void ThrowIfTypeNotPrimitive() { if (!typeof(T).IsPrimitive) { throw new ArgumentException($"The type {typeof(T).Name} is not a primitive type."); } } internal void ThrowIfNotVectorizable() { if (!JemUtil.IsNumericType() || _Length == 0 || (_Length % JemUtil.VectorLength() != 0)) { throw new InvalidOperationException("Buffer is not vectorizable."); } } internal string Name => $"{nameof(FixedBuffer)}({this._Length})"; private string DebuggerDisplay() => string.Format("{{{0}[{1}]}}", typeof(T).Name, Length); #region Arithmetic [MethodImpl(MethodImplOptions.AggressiveInlining)] public void VectorFill(T value) { ThrowIfInvalid(); int c = JemUtil.VectorLength(); int i; Vector fill = new Vector(value); Span> s = WriteVectorSpan; for (i = 0; i < s.Length; i ++) { s[i] = fill; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void VectorMultiply(T value) { ThrowIfInvalid(); int c = JemUtil.VectorLength(); int i; T r; Vector mul = new Vector(value); for (i = 0; i < _Length - c; i += c) { Vector f = Read>(i); Vector result = f * mul; Write(i, ref result); } for (; i < _Length; ++i) { r = GM.Multiply(this[i], value); Write(i, ref r); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void VectorSqrt() { ThrowIfInvalid(); int c = JemUtil.VectorLength(); int i; T r; for (i = 0; i < _Length - c; i += c) { Vector f = Read>(i); Vector result = Vector.SquareRoot(f); Write(i, ref result); } for (; i < _Length; ++i) { r = JemUtil.ValToGenericStruct(GM.Sqrt(this[i])); Write(i, ref r); } } public unsafe bool VectorLessThanAll(T value, out int index) { ThrowIfInvalid(); index = -1; bool r = true; int c = Vector.Count; int i; Vector v = new Vector(value); Vector O = Vector.One; for (i = 0; i <= _Length - c; i+= c) { Vector s = Unsafe.Read>(BufferHelpers.Add(_Ptr, i).ToPointer()); Vector vcmp = Vector.LessThan(s, v); if (vcmp == O) { continue; } else { r = false; for (int j = 0; j < c; j++) { if (vcmp[j].Equals(default)) { index = i + j; return r; } } return r; } } for (; i < _Length; ++i) { if (Read(i).CompareTo(value) >= 0) { r = false; index = i; return r; } } return r; } #endregion #endregion #region Operators public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref this.Read(index); } public static implicit operator IntPtr (FixedBuffer buffer) { return buffer._Ptr; } #endregion #region Fields private static readonly Type ElementType = typeof(T); private static readonly ulong ElementSizeInBytes = (ulong) JemUtil.SizeOfStruct(); private static readonly int VectorWidth = Vector.Count; private readonly IntPtr _Ptr; private readonly ulong _SizeInBytes; private readonly int _Length; private readonly long _Timestamp; internal readonly int AllocateThreadId; internal readonly int Rid; #endregion } } ================================================ FILE: jemalloc.Api/FixedBufferAllocation.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace jemalloc { public readonly struct FixedBufferAllocation : IEquatable { #region Constructor public FixedBufferAllocation(IntPtr ptr, ulong size, long timestamp, int tid, int rid) { this.Ptr = ptr; this.Size = size; this.TimeStamp = timestamp; this.ThreadId = tid; this.Rid = rid; HashCode = JemUtil.CombineHashCodes(this.Ptr.GetHashCode(), this.Size.GetHashCode(), this.TimeStamp.GetHashCode(), this.ThreadId.GetHashCode(), this.Rid.GetHashCode()); } #endregion public override int GetHashCode() { return this.HashCode; } public override bool Equals(object obj) { if (obj is FixedBufferAllocation) { FixedBufferAllocation o = (FixedBufferAllocation)obj; return this.HashCode == o.HashCode; } else { return false; } } bool IEquatable.Equals(FixedBufferAllocation other) { return this.HashCode == other.HashCode; } public readonly IntPtr Ptr; public readonly ulong Size; public readonly long TimeStamp; public readonly int ThreadId; public readonly int Rid; public readonly int HashCode; } public class FixedBufferComparator : IEqualityComparer { public bool Equals(FixedBufferAllocation l, FixedBufferAllocation r) { return l.HashCode == r.HashCode; } public int GetHashCode(FixedBufferAllocation a) { return a.HashCode; } } } ================================================ FILE: jemalloc.Api/FixedBufferEnumerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; namespace jemalloc { public class FixedBufferEnumerator : IEnumerator, IEnumerator where T : struct, IEquatable, IComparable, IConvertible { /// The SafeBuffer being enumerated. private readonly FixedBuffer _buffer; /// The next index to yield. private int _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal FixedBufferEnumerator(FixedBuffer buffer) { _buffer = buffer; _buffer.Acquire(); _index = -1; } /// Advances the enumerator to the next element of the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { int index = _index + 1; if (index < _buffer.Length) { _index = index; return true; } else { return false; } } public void Reset() { _index = -1; } /// Gets the element at the current position of the enumerator. object IEnumerator.Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer[_index]; } public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer[_index]; } void IDisposable.Dispose() { _buffer.Release(); } } } ================================================ FILE: jemalloc.Api/GDI.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; namespace jemalloc { public unsafe static partial class Win32 { public enum BitmapCompressionMode { BI_RGB = 0x0000, BI_RLE8 = 0x0001, BI_RLE4 = 0x0002, BI_BITFIELDS = 0x0003, BI_JPEG = 0x0004, BI_PNG = 0x0005, BI_CMYK = 0x000B, BI_CMYKRLE8 = 0x000C, BI_CMYKRLE4 = 0x000D } [StructLayout(LayoutKind.Sequential)] public struct BITMAPINFOHEADER { public uint biSize; public int biWidth; public int biHeight; public ushort biPlanes; public ushort biBitCount; public BitmapCompressionMode biCompression; public uint biSizeImage; public int biXPelsPerMeter; public int biYPelsPerMeter; public uint biClrUsed; public uint biClrImportant; public void Init() { biSize = (uint)Marshal.SizeOf(this); } } [StructLayout(LayoutKind.Sequential)] public struct RGBQUAD { public byte rgbBlue; public byte rgbGreen; public byte rgbRed; public byte rgbReserved; } public struct BITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD[] bmiColors; } [DllImport("gdi32.dll")] static extern IntPtr CreateDIBitmap(IntPtr hdc, [In] ref BITMAPINFOHEADER lpbmih, uint fdwInit, byte[] lpbInit, [In] ref BITMAPINFO lpbmi, uint fuUsage); } } ================================================ FILE: jemalloc.Api/GenericMath.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace jemalloc { public static class GM where TData : struct, IEquatable, IComparable, IConvertible { #region Constructor static GM() { if (!JemUtil.IsNumericType()) { throw new InvalidOperationException($"Type {typeof(TData).Name} is not a numeric type."); } } #endregion #region Properties public static Random Rng { get; } = new Random(); #endregion #region Methods public static TData Const(TValue v) where TValue : struct, IEquatable, IComparable, IConvertible { return (TData) Convert.ChangeType(v, typeof(TData)); } public static TData Multiply(TData l, TData r) { Tuple value = new Tuple(l, r); switch (value) { case Tuple v: return (TData)Convert.ChangeType(checked((byte)(v.Item1 * v.Item2)), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked((SByte)(v.Item1 * v.Item2)), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType((checked((UInt16)(v.Item1 * v.Item2))), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked((Int16)(v.Item1 * v.Item2)), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked(v.Item1 * v.Item2), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked(v.Item1 * v.Item2), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked(v.Item1 * v.Item2), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked(v.Item1 * v.Item2), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked(v.Item1 * v.Item2), typeof(TData)); case Tuple v: return (TData)Convert.ChangeType(checked(v.Item1 * v.Item2), typeof(TData)); case Tuple v: throw new ArgumentException($"Cannot multiply 2 bools."); default: throw new Exception($"Unsupported math type: {typeof(TData).Name}"); } } public static double Sqrt(TData n) { switch (n) { case SByte v: return checked(Math.Sqrt(v)); case Byte v: return checked(Math.Sqrt(v)); case Int32 v: return checked(Math.Sqrt(v)); case UInt32 v: return checked(Math.Sqrt(v)); case Int16 v: return checked(Math.Sqrt(v)); case UInt16 v: return checked(Math.Sqrt(v)); case Int64 v: return checked(Math.Sqrt(v)); case UInt64 v: return checked(Math.Sqrt(v)); case Single v: return checked(Math.Sqrt(v)); case Double v: return checked(Math.Sqrt(v)); case bool v: throw new ArgumentException($"Cannot get the square root of a bool."); default: throw new ArithmeticException(); } } public static double F(Func f, TData n) { switch (n) { case SByte v: return checked(f(v)); case Byte v: return checked(f(v)); case Int32 v: return checked(f(v)); case UInt32 v: return checked(f(v)); case Int16 v: return checked(f(v)); case UInt16 v: return checked(f(v)); case Int64 v: return checked(f(v)); case UInt64 v: return checked(f(v)); case Single v: return checked(f(v)); case Double v: return checked(f(v)); case Boolean v: throw new ArgumentException($"Cannot apply math functions to a bool."); default: throw new ArithmeticException(); } } public static TData Random() { TData e = default; switch (e) { case SByte v: return Const(checked((sbyte)Rng.Next(0, SByte.MaxValue))); case Byte v: return Const(checked((byte)Rng.Next(0, Byte.MaxValue))); case Int32 v: return Const(checked((int)Rng.Next(0, Int32.MaxValue))); case UInt32 v: return Const(checked((uint)Rng.Next(0, Int32.MaxValue))); case Int16 v: return Const(checked((short)Rng.Next(0, Int16.MaxValue))); case UInt16 v: return Const(checked((ushort)Rng.Next(0, UInt16.MaxValue))); case Int64 v: return Const(checked((long)(Rng.NextDouble() * Int64.MaxValue))); case UInt64 v: return Const(checked((ulong)(Rng.NextDouble() * UInt64.MaxValue))); case Single v: return Const(checked(((Single)(Rng.NextDouble() * Int64.MaxValue)))); case Double v: return Const(checked((((double)Rng.NextDouble() * Int64.MaxValue)))); case Boolean v: return Const(Convert.ToBoolean(Rng.Next(0, 1))); default: throw new ArithmeticException(); } } public static TData Random(double max) { return Const(checked((double)Rng.NextDouble() * max)); } public static (TData, TData) RandomMultiplyFactorAndValue() { TData e = default; TData max; int factor = Rng.Next(0, 4); switch (e) { case SByte v: max = Random((sbyte)(sbyte.MaxValue / 4)); break; case Byte v: max = Random((byte)(byte.MaxValue / (byte) 4)); break; case Int16 v: max = Random((short)(short.MaxValue / (short)4)); break; case UInt16 v: max = Random((ushort)(ushort.MaxValue / (ushort)4)); break; case Int32 v: max = Random((int)(int.MaxValue / 4)); break; case UInt32 v: max = Random(uint.MaxValue / 4u); break; case Int64 v: max = Random((long)(long.MaxValue / 4)); break; case UInt64 v: max = Random(ulong.MaxValue / 4u); break; case Double v: max = Random((double)(long.MaxValue / 4)); break; case Single v: max = Random((Single)(long.MaxValue / 4)); break; default: throw new ArgumentException($"Cannot multiply type {nameof(TData)}."); } return (Const(factor), Const(max)); } #endregion } } ================================================ FILE: jemalloc.Api/HugeArray.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace jemalloc { public class HugeArray : HugeBuffer where T : struct, IEquatable { public HugeArray(ulong length, params T[] values) : base(length, values) { } } } ================================================ FILE: jemalloc.Api/HugeBuffer.cs ================================================ using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Numerics; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Threading; namespace jemalloc { public abstract class HugeBuffer : SafeHandle, IRetainable, IDisposable, IEquatable>, IEnumerable where T : struct, IEquatable { #region Constructors protected HugeBuffer(ulong length, params T[] values) : base(IntPtr.Zero, true) { ulong l = (ulong)values.Length; if (BufferHelpers.IsReferenceOrContainsReferences()) { throw new ArgumentException("Only structures without reference fields can be used with this class."); } if (l > length) { throw new ArgumentException("The length of the list of values must be smaller or equal to the length of the buffer"); } SizeInBytes = NotAllocated; base.SetHandle(Allocate(length)); if (IsAllocated) { CopyFrom(values); } } #endregion #region Overriden members [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override bool ReleaseHandle() { bool r = Jem.Free(handle); if (!r) { return false; } else { handle = IntPtr.Zero; unsafe { voidPtr = (void*)0; } Length = 0; SizeInBytes = 0; return true; } } public override bool IsInvalid => handle == IntPtr.Zero; #region Disposer [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override void Dispose(bool disposing) { if (disposing) { ThrowIfNotAllocatedOrInvalid(); ThrowIfRetained(); ReleaseHandle(); } base.Dispose(disposing); } #endregion #endregion #region Implemented members public void Retain() => Acquire(); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public bool Release() { if (IsNotAllocated || IsInvalid) { return false; } else { Jem.DecrementRefCount(handle); DangerousRelease(); return true; } } public bool Equals(HugeBuffer other) { ThrowIfNotAllocatedOrInvalid(); return this.handle == other.handle && this.Length == other.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator GetEnumerator() => new HugeBufferEnumerator(this); [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => new HugeBufferEnumerator(this); #endregion #region Properties public ulong Length { get; protected set; } public ulong SizeInBytes { get; protected set; } public bool IsNotAllocated => SizeInBytes == NotAllocated; public bool IsAllocated => !IsNotAllocated; public bool IsValid => !IsInvalid; public bool IsRetained => _RefCount > 0; public bool IsVectorizable { get; protected set; } protected int _RefCount => Jem.GetRefCount(handle); #endregion #region Methods #region Memory management protected unsafe virtual IntPtr Allocate(ulong length) { if (length < 0) throw new ArgumentOutOfRangeException("length"); Contract.EndContractBlock(); ulong s = checked(length * ElementSizeInBytes); handle = Jem.Calloc(length, ElementSizeInBytes); if (handle != IntPtr.Zero) { voidPtr = handle.ToPointer(); Length = length; SizeInBytes = s; InitSegments(); InitVectors(); } return handle; } protected unsafe void InitSegments() { int n = (int)((Length - 1) / Int32.MaxValue) + 1; segments = new IntPtr[n]; segments2 = new Tuple[segments.Length]; segments[0] = handle; segments2[0] = new Tuple(handle, n == 1 ? (int)Length : Int32.MaxValue); for (int i = 1; i < n; i++) { segments[i] = BufferHelpers.Add(segments[i - 1], Int32.MaxValue); if (i < n - 1) { segments2[i] = new Tuple(segments[i], Int32.MaxValue); } } segments2[n - 1] = new Tuple(segments[n - 1], (int)(Length - (ulong)((n - 1) * Int32.MaxValue))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe void InitVectors() { if (IsNumeric && Length % (ulong)VectorLength == 0 && SIMD) { IsVectorizable = true; } else { IsVectorizable = false; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected int _GetSegmentIndex(ulong index) { return (int)(index / Int32.MaxValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe void _GetSegment(ulong index, out void* ptr, out int offset) { int s = _GetSegmentIndex(index); int l = segments.Length; ptr = segments[s].ToPointer(); offset = (int)(index - ((ulong)(s) * Int32.MaxValue)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe Span _GetSegmentSpan(ulong index) { int i = _GetSegmentIndex(index); return new Span(segments2[i].Item1.ToPointer(), segments2[i].Item2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe Span> _GetSegmentVectorSpan(ulong index) { int i = _GetSegmentIndex(index); return new Span>(segments2[i].Item1.ToPointer(), segments2[i].Item2 / VectorLength + 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe ref T _Read(ulong index) { // return (T*) (seg_ptr + byteOffset); _GetSegment(index, out void* ptr, out int offset); ref T ret = ref Unsafe.Add(ref Unsafe.AsRef(ptr), offset); return ref ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe T _Write(ulong index, T value) { // return (T*) (seg_ptr + byteOffset); _GetSegment(index, out void* ptr, out int offset); ref T v = ref Unsafe.AsRef(BufferHelpers.Add(new IntPtr(ptr), offset).ToPointer()); v = value; return value; } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public bool Acquire() { if (IsNotAllocated || IsInvalid) return false; bool success = false; DangerousAddRef(ref success); if (success) { Jem.IncrementRefCount(handle); } return success; } public bool Release(int n) { bool r = false; for (int i = 0; i < n; i++) { r = Release(); if (r) { continue; } else { return r; } } return r; } #endregion #region Values public bool EqualTo(T[] array) { ThrowIfNotAllocatedOrInvalid(); if (this.Length != (ulong)array.Length) { return false; } return _GetSegmentSpan(0).SequenceEqual(new ReadOnlySpan(array)); } public unsafe void Fill(T value) { ThrowIfNotAllocatedOrInvalid(); for (int i = 0; i < segments.Length; i++) { Span s = new Span(segments2[i].Item1.ToPointer(), segments2[i].Item2); s.Fill(value); } } public void CopyFrom(T[] array) { ThrowIfNotAllocatedOrInvalid(); new Span(array).CopyTo(_GetSegmentSpan(0)); } public T[] CopyToArray() { ThrowIfNotAllocatedOrInvalid(); if (this.Length > Int32.MaxValue) { throw new ArgumentOutOfRangeException("This length of this array exceeds the max length of a managed array."); } T[] a = new T[this.Length]; _GetSegmentSpan(0).CopyTo(new Span(a)); return a; } public unsafe Span GetSpan(ulong index = 0, int length = 1) where C : struct, IEquatable { ThrowIfNotAllocatedOrInvalid(); ThrowIfIndexOutOfRange(index); ulong s = (index * (ulong) ElementSizeInBytes + (ulong) length * (ulong) Unsafe.SizeOf()); if (s > SizeInBytes) { BufferSizeIsOutOfRange(s); } _GetSegment(index, out void* ptr, out int offset); void* p = BufferHelpers.Add(new IntPtr(ptr), offset).ToPointer(); return new Span(p, length); } public unsafe Span Slice(ulong start, ulong end) { ThrowIfNotAllocatedOrInvalid(); ThrowIfIndexOutOfRange(start); ThrowIfIndexOutOfRange(end); if (start >= end) { throw new ArgumentOutOfRangeException($"The end {end} of the slice must be greater than the start {start}."); } else if (end - start > Int32.MaxValue) { throw new ArgumentOutOfRangeException($"The size of the slice must be less than or equal to {Int32.MaxValue}."); } _GetSegment(end, out void* ptr, out int offset); void* p = BufferHelpers.Add(new IntPtr(ptr), offset).ToPointer(); return new Span(p, (int) (end - start)); } public unsafe Vector GetAsSingleVector() { ThrowIfNotAllocatedOrInvalid(); if (this.Length != (ulong) VectorLength) { throw new InvalidOperationException($"The length of the array must be {Vector.Count} elements to create a vector of type {CLRType.Name}."); } return Unsafe.Read>(handle.ToPointer()); } public unsafe Span> GetSliceSegmentAsVectorSpan(ulong index) { ThrowIfNotAllocatedOrInvalid(); if ((Length - index) < (ulong) VectorLength) { ThrowIfIndexOutOfRange(index); } T v = this[index]; int i = _GetSegmentIndex(index); if (segments2[i].Item2 % VectorLength != 0) { BufferIsNotVectorisable(); } return new Span>(segments2[i].Item1.ToPointer(), segments2[i].Item2 / VectorLength + 1); } public unsafe Vector GetSliceAsSingleVector(ulong index) { ThrowIfNotAllocatedOrInvalid(); ThrowIfIndexOutOfRange(index); if ((Length - index) < (ulong) VectorLength) { BufferIsNotVectorisable(); } int i = _GetSegmentIndex(index); _GetSegment(index, out void* ptr, out int offset); IntPtr start = BufferHelpers.Add(segments2[i].Item1, offset); return Unsafe.Read>(start.ToPointer()); } #endregion #region Vector /* public unsafe void VectorMultiply(T value) { ThrowIfNotAllocatedOrInvalid(); ThrowIfNotNumeric(); Vector mul = new Vector(value); for (int h = 0; h < segments2.Length; h++) { int i = 0; for (; i < segments2[h].Item2 - VectorLength; i++ ) { Vector v = Unsafe.Read>(BufferHelpers.Add(segments2[h].Item1, i).ToPointer()); v = v * mul; } f } }*/ #endregion #region Helpers [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfCannotAcquire() { if (!Acquire()) { throw new InvalidOperationException("Could not acquire handle."); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfIndexOutOfRange(ulong index) { if (index >= Length) { BufferIndexIsOutOfRange(index); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotAllocatedOrInvalid() { if (IsNotAllocated) { BufferIsNotAllocated(); } else if (IsInvalid) { HandleIsInvalid(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfRetained() { if (IsRetained) { throw new InvalidOperationException($"SafeBuffer<{typeof(T)}[{Length}] has outstanding references."); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotAllocated() { if (IsNotAllocated) { BufferIsNotAllocated(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotVectorisable() { if (!IsVectorizable) { BufferIsNotVectorisable(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotNumeric() { if (!IsNumeric) { BufferIsNotNumeric(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static InvalidOperationException HandleIsInvalid() { Contract.Assert(false, "The handle is invalid."); return new InvalidOperationException("The handle is invalid."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static InvalidOperationException BufferIsNotAllocated() { Contract.Assert(false, "Unallocated safe buffer used."); return new InvalidOperationException("Unallocated safe buffer used."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static InvalidOperationException BufferIsNotVectorisable() { Contract.Assert(false, "Buffer is not vectorisable."); return new InvalidOperationException("Buffer is not vectorisable."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static InvalidOperationException BufferIsNotNumeric() { Contract.Assert(false, "Buffer is not numeric."); return new InvalidOperationException("Buffer is not numeric."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void BufferIndexIsOutOfRange(ulong index) { Contract.Assert(false, $"Index {index} into buffer is out of range."); throw new IndexOutOfRangeException($"Index {index} into buffer is out of range."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void BufferSizeIsOutOfRange(ulong index) { Contract.Assert(false, $"Size {index} exceeds the size of the buffer."); throw new IndexOutOfRangeException($"Size {index} exceeds the size of the buffer."); } #endregion #endregion #region Operators public ref T this[ulong index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref _Read(index); } #endregion #region Fields protected static readonly Type CLRType = typeof(T); protected static readonly T Element = default; protected static readonly uint ElementSizeInBytes = (uint) JemUtil.SizeOfStruct(); protected static readonly UInt64 NotAllocated = UInt64.MaxValue; protected static readonly bool IsNumeric = JemUtil.IsNumericType(); protected static readonly int VectorLength = Vector.Count; protected static bool SIMD = Vector.IsHardwareAccelerated; protected internal unsafe void* voidPtr; protected unsafe IntPtr[] segments; protected unsafe Tuple[] segments2; //Debugger Display = {T[length]} private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, Length); #endregion } } ================================================ FILE: jemalloc.Api/HugeBufferEnumerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; namespace jemalloc { /// Enumerates the elements of a . public class HugeBufferEnumerator : IEnumerator, IEnumerator where T : struct, IEquatable { /// The span being enumerated. private readonly HugeBuffer _buffer; /// The next index to yield. private ulong _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HugeBufferEnumerator(HugeBuffer buffer) { _buffer = buffer; _buffer.Acquire(); _index = UInt64.MaxValue; } /// Advances the enumerator to the next element of the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_index == UInt64.MaxValue) { _index = 0; return true; } else { ulong index = _index + 1; if (index < _buffer.Length) { _index = index; return true; } else { return false; } } } public void Reset() { _index = UInt64.MaxValue; } /// Gets the element at the current position of the enumerator. object IEnumerator.Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer[_index]; } public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer[_index]; } void IDisposable.Dispose() { _buffer.Release(); } } } ================================================ FILE: jemalloc.Api/IBufferAllocation.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace jemalloc { public interface IBufferAllocation { IntPtr Ptr { get; } ulong Size { get; } long TimeStamp { get; } int ThreadId { get; } int HashCode { get; } } } ================================================ FILE: jemalloc.Api/JemApi.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Collections.Immutable; using System.Linq; using System.IO; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Security; using System.Text; using System.Threading; namespace jemalloc { #region Types and Enums public class CallerInformation { public string Name; public string File; public int LineNumber; public CallerInformation(string name, string file, int line_number) { this.Name = name; this.File = file; this.LineNumber = line_number; } public override string ToString() => Jem.GetCallerDetails(this); } internal enum ERRNO { ENONE = 0, EPERM = 1, ENOENT = 2, ESRCH = 3, EINTR = 4, EIO = 5, ENXIO = 6, E2BIG = 7, ENOEXEC = 8, EBADF = 9, ECHILD = 10, EAGAIN = 11, ENOMEM = 12, EACCES = 13, EFAULT = 14, EBUSY = 16, EEXIST = 17, EXDEV = 18, ENODEV = 19, ENOTDIR = 20, EISDIR = 21, ENFILE = 23, EMFILE = 24, ENOTTY = 25, EFBIG = 27, ENOSPC = 28, ESPIPE = 29, EROFS = 30, EMLINK = 31, EPIPE = 32, EDOM = 33, EDEADLK = 36, ENAMETOOLONG = 38, ENOLCK = 39, ENOSYS = 40, ENOTEMPTY = 41 } [Flags] public enum ALLOC_FLAGS { ZERO, DETAILS } #endregion public unsafe static partial class Jem { #region Constructor static Jem() { __Internal.JeMallocMessage += messagesCallback; } #endregion #region Methods #region Low-level jemalloc API public static global::System.IntPtr Malloc(ulong size, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { CallerInformation caller = new CallerInformation(memberName, fileName, lineNumber); Initialized = true; IntPtr __ret = __Internal.JeMalloc(size); if (__ret != IntPtr.Zero) { allocLock.EnterWriteLock(); _Allocations.Add(__ret, 0); allocLock.ExitWriteLock(); return __ret; } else { throw new OutOfMemoryException($"Could not allocate {size} bytes for {GetCallerDetails(caller)}."); } } public static global::System.IntPtr Calloc(ulong num, ulong size, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { CallerInformation caller = new CallerInformation(memberName, fileName, lineNumber); Initialized = true; IntPtr __ret = __Internal.JeCalloc(num, size); if (__ret != IntPtr.Zero) { allocLock.EnterWriteLock(); _Allocations.Add(__ret, 0); allocLock.ExitWriteLock(); return __ret; } else { throw new OutOfMemoryException($"Could not allocate {num * size} bytes for {GetCallerDetails(caller)}."); } } public static int PosixMemalign(void** memptr, ulong alignment, ulong size) { Initialized = true; var __ret = __Internal.JePosixMemalign(memptr, alignment, size); return __ret; } public static global::System.IntPtr AlignedAlloc(ulong alignment, ulong size) { Initialized = true; var __ret = __Internal.JeAlignedAlloc(alignment, size); return __ret; } public static global::System.IntPtr Realloc(global::System.IntPtr ptr, ulong size) { Initialized = true; var __ret = __Internal.JeRealloc(ptr, size); return __ret; } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static bool Free(global::System.IntPtr ptr, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { bool ret = false; RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { allocLock.EnterUpgradeableReadLock(); ret = _Allocations.ContainsKey(ptr); if (ret) { allocLock.EnterWriteLock(); _Allocations.Remove(ptr); __Internal.JeFree(ptr); allocLock.ExitWriteLock(); } allocLock.ExitUpgradeableReadLock(); } return ret; } public static global::System.IntPtr Mallocx(ulong size, int flags) { Initialized = true; var __ret = __Internal.JeMallocx(size, flags); return __ret; } public static global::System.IntPtr Rallocx(global::System.IntPtr ptr, ulong size, int flags) { Initialized = true; var __ret = __Internal.JeRallocx(ptr, size, flags); return __ret; } public static ulong Xallocx(global::System.IntPtr ptr, ulong size, ulong extra, int flags) { Initialized = true; var __ret = __Internal.JeXallocx(ptr, size, extra, flags); return __ret; } public static ulong Sallocx(global::System.IntPtr ptr, int flags) { Initialized = true; var __ret = __Internal.JeSallocx(ptr, flags); return __ret; } public static void Dallocx(global::System.IntPtr ptr, int flags) { Initialized = true; __Internal.JeDallocx(ptr, flags); } public static void Sdallocx(global::System.IntPtr ptr, ulong size, int flags) { Initialized = true; __Internal.JeSdallocx(ptr, size, flags); } public static ulong Nallocx(ulong size, int flags) { Initialized = true; var __ret = __Internal.JeNallocx(size, flags); return __ret; } public static int Mallctl(string name, global::System.IntPtr oldp, ref ulong oldlenp, global::System.IntPtr newp, ulong newlen) { fixed (ulong* __refParamPtr2 = &oldlenp) { var __arg2 = __refParamPtr2; var __ret = __Internal.JeMallctl(name, oldp, __arg2, newp, newlen); return __ret; } } public static int Mallctlnametomib(string name, ref ulong mibp, ref ulong miblenp) { fixed (ulong* __refParamPtr1 = &mibp) { var __arg1 = __refParamPtr1; fixed (ulong* __refParamPtr2 = &miblenp) { var __arg2 = __refParamPtr2; var __ret = __Internal.JeMallctlnametomib(name, __arg1, __arg2); return __ret; } } } public static int Mallctlbymib(ref ulong mib, ulong miblen, global::System.IntPtr oldp, ref ulong oldlenp, global::System.IntPtr newp, ulong newlen) { fixed (ulong* __refParamPtr0 = &mib) { var __arg0 = __refParamPtr0; fixed (ulong* __refParamPtr3 = &oldlenp) { var __arg3 = __refParamPtr3; var __ret = __Internal.JeMallctlbymib(__arg0, miblen, oldp, __arg3, newp, newlen); return __ret; } } } #endregion #region High-level API public static bool Init(string conf) { if (!Initialized) { if (mallocMessagesBuilder == null) { mallocMessagesBuilder = new StringBuilder(); } try { MallocConf = conf; } catch (Exception) { throw new ArgumentException($"The configuration string \'{conf}\' is invalid"); } Initialized = true; return true; } else return false; } #region Memory life-time management public static int GetRefCount(IntPtr ptr) { allocLock.EnterReadLock(); int c = _Allocations[ptr]; allocLock.ExitReadLock(); return c; } public static void IncrementRefCount(IntPtr ptr) { allocLock.EnterWriteLock(); _Allocations[ptr] = _Allocations[ptr] + 1; allocLock.ExitWriteLock(); } public static void DecrementRefCount(IntPtr ptr) { allocLock.EnterWriteLock(); _Allocations[ptr] = _Allocations[ptr] - 1; allocLock.ExitWriteLock(); } public static bool PtrIsAllocated(IntPtr ptr) { allocLock.EnterReadLock(); bool r = _Allocations.ContainsKey(ptr); allocLock.ExitReadLock(); return r; } #endregion public static string MallocStats => GetMallocStats(string.Empty); public static string GetMallocStats(string opt) { StringBuilder statsBuilder = new StringBuilder(1000); __Internal.JeMallocMessageCallback stats = (o, m) => { statsBuilder.Append(m); }; __Internal.JeMallocStatsPrint(Marshal.GetFunctionPointerForDelegate(stats), IntPtr.Zero, opt); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { statsBuilder = statsBuilder.Replace("\\n", "\\r\\n"); } return statsBuilder.ToString(); } public static ulong MallocUsableSize(global::System.IntPtr ptr) { var __ret = __Internal.JeMallocUsableSize(ptr); return __ret; } public static string MallocConf { get { return Marshal.PtrToStringAnsi(__Internal.JeGetMallocConf()); } set { __Internal.JeSetMallocConf(Marshal.StringToHGlobalAnsi(value)); } } #region MallCtl public static int GetMallCtlInt32(string name) { void* i = stackalloc int[1]; ulong size = sizeof(Int32); ERRNO r = (ERRNO) Mallctl(name, (IntPtr) i, ref size, IntPtr.Zero, 0); return r == ERRNO.ENONE ? *(Int32*)(i) : throw GetExceptionForErrNo($"Could not get mallctl value {name}.", r); } public static bool SetMallCtlInt32(string name, int value) { void* i = &value; ulong size = sizeof(Int32); ERRNO r = (ERRNO) Mallctl(name, IntPtr.Zero, ref size, (IntPtr) i, size); return r == ERRNO.ENONE ? true : throw GetExceptionForErrNo($"Could not set mallctl value {name} to {value}.", r); } public static bool GetMallCtlBool(string name) { void* i = stackalloc byte[1]; ulong size = sizeof(byte); ERRNO r = (ERRNO)Mallctl(name, (IntPtr)i, ref size, IntPtr.Zero, 0); return r == ERRNO.ENONE ? *(byte*)(i) == 1 ? true : false: r == ERRNO.ENOENT ? false : throw GetExceptionForErrNo($"Could not get mallctl value {name}.", r); } public static bool SetMallCtlBool(string name, bool value) { byte v = value ? (byte) 1 : (byte) 0; void* n = &v; ulong size = sizeof(byte); ERRNO r = (ERRNO)Mallctl(name, IntPtr.Zero, ref size, (IntPtr)n, size); return r == ERRNO.ENONE ? true : throw GetExceptionForErrNo($"Could not set mallctl value {name} to {value}.", r); } public static UInt64 GetMallCtlUInt64(string name) { void* i = stackalloc UInt64[1]; ulong size = sizeof(UInt64); ERRNO r = (ERRNO) Mallctl(name, (IntPtr) i, ref size, IntPtr.Zero, 0); return r == ERRNO.ENONE ? *(UInt64*)(i) : throw GetExceptionForErrNo($"Could not get mallctl value {name}.", r); } public static bool SetMallCtlUInt64(string name, ulong value) { void* n = &value; ulong size = sizeof(UInt64); ERRNO r = (ERRNO) Mallctl(name, IntPtr.Zero, ref size, (IntPtr) n, size); return r == ERRNO.ENONE ? true : throw GetExceptionForErrNo($"Could not set mallctl value {name} to {value}.", r); } public static Int64 GetMallCtlSInt64(string name) { void* i = stackalloc Int64[1]; ulong size = sizeof(Int64); ERRNO r = (ERRNO) Mallctl(name, (IntPtr) i, ref size, IntPtr.Zero, 0); return r == ERRNO.ENONE ? *(Int64*)(i) : throw GetExceptionForErrNo($"Could not get mallctl value {name}.", r); } public static bool SetMallCtlSInt64(string name, long value) { void* n = &value; ulong size = sizeof(Int64); ERRNO r = (ERRNO) Mallctl(name, IntPtr.Zero, ref size, (IntPtr)n, size); return r == ERRNO.ENONE ? true : throw GetExceptionForErrNo($"Could not set mallctl value {name} to {value}.", r); } public static string GetMallCtlStr(string name) { void* p = stackalloc IntPtr[1]; IntPtr retp = new IntPtr(p); ulong size = (ulong)sizeof(IntPtr); int ret = Mallctl(name, (IntPtr) p, ref size, IntPtr.Zero, 0); if ((ERRNO)ret == ERRNO.ENONE) { return Marshal.PtrToStringAnsi(*(IntPtr*)p); } else { throw GetExceptionForErrNo($"Could not get mallctl value {name}.", (ERRNO)ret); } } #endregion public static int TryFreeAll() { allocLock.EnterWriteLock(); int c = _Allocations.Count; foreach (IntPtr p in _Allocations.Keys) { Jem.Free(p); _Allocations.Remove(p); } allocLock.ExitWriteLock(); return c; } public static Span Malloc(ulong size, int length, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) where T : struct { IntPtr ptr = Malloc(size, memberName, fileName, lineNumber); return new Span((void*)ptr, length); } #region FixedBuffer public static IntPtr AllocateFixedBuffer(ulong length, ulong size, long timestamp, int tid, int rid, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { IntPtr ptr = Jem.Calloc(length, size, memberName, fileName, lineNumber); if (ptr != IntPtr.Zero) { fixedBufferLock.EnterWriteLock(); _FixedBufferAllocations.Add(ptr, new FixedBufferAllocation(ptr, length * size, timestamp, tid, rid)); fixedBufferLock.ExitWriteLock(); } return ptr; } public static bool FreeFixedBuffer(IntPtr ptr) { fixedBufferLock.EnterWriteLock(); bool r = _FixedBufferAllocations.Remove(ptr); fixedBufferLock.ExitWriteLock(); if (!r) { return false; } else { Jem.Free(ptr); return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool FixedBufferIsAllocatedWith(IntPtr ptr, ulong size, long timestamp, int tid, int rid) { fixedBufferLock.EnterReadLock(); bool r1 = _FixedBufferAllocations.ContainsKey(ptr); if (!r1) { fixedBufferLock.ExitReadLock(); return false; } else { bool r2 = _FixedBufferAllocations[ptr].Equals(new FixedBufferAllocation(ptr, size, timestamp, tid, rid)); fixedBufferLock.ExitReadLock(); return r2; } } #endregion #endregion #region Utility methods internal static bool AllocFlagSet(ALLOC_FLAGS flag) { return (flag & Flags) == flag; } internal static Exception GetExceptionForErrNo(string message, ERRNO no) { switch (no) { case ERRNO.ENOMEM: return new OutOfMemoryException(message); default: return new Exception(message + $" {no}."); } } internal static string GetCallerDetails(string memberName, string fileName, int lineNumber) { return $"Member {memberName} at line {lineNumber} in file {fileName}"; } internal static string GetCallerDetails(CallerInformation c) { return $"Member {c.Name} at line {c.LineNumber} in file {c.File}"; } #endregion #endregion #region Properties public static bool Initialized { get; private set; } = false; public static ALLOC_FLAGS Flags; #region Malloc Messages public static event JeMallocMessageAction MallocMessage; public static string MallocMessages => mallocMessagesBuilder.ToString(); private static __Internal.JeMallocMessageCallback messagesCallback = (o, m) => { mallocMessagesBuilder.Append(m); MallocMessage.Invoke(m); }; #endregion #region jemalloc Statistics public static UInt64 AllocatedBytes => GetMallCtlUInt64("stats.allocated"); public static UInt64 ActiveBytes => GetMallCtlUInt64("stats.active"); public static UInt64 MappedBytes => GetMallCtlUInt64("stats.mapped"); public static UInt64 ResidentBytes => GetMallCtlUInt64("stats.resident"); #endregion #region Allocations ledgers public static List> AllocationsDetails { get; private set; } = new List>(); #endregion #endregion #region Fields private static StringBuilder mallocMessagesBuilder = new StringBuilder(); private static Dictionary _Allocations = new Dictionary(1024); private static Dictionary _FixedBufferAllocations = new Dictionary(1024); private static FixedBufferComparator fixedBufferComparator = new FixedBufferComparator(); private static ReaderWriterLockSlim allocLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private static ReaderWriterLockSlim fixedBufferLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); #endregion } } ================================================ FILE: jemalloc.Api/JemPinnable.cs ================================================ using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Text; namespace jemalloc { [StructLayout(LayoutKind.Sequential)] internal sealed class JemPinnable { public T Data; } } ================================================ FILE: jemalloc.Api/JemUtil.cs ================================================ using System; using System.Diagnostics; using System.Collections.Generic; using System.Collections.Concurrent; using System.Collections.Immutable; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.ConstrainedExecution; using System.Security; namespace jemalloc { public static class JemUtil { #region Constructor static JemUtil() { } #endregion #region Properties public static ConcurrentDictionary BenchmarkValues { get; } = new ConcurrentDictionary(); public static ConcurrentDictionary BenchmarkStatistics { get; } = new ConcurrentDictionary(); public static Process CurrentProcess { get; } = Process.GetCurrentProcess(); public static Random Rng { get; } = new Random(); public static long ProcessPrivateMemory { get { CurrentProcess.Refresh(); return CurrentProcess.PrivateMemorySize64; } } public static long ProcessPeakVirtualMem { get { CurrentProcess.Refresh(); return CurrentProcess.PeakVirtualMemorySize64; } } public static long ProcessPeakWorkingSet { get { CurrentProcess.Refresh(); return CurrentProcess.PeakWorkingSet64; } } public static long ProcessPeakPagedMem { get { CurrentProcess.Refresh(); return CurrentProcess.PeakPagedMemorySize64; } } public static long ProcessVirtualMemory { get { CurrentProcess.Refresh(); return CurrentProcess.VirtualMemorySize64; } } public static long ProcessWorkingSet { get { CurrentProcess.Refresh(); return CurrentProcess.WorkingSet64; } } public static long ProcessPagedMemory { get { CurrentProcess.Refresh(); return CurrentProcess.PagedMemorySize64; } } public static Type CLRType() => typeof(T); public static uint ElementSizeInBytes() where T: struct => (uint)JemUtil.SizeOfStruct(); public static int VectorLength () where T : struct => Vector.Count; public static bool SIMD => Vector.IsHardwareAccelerated; #endregion #region Methods public static bool IsNumericType() { return NumericTypes.Contains(typeof(T)); } public unsafe static Span PtrToSpan(IntPtr ptr, int length) { return new Span((void*)ptr, length); } public unsafe static ref readonly T PtrToStruct(void* ptr) where T : struct { return ref Unsafe.AsRef(ptr); } public unsafe static TReturn ValToGenericStruct(TValue v) where TValue : struct where TReturn : struct { void* ptr = Unsafe.AsPointer(ref v); return PtrToStruct(ptr); } public static int SizeOfStruct() where T : struct { return Unsafe.SizeOf(); } // System.String.GetHashCode(): http://referencesource.microsoft.com/#mscorlib/system/string.cs,0a17bbac4851d0d4 // System.Web.Util.StringUtil.GetStringHashCode(System.String): http://referencesource.microsoft.com/#System.Web/Util/StringUtil.cs,c97063570b4e791a public static int CombineHashCodes(params int[] hashCodes) { int hash1 = (5381 << 16) + 5381; int hash2 = hash1; int i = 0; foreach (var hashCode in hashCodes) { if (i % 2 == 0) hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ hashCode; else hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ hashCode; ++i; } return hash1 + (hash2 * 1566083941); } public static string PrintSize(double bytes, string suffix = "") { if (bytes >= 0 && bytes <= 1024) { return string.Format("{0:N0} {1}", bytes, suffix); } else if (bytes >= 1024 && bytes < (1024 * 1024)) { return string.Format("{0:N1} K{1}", bytes / 1024, suffix); } else if (bytes >= (1024 * 1024) && bytes < (1024 * 1024 * 1024)) { return string.Format("{0:N1} M{1}", bytes / (1024 * 1024), suffix); } else if (bytes >= (1024 * 1024 * 1024) && (bytes < (1024f * 1024f * 1024f * 1024f))) { return string.Format("{0:N1} G{1}", bytes / (1024 * 1024 * 1024), suffix); } else if (bytes >= (1024f * 1024f * 1024f * 1024f) && (bytes < (1024f * 1024f * 1024f * 1024f * 1024f))) { return string.Format("{0:N1} T{1}", bytes / (1024f * 1024f * 1024f * 1024f), suffix); } else throw new ArgumentOutOfRangeException(); } public static string PrintBytes(double bytes, string suffix = "") { if (bytes >= 0 && bytes <= 1024) { return string.Format("{0:N0} B{1}", bytes, suffix); } else if (bytes >= 1024 && bytes < (1024 * 1024)) { return string.Format("{0:N1} KB{1}", bytes / 1024, suffix); } else if (bytes >= (1024 * 1024) && bytes < (1024 * 1024 * 1024)) { return string.Format("{0:N1} MB{1}", bytes / (1024 * 1024), suffix); } else if (bytes >= (1024 * 1024 * 1024) && (bytes < (1024f * 1024f * 1024f * 1024f))) { return string.Format("{0:N1} GB{1}", bytes / (1024 * 1024 * 1024), suffix); } else if (bytes >= (1024f * 1024f * 1024f * 1024f) && (bytes < (1024f * 1024f * 1024f * 1024f * 1024f))) { return string.Format("{0:N1} TB{1}", bytes / (1024f * 1024f * 1024f * 1024f), suffix); } else throw new ArgumentOutOfRangeException(); } public static Tuple PrintBytesToTuple(double bytes, string suffix = "") { string[] s = PrintBytes(bytes, suffix).Split(' '); return new Tuple(Double.Parse(s[0]), s[1]); } public unsafe static ulong GetCurrentThreadCycles() { if (Environment.OSVersion.Platform != PlatformID.Win32NT) { throw new PlatformNotSupportedException(); } if (!__Internal.DuplicateHandle(__Internal.GetCurrentProcess(), __Internal.GetCurrentThread(), __Internal.GetCurrentProcess(), out IntPtr* p, 0x0400, true, (uint) __Internal.DuplicateOptions.DUPLICATE_SAME_ACCESS)) { throw new System.ComponentModel.Win32Exception(); } if (__Internal.QueryThreadCycleTime(in *p, out ulong cycles)) { //__Internal.CloseHandle(*p); return cycles; } else { //__Internal.CloseHandle(*p); throw new System.ComponentModel.Win32Exception(); } } public partial struct __Internal { [Flags] public enum DuplicateOptions : uint { DUPLICATE_CLOSE_SOURCE = (0x00000001),// Closes the source handle. This occurs regardless of any error status returned. DUPLICATE_SAME_ACCESS = (0x00000002), //Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle. } [DllImport("kernel32.dll", SetLastError = true)] [SuppressUnmanagedCodeSecurity] internal static extern bool QueryThreadCycleTime(in IntPtr hThread, out ulong cycles); [DllImport("kernel32.dll", SetLastError = true)] [SuppressUnmanagedCodeSecurity] internal static extern IntPtr GetCurrentThread(); [DllImport("kernel32.dll", SetLastError = true)] [SuppressUnmanagedCodeSecurity] internal static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", SetLastError = true)] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe extern bool DuplicateHandle(IntPtr hSourceProcessHandle,IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr* lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); [DllImport("kernel32.dll", SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool QueryPerformanceCounter(out long lpPerformanceCount); } #endregion #region Fields public static readonly Type Int8CLRType = typeof(SByte); public static readonly Type UInt8CLRType = typeof(Byte); public static readonly Type Int16CLRType = typeof(Int16); public static readonly Type UInt16CLRType = typeof(UInt16); public static readonly Type Int32CLRType = typeof(Int32); public static readonly Type UInt32CLRType = typeof(UInt32); public static readonly Type Int64CLRType = typeof(Int64); public static readonly Type UInt64CLRType = typeof(UInt64); public static readonly Type SingleCLRType = typeof(Single); public static readonly Type DoubleCLRType = typeof(Double); public static readonly Type CharCLRType = typeof(Char); public static readonly Type DecimalCLRType = typeof(Decimal); public static readonly Type IntPtrCLRType = typeof(IntPtr); public static readonly Type BoolCLRType = typeof(Boolean); public static readonly HashSet NumericTypes = new HashSet(new Type[] { Int8CLRType, UInt8CLRType, Int16CLRType, UInt16CLRType, Int32CLRType, UInt32CLRType, Int64CLRType, UInt64CLRType, SingleCLRType, DoubleCLRType, CharCLRType, BoolCLRType }); #endregion } } ================================================ FILE: jemalloc.Api/MemoryRef.cs ================================================ using System; using System.Collections.Generic; using System.Buffers; using System.Text; namespace jemalloc { public readonly ref struct MemoryRef where T : struct { internal MemoryRef(Guid guid, MemoryHandle h) { Id = guid; Handle = h; } private readonly Guid Id; private readonly MemoryHandle Handle; public bool Release() { return false; } } } ================================================ FILE: jemalloc.Api/NDArray.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace jemalloc { //WIP public class NDArray where T : struct { public NDArray(ulong points, int rank) { //data[3L] } //HugeArray[] data; } } ================================================ FILE: jemalloc.Api/SafeArray.cs ================================================ using System; using System.Collections.Generic; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; namespace jemalloc { public class SafeArray : SafeBuffer where T : struct, IEquatable { public SafeArray(int length, params T[] values) : base(length, values) {} } } ================================================ FILE: jemalloc.Api/SafeBuffer.cs ================================================ using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Numerics; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Text; namespace jemalloc { public abstract class SafeBuffer : SafeHandle, IRetainable, IDisposable, IEquatable>, IEnumerable where T : struct, IEquatable { #region Constructors protected SafeBuffer(int length, params T[] values) : base(IntPtr.Zero, true) { if (BufferHelpers.IsReferenceOrContainsReferences(typeof(T), out FieldInfo field)) { throw new ArgumentException($"Only structures without reference fields can be used with this class. The field {field.Name} has type {field.FieldType.Name}."); } if (values.Length > length) { throw new ArgumentException("The length of the list of values must be smaller or equal to the length of the buffer"); } SizeInBytes = NotAllocated; base.SetHandle(Allocate(length)); if (IsAllocated) { CopyFrom(values); } } #endregion #region Overriden members [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override bool ReleaseHandle() { bool r = Jem.Free(handle); if (!r) { return false; } else { handle = IntPtr.Zero; unsafe { voidPtr = (void *) 0; } Length = 0; SizeInBytes = 0; return true; } } public override bool IsInvalid => handle == IntPtr.Zero; #region Disposer [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override void Dispose(bool disposing) { if (disposing) { ThrowIfNotAllocatedOrInvalid(); ThrowIfRetained(); ReleaseHandle(); } base.Dispose(disposing); } #endregion #endregion #region Implemented members public void Retain() => Acquire(); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public bool Release() { if (IsNotAllocated || IsInvalid) { return false; } else { Jem.DecrementRefCount(handle); DangerousRelease(); return true; } } public bool Equals(SafeBuffer other) { ThrowIfNotAllocatedOrInvalid(); return this.handle == other.handle && this.Length == other.Length; } public IEnumerator GetEnumerator() => new SafeBufferEnumerator(this); IEnumerator IEnumerable.GetEnumerator() => new SafeBufferEnumerator(this); #endregion #region Properties public int Length { get; protected set; } public ulong SizeInBytes { get; protected set; } public bool IsNotAllocated => SizeInBytes == NotAllocated; public bool IsAllocated => !IsNotAllocated; public bool IsValid => !IsInvalid; public bool IsRetained => IsValid ? _RefCount > 0 : false; public bool IsVectorizable { get; protected set; } public Span Span { get { ThrowIfNotAllocatedOrInvalid(); return _Span; } } protected int _RefCount => Jem.GetRefCount(handle); protected unsafe Span _Span => new Span(voidPtr, Length); #endregion #region Methods #region Memory management protected unsafe virtual IntPtr Allocate(int length) { if (length <= 0) throw new ArgumentOutOfRangeException("length"); Contract.EndContractBlock(); ulong s = checked((uint)length * ElementSizeInBytes); handle = Jem.Calloc((uint)length, ElementSizeInBytes); if (handle != IntPtr.Zero) { voidPtr = handle.ToPointer(); Length = length; SizeInBytes = s; InitVector(); } return handle; } protected void InitVector() { if (IsNumeric && Length % VectorLength == 0 && SIMD && IsNumeric) { IsVectorizable = true; } else { IsVectorizable = false; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe ref T Read(int index) { // return (T*) (_ptr + byteOffset); return ref Unsafe.Add(ref Unsafe.AsRef(voidPtr), index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected unsafe void Write(int index, T value) { ref T v = ref Unsafe.Add(ref Unsafe.AsRef(voidPtr), index); v = value; } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public bool Acquire() { if (IsNotAllocated || IsInvalid) return false; bool success = false; DangerousAddRef(ref success); if (success) { Jem.IncrementRefCount(handle); } return success; } public bool Release(int n) { bool r = false; for (int i = 0; i < n; i++) { r = Release(); if (r) { continue; } else { return r; } } return r; } #endregion #region Values public bool EqualTo(T[] array) { ThrowIfNotAllocatedOrInvalid(); return _Span.SequenceEqual(new ReadOnlySpan(array)); } public void Fill(T value) { ThrowIfNotAllocatedOrInvalid(); Span.Fill(value); } public void CopyFrom(T[] array) { ThrowIfNotAllocatedOrInvalid(); Span s = _Span; new Span(array).CopyTo(_Span); } public T[] CopyToArray() { ThrowIfNotAllocatedOrInvalid(); T[] a = new T[this.Length]; _Span.CopyTo(new Span(a)); return a; } public unsafe Span GetSpan(int index = 0, int length = 1) where C : struct, IEquatable { ThrowIfNotAllocatedOrInvalid(); ThrowIfIndexOutOfRange(index); ulong s = (ulong) (index * ElementSizeInBytes + length * Unsafe.SizeOf()); if (s > SizeInBytes) { SizeIsOutOfRange(s); } void* p = BufferHelpers.Add(handle, index).ToPointer(); return new Span(p, length); } public unsafe Span Slice(int index) => GetSpan(index, Length - index); public unsafe Span Slice(int start, int end) { if (start >= end) { throw new ArgumentOutOfRangeException($"The end {end} of the slice must be greater than the start {start}."); } else { return GetSpan(start, end - start); } } public unsafe Vector GetVector() where C : struct, IEquatable, IConvertible, IComparable, IFormattable { ThrowIfNotAllocatedOrInvalid(); ThrowIfNotNumeric(); if (this.Length != Vector.Count) { throw new InvalidOperationException($"The length of the array must be {Vector.Count} elements to create a vector of type {CLRType.Name}."); } else { return Unsafe.Read>(voidPtr); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Vector GetSliceAsVector(int index) { ThrowIfNotAllocatedOrInvalid(); ThrowIfNotNumeric(); if ((index + VectorLength) > Length) { BufferIndexIsOutOfRange(index); } return Unsafe.Read>(BufferHelpers.Add(handle, index).ToPointer()); } #endregion [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotAllocatedOrInvalid() { if (IsNotAllocated) { BufferIsNotAllocated(); } else if (IsInvalid) { HandleIsInvalid(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfRetained() { if (IsRetained) { throw new InvalidOperationException($"SafeBuffer<{typeof(T)}[{Length}] has outstanding references."); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotAllocated() { if (IsNotAllocated) { BufferIsNotAllocated(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfCannotAcquire() { if (!Acquire()) { throw new InvalidOperationException("Could not acquire handle."); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotVectorisable() { if (!IsVectorizable) { BufferIsNotVectorisable(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfNotNumeric() { if (!IsNumeric) { BufferIsNotNumeric(); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfIndexOutOfRange(int index) { if (index < 0 || index >= Length) { BufferIndexIsOutOfRange(index); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private void ThrowIfLengthOutOfRange(int length) { if (length < 0 || length>= Length) { LengthIsOutOfRange(length); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void HandleIsInvalid() { throw new InvalidOperationException("The handle is invalid."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void BufferIsNotAllocated() { Contract.Assert(false, "Unallocated safe buffer used."); throw new InvalidOperationException("Unallocated safe buffer used."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void BufferIsNotVectorisable() { Contract.Assert(false, "Buffer is not vectorisable."); throw new InvalidOperationException("Buffer is not vectorisable."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void BufferIsNotNumeric() { Contract.Assert(false, "Buffer is not numeric."); throw new InvalidOperationException("Buffer is not numeric."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void BufferIndexIsOutOfRange(int index) { Contract.Assert(false, $"Index {index} into buffer is out of range."); throw new IndexOutOfRangeException($"Index {index} into buffer is out of range."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void LengthIsOutOfRange(int length) { Contract.Assert(false, $"Length {length} exceeds buffer length."); throw new Exception($"Length {length} exceeds buffer length."); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] private static void SizeIsOutOfRange(ulong size) { Contract.Assert(false, $"Length {size} exceeds buffer size."); throw new Exception($"Length {size} exceeds buffer size."); } #region Arithmetic public void VectorFill(T value) { Span> s = GetSpan>(); Vector fill = new Vector(value); for (int i = 0; i < s.Length; i++) { s[i] = fill; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void VectorMultiply(T value) { ThrowIfNotVectorisable(); Span> vectorSpan = GetSpan>(); Vector mulVector = new Vector(value); for (int i = 0; i < vectorSpan.Length; i++) { vectorSpan[i] = Vector.Multiply(vectorSpan[i], mulVector); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void VectorSqrt() { ThrowIfNotVectorisable(); Span> vector = GetSpan>(); for (int i = 0; i < vector.Length; i++) { vector[i] = Vector.SquareRoot(vector[i]); } } #endregion #endregion #region Operators public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowIfNotAllocatedOrInvalid(); ThrowIfIndexOutOfRange(index); return ref this.Read(index); } } #endregion #region Fields protected static readonly Type CLRType = typeof(T); protected static readonly T Element = default; protected static readonly uint ElementSizeInBytes = (uint) JemUtil.SizeOfStruct(); protected static readonly UInt64 NotAllocated = UInt64.MaxValue; protected static bool IsNumeric = JemUtil.IsNumericType(); protected static int VectorLength = IsNumeric ? Vector.Count : 0; protected static bool SIMD = Vector.IsHardwareAccelerated; protected internal unsafe void* voidPtr; //Debugger Display = {T[length]} protected string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, Length); #endregion } } ================================================ FILE: jemalloc.Api/SafeBufferEnumerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; namespace jemalloc { /// Enumerates the elements of a . public class SafeBufferEnumerator : IEnumerator, IEnumerator where T : struct, IEquatable { /// The SafeBuffer being enumerated. private readonly SafeBuffer _buffer; /// The next index to yield. private int _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal SafeBufferEnumerator(SafeBuffer buffer) { _buffer = buffer; _buffer.Acquire(); _index = -1; } /// Advances the enumerator to the next element of the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { int index = _index + 1; if (index < _buffer.Length) { _index = index; return true; } else { return false; } } public void Reset() { _index = -1; } /// Gets the element at the current position of the enumerator. object IEnumerator.Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer[_index]; } public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer[_index]; } void IDisposable.Dispose() { _buffer.Release(); } } } ================================================ FILE: jemalloc.Api/Utf8Buffer.cs ================================================ //B // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Buffers; using System.Buffers.Text; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; using System.Text.Primitives; using System.Text.Utf8; namespace jemalloc { [DebuggerDisplay("{ToString()} utf8")] public readonly struct Utf8Buffer : IEquatable, IRetainable, IDisposable { #region Constructors public Utf8Buffer(ReadOnlySpan utf8Bytes) => buffer = new FixedBuffer(utf8Bytes); public Utf8Buffer(Utf8Span utf8Span) : this(utf8Span.Bytes) { } public Utf8Buffer(string utf16String) { if (utf16String == null) { throw new ArgumentNullException(nameof(utf16String)); } if (utf16String == string.Empty) { buffer = new FixedBuffer(); } else { byte[] b = Encoding.UTF8.GetBytes(utf16String); buffer = new FixedBuffer(b); } } private Utf8Buffer(byte[] utf8Bytes) => buffer = new FixedBuffer(utf8Bytes); #endregion #region Implemented members public void Retain() => buffer.Retain(); public bool Release() => buffer.Release(); #region Disposer void IDisposable.Dispose() { if (IsRetained) { throw new InvalidOperationException($"FixedString<[{this.Length}] has outstanding references."); } else { Dispose(true); GC.SuppressFinalize(this); } } #endregion #endregion #region Overidden members public override int GetHashCode() => Span.GetHashCode(); public override string ToString() => Span.ToString(); public override bool Equals(object obj) { if (obj is Utf8Buffer) { return Equals((Utf8Buffer)obj); } if (obj is string) { return Equals((string)obj); } return false; } #endregion #region Properties public static Utf8Buffer Empty => s_empty; public bool IsEmpty => Bytes.Length == 0; public bool IsRetained => buffer.IsRetained; public int Length => buffer.Length; public ReadOnlySpan Bytes => buffer.Span; internal Utf8Span Span { get { ThrowIfInvalid(); return new Utf8Span(this); } } #endregion #region Operators public ref byte this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref this.buffer[index]; } public static bool operator ==(Utf8Buffer left, Utf8Buffer right) => left.Equals(right); public static bool operator !=(Utf8Buffer left, Utf8Buffer right) => !left.Equals(right); public static bool operator ==(Utf8Buffer left, Utf8Span right) => left.Equals(right); public static bool operator !=(Utf8Buffer left, Utf8Span right) => !left.Equals(right); public static bool operator ==(Utf8Span left, Utf8Buffer right) => right.Equals(left); public static bool operator !=(Utf8Span left, Utf8Buffer right) => !right.Equals(left); // TODO: do we like all these O(N) operators? public static bool operator ==(Utf8Buffer left, string right) => left.Equals(right); public static bool operator !=(Utf8Buffer left, string right) => !left.Equals(right); public static bool operator ==(string left, Utf8Buffer right) => right.Equals(left); public static bool operator !=(string left, Utf8Buffer right) => !right.Equals(left); public static implicit operator ReadOnlySpan(Utf8Buffer utf8String) => utf8String.Bytes; public static implicit operator Utf8Span(Utf8Buffer utf8String) => utf8String.Span; public static explicit operator Utf8Buffer(string utf16String) => new Utf8Buffer(utf16String); public static explicit operator string(Utf8Buffer utf8String) => utf8String.ToString(); #endregion #region Methods public void ThrowIfInvalid() => buffer.ThrowIfInvalid(); public bool Free() => buffer.Free(); public bool Equals(Utf8Buffer other) => Bytes.SequenceEqual(other.Bytes); public bool Equals(Utf8Span other) => Bytes.SequenceEqual(other.Bytes); public bool Equals(string other) => Span.Equals(other); public Utf8CodePointEnumerator GetEnumerator() => new Utf8CodePointEnumerator(buffer.Span); public int CompareTo(Utf8Buffer other) => Span.CompareTo(other); public int CompareTo(string other) => Span.CompareTo(other); public int CompareTo(Utf8Span other) => Span.CompareTo(other); public bool StartsWith(uint codePoint) => Span.StartsWith(codePoint); public bool StartsWith(Utf8Buffer value) => Span.StartsWith(value.Span); public bool StartsWith(Utf8Span value) => Span.StartsWith(value); public bool EndsWith(Utf8Buffer value) => Span.EndsWith(value.Span); public bool EndsWith(Utf8Span value) => Span.EndsWith(value); public bool EndsWith(uint codePoint) => Span.EndsWith(codePoint); #region Slicing // TODO: should Utf8String slicing operations return Utf8Span? // TODO: should we add slicing overloads that take char delimiters? // TODO: why do we even have Try versions? If the delimiter is not found, the result should be the original. public bool TrySubstringFrom(Utf8Buffer value, out Utf8Buffer result) { int idx = IndexOf(value); if (idx == StringNotFound) { result = default; return false; } result = Substring(idx); return true; } public bool TrySubstringFrom(uint codePoint, out Utf8Buffer result) { int idx = IndexOf(codePoint); if (idx == StringNotFound) { result = default; return false; } result = Substring(idx); return true; } public bool TrySubstringTo(Utf8Buffer value, out Utf8Buffer result) { int idx = IndexOf(value); if (idx == StringNotFound) { result = default; return false; } result = Substring(0, idx); return true; } public bool TrySubstringTo(uint codePoint, out Utf8Buffer result) { int idx = IndexOf(codePoint); if (idx == StringNotFound) { result = default; return false; } result = Substring(0, idx); return true; } #endregion #region Index-based operations // TODO: should we even have index based operations? // TODO: should we have search (e.g. IndexOf) overlaods that take char? public Utf8Buffer Substring(int index) => index == 0 ? this : Substring(index, Bytes.Length - index); public Utf8Buffer Substring(int index, int length) { if (length == 0) { return Empty; } if (index == 0 && length == Bytes.Length) return this; return new Utf8Buffer(buffer.Span.Slice(index, length)); } public int IndexOf(Utf8Buffer value) => Bytes.IndexOf(value.Bytes); public int IndexOf(uint codePoint) => Span.IndexOf(codePoint); public int IndexOf(string s) => Span.IndexOf(new Utf8Span(Encoding.UTF8.GetBytes(s))); public int LastIndexOf(Utf8Buffer value) => Span.LastIndexOf(value.Span); public int LastIndexOf(uint codePoint) => Span.LastIndexOf(codePoint); public bool TryFormat(Span buffer, out int written, StandardFormat format = default, SymbolTable symbolTable = null) { if (!format.IsDefault) throw new ArgumentOutOfRangeException(nameof(format)); if (symbolTable == SymbolTable.InvariantUtf8) { written = Bytes.Length; return Bytes.TryCopyTo(buffer); } return symbolTable.TryEncode(Bytes, buffer, out var consumed, out written); } #endregion /* // TODO: unless we change the type of Trim to Utf8Span, this double allocates. public FixedUtf8String Trim() => TrimStart().TrimEnd(); // TODO: implement Utf8String.Trim(uint[]) public FixedString Trim(uint[] codePoints) => throw new NotImplementedException(); public FixedString TrimStart() { Utf8CodePointEnumerator it = GetEnumerator(); while (it.MoveNext() &&Char.IsWhiteSpace(it.Current)) { } return Substring(it.PositionInCodeUnits); } public FixedUtf8String TrimStart(uint[] codePoints) { if (codePoints == null || codePoints.Length == 0) return TrimStart(); // Trim Whitespace Utf8CodePointEnumerator it = GetEnumerator(); while (it.MoveNext()) { if(Array.IndexOf(codePoints, it.Current) == -1){ break; } } return Substring(it.PositionInCodeUnits); } // TODO: do we even want this overload? System.String does not have an overload that takes string public FixedString TrimStart(FixedString characters) { if (characters == Empty) { // Trim Whitespace return TrimStart(); } Utf8CodePointEnumerator it = GetEnumerator(); Utf8CodePointEnumerator itPrefix = characters.GetEnumerator(); while (it.MoveNext()) { bool found = false; // Iterate over prefix set while (itPrefix.MoveNext()) { if (it.Current == itPrefix.Current) { // Character found, don't check further found = true; break; } } if (!found) { // Reached the end, char was not found break; } itPrefix.Reset(); } return Substring(it.PositionInCodeUnits); } public FixedString TrimEnd() { var it = new Utf8CodePointReverseEnumerator(Bytes); while (it.MoveNext() && Unicode.IsWhitespace(it.Current)) { } return Substring(0, it.PositionInCodeUnits); } // TODO: implement Utf8String.TrimEnd(uint[]) public FixedString TrimEnd(uint[] codePoints) => throw new NotImplementedException(); // TODO: do we even want this overload? System.String does not have an overload that takes string public FixedString TrimEnd(FixedString characters) { if (characters == Empty) { // Trim Whitespace return TrimEnd(); } var it = new Utf8CodePointReverseEnumerator(Bytes); Utf8CodePointEnumerator itPrefix = characters.GetEnumerator(); while (it.MoveNext()) { bool found = false; // Iterate over prefix set while (itPrefix.MoveNext()) { if (it.Current == itPrefix.Current) { // Character found, don't check further found = true; break; } } if (!found) { // Reached the end, char was not found break; } itPrefix.Reset(); } return Substring(0, it.PositionInCodeUnits); } */ #region Disposer private void Dispose(bool disposing) { buffer.Free(); } #endregion #endregion #region Fields //private readonly byte[] _buffer; private readonly FixedBuffer buffer; private const int StringNotFound = -1; static Utf8Buffer s_empty = new Utf8Buffer(string.Empty); #endregion } } ================================================ FILE: jemalloc.Api/jemalloc.Api.csproj ================================================ netstandard2.0 AnyCPU;x64 jemalloc latest true true jemalloc.NET 0.2.4-alpha Allister Beharry jemalloc.NET 0.2.4.0 0.2.4.0 https://github.com/allisterb/jemalloc.NET https://github.com/allisterb/jemalloc.NET/LICENSE https://github.com/allisterb/jemalloc.NET Native memory manager for .NET latest x64 true ..\x64\Debug\ x64 true ..\x64\Debug\ latest x64 latest true ..\x64\Release\ portable true ================================================ FILE: jemalloc.Api/jemalloc.cs ================================================ using System; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; namespace jemalloc { #region Delegates [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] public unsafe delegate global::System.IntPtr ExtentAllocT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, ulong _3, bool* _4, bool* _5, uint _6); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public unsafe delegate bool ExtentDallocT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, [MarshalAs(UnmanagedType.I1)] bool _3, uint _4); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] public unsafe delegate void ExtentDestroyT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, [MarshalAs(UnmanagedType.I1)] bool _3, uint _4); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public unsafe delegate bool ExtentCommitT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, ulong _3, ulong _4, uint _5); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public unsafe delegate bool ExtentDecommitT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, ulong _3, ulong _4, uint _5); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public unsafe delegate bool ExtentPurgeT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, ulong _3, ulong _4, uint _5); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public unsafe delegate bool ExtentSplitT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, ulong _3, ulong _4, [MarshalAs(UnmanagedType.I1)] bool _5, uint _6); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public unsafe delegate bool ExtentMergeT(global::System.IntPtr _0, global::System.IntPtr _1, ulong _2, global::System.IntPtr _3, ulong _4, [MarshalAs(UnmanagedType.I1)] bool _5, uint _6); public delegate void JeMallocMessageAction(string m); #endregion public unsafe static partial class Jem { public partial struct __Internal { [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_malloc")] internal static extern global::System.IntPtr JeMalloc([In] ulong size); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_calloc")] internal static extern global::System.IntPtr JeCalloc([In] ulong num, [In] ulong size); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_posix_memalign")] internal static extern int JePosixMemalign(void** memptr, ulong alignment, ulong size); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_aligned_alloc")] internal static extern global::System.IntPtr JeAlignedAlloc(ulong alignment, ulong size); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_realloc")] internal static extern global::System.IntPtr JeRealloc(global::System.IntPtr ptr, ulong size); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_free")] internal static extern void JeFree(global::System.IntPtr ptr); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_mallocx")] internal static extern global::System.IntPtr JeMallocx(ulong size, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_rallocx")] internal static extern global::System.IntPtr JeRallocx(global::System.IntPtr ptr, ulong size, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_xallocx")] internal static extern ulong JeXallocx(global::System.IntPtr ptr, ulong size, ulong extra, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_sallocx")] internal static extern ulong JeSallocx(global::System.IntPtr ptr, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_dallocx")] internal static extern void JeDallocx(global::System.IntPtr ptr, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_sdallocx")] internal static extern void JeSdallocx(global::System.IntPtr ptr, ulong size, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_nallocx")] internal static extern ulong JeNallocx(ulong size, int flags); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_mallctl")] internal static extern int JeMallctl([MarshalAs(UnmanagedType.LPStr)] [In] string name, global::System.IntPtr oldp, ulong* oldlenp, global::System.IntPtr newp, ulong newlen); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_mallctlnametomib")] internal static extern int JeMallctlnametomib([MarshalAs(UnmanagedType.LPStr)] string name, ulong* mibp, ulong* miblenp); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_mallctlbymib")] internal static extern int JeMallctlbymib(ulong* mib, ulong miblen, global::System.IntPtr oldp, ulong* oldlenp, global::System.IntPtr newp, ulong newlen); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_malloc_stats_print")] internal static extern void JeMallocStatsPrint(global::System.IntPtr write_cb, global::System.IntPtr je_cbopaque, [MarshalAs(UnmanagedType.LPStr)] string opts); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint="je_malloc_usable_size")] internal static extern ulong JeMallocUsableSize(global::System.IntPtr ptr); [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(global::System.Runtime.InteropServices.CallingConvention.Cdecl)] internal unsafe delegate void JeMallocMessageCallback(global::System.IntPtr _0, [MarshalAs(UnmanagedType.LPStr)] string _1); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "je_set_malloc_conf", CharSet = CharSet.Ansi)] internal static extern void JeSetMallocConf(IntPtr ptr); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "je_get_malloc_conf", CharSet = CharSet.Ansi)] internal static extern IntPtr JeGetMallocConf(); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "je_get_malloc_message_ptr")] internal static extern global::System.IntPtr JeGetMallocMessagePtr(); [SuppressUnmanagedCodeSecurity] [DllImport("jemallocd", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "je_set_malloc_message_ptr")] internal static extern void JeSetMallocMessagePtr(global::System.IntPtr p); internal static JeMallocMessageCallback JeMallocMessage { get { var ret = __Internal.JeGetMallocMessagePtr(); return ret == IntPtr.Zero ? null : (__Internal.JeMallocMessageCallback)Marshal.GetDelegateForFunctionPointer(ret, typeof(__Internal.JeMallocMessageCallback)); } set { IntPtr ptr = value == null ? global::System.IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(value); __Internal.JeSetMallocMessagePtr(ptr); } } } } } ================================================ FILE: jemalloc.Benchmarks/BenchmarkStatisticColumn.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Running; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Columns; namespace jemalloc.Benchmarks { public class BenchmarkStatisticColumn : IColumn { #region Constructors public BenchmarkStatisticColumn(string columnName, string legend) { ColumnName = columnName; Legend = legend; } #endregion #region Implemented properties // // Summary: // An unique identificator of the column. If there are several columns with the // same Id, only one of them will be shown in the summary. public string Id => ColumnName; // // Summary: // Defines order of column in the same category. public int PriorityInCategory { get; } = 99; // Summary: // Defines how to format column's value public UnitType UnitType { get; } = UnitType.Size; // // Summary: // Column description. public string Legend { get; protected set; } public bool AlwaysShow => true; public bool IsNumeric => true; public ColumnCategory Category => ColumnCategory.Statistics; #endregion #region Implemented methods public bool IsDefault(Summary summary, Benchmark benchmark) => false; public string GetValue(Summary summary, Benchmark benchmark) { if (JemUtil.BenchmarkStatistics.ContainsKey($"{benchmark.Target.Method.Name}_{ColumnName}_{benchmark.Parameters[0].Value.GetHashCode()}")) { return JemUtil.BenchmarkStatistics[$"{benchmark.Target.Method.Name}_{ColumnName}_{benchmark.Parameters[0].Value.GetHashCode()}"]; } else if (JemUtil.BenchmarkStatistics.ContainsKey($"{benchmark.Target.Method.Name}_{ColumnName}")) { return JemUtil.BenchmarkStatistics[$"{benchmark.Target.Method.Name}_{ColumnName}"]; } else return string.Empty; /* string[] catgegories = new string[2] { "Managed", "Unmanaged" }; foreach (string c in catgegories) { if (benchmark.Target.Categories.Contains(c)) { if (JemUtil.BenchmarkStatistics.ContainsKey($"{c}_{ColumnName}")) { return JemUtil.BenchmarkStatistics[$"{c}_{ColumnName}"]; } } }*/ } public bool IsAvailable(Summary summary) { foreach(Benchmark benchmark in summary.Benchmarks) { if (JemUtil.BenchmarkStatistics.ContainsKey($"{benchmark.Target.Method.Name}_{ColumnName}")) { return true; } } return false; } public string GetValue(Summary summary, Benchmark benchmark, ISummaryStyle style) => GetValue(summary, benchmark); public string ColumnName { get; } #endregion #region Overriden methods public override string ToString() => ColumnName; #endregion #region Available columns public static readonly IColumn PrivateMemory = new BenchmarkStatisticColumn("PrivateMemory", "Total memory allocated for process(native and managed, 1KB = 1024B)"); public static readonly IColumn WorkingSet = new BenchmarkStatisticColumn("WorkingSet", "Memory that is currently being used by process(native and managed, 1KB = 1024B)"); public static readonly IColumn JemAllocated = new BenchmarkStatisticColumn("JemAllocated", "Total memory allocated by jemalloc(native only, inclusive, 1KB = 1024B)"); public static readonly IColumn JemResident = new BenchmarkStatisticColumn("JemResident", "Resident memory allocated by jemalloc(native only, inclusive, 1KB = 1024B)"); public static readonly IColumn ThreadCycles = new BenchmarkStatisticColumn("ThreadCycles", "RDTSC time elapsed."); public static readonly IColumn ISPCResult = new BenchmarkStatisticColumn("ISPCResult", "Result obtained with Intel ISPC compiler on a AVX2 CPU."); public static readonly IColumn ISPCResult2 = new BenchmarkStatisticColumn("ISPCResult2", "Result obtained with Intel ISPC compiler on a SSE4.2 CPU."); #endregion } } ================================================ FILE: jemalloc.Benchmarks/Benchmarks/FixedBufferVsManagedArray.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Order; using BenchmarkDotNet.Loggers; namespace jemalloc.Benchmarks { [OrderProvider(methodOrderPolicy: MethodOrderPolicy.Declared)] public class FixedBufferVsManagedArrayBenchmark : JemBenchmark where T : struct, IEquatable, IComparable, IConvertible { public int ArraySize => Parameter; public readonly T fill = typeof(T) == typeof(TestUDT) ? JemUtil.ValToGenericStruct(TestUDT.MakeTestRecord(JemUtil.Rng)) : GM.Random(); public readonly (T factor, T max) mul = GM.RandomMultiplyFactorAndValue(); public override void GlobalSetup() { DebugInfoThis(); base.GlobalSetup(); Info($"Array size is {ArraySize}."); T[] managedArray = new T[ArraySize]; SetValue("managedArray", managedArray); FixedBuffer nativeArray = new FixedBuffer(ArraySize); nativeArray.Acquire(); SetValue("nativeArray", nativeArray); if (Operation == Operation.FILL) { Info($"Array fill value is {fill}."); SetValue("fill", fill); } else if (Operation == Operation.MATH) { Info($"Array fill value is {mul.max}."); nativeArray.Fill(mul.max); new Span(managedArray).Fill(mul.max); SetValue("fill", mul.max); Info($"Array multiply factor is {mul.factor}."); SetValue("mul", mul.factor); } } #region Fill [Benchmark(Description = "Fill a managed array with a single value.")] [BenchmarkCategory("Fill")] public void FillManagedArray() { T[] managedArray = GetValue("managedArray"); T fill = GetValue("fill"); for (int i = 0; i < managedArray.Length; i++) { managedArray[i] = fill; } } [Benchmark(Description = "Fill a FixedBuffer on the system unmanaged heap with a single value.")] [BenchmarkCategory("Fill")] public void FillFixedBuffer() { FixedBuffer nativeArray = GetValue>("nativeArray"); T fill = GetValue("fill"); nativeArray.Fill(fill); } [Benchmark(Description = "Create and Fill a managed array with a single value.")] [BenchmarkCategory("Fill")] public void FillManagedArrayWithCreate() { T[] managedArray = new T[ArraySize]; T fill = GetValue("fill"); for (int i = 0; i < managedArray.Length; i++) { managedArray[i] = fill; } managedArray = null; } [Benchmark(Description = "Create and Fill a FixedBuffer on the system unmanaged heap with a single value.")] [BenchmarkCategory("Fill")] public void FillFixedBufferWithCreate() { FixedBuffer nativeArray = new FixedBuffer(ArraySize); T fill = GetValue("fill"); nativeArray.Fill(fill); nativeArray.Free(); } [GlobalCleanup(Target = nameof(FillFixedBuffer))] public void FillValidateAndCleanup() { InfoThis(); T[] managedArray = GetValue("managedArray"); FixedBuffer nativeArray = GetValue>("nativeArray"); T fill = GetValue("fill"); for (int i = 0; i < ArraySize / 1000; i++) { int index = GM.Rng.Next(0, ArraySize); if (!nativeArray[index].Equals(fill)) { Log.WriteLineError($"Native array at index {index} is {nativeArray[index]} not {fill}."); throw new Exception(); } } nativeArray.Release(); nativeArray.Free(); managedArray = null; RemoveValue("managedArray"); RemoveValue("nativeArray"); RemoveValue("fill"); } #endregion #region Arithmetic [Benchmark(Description = "Multiply all values of a managed array with a single value.")] [BenchmarkCategory("Arithmetic")] public void ArithmeticMutiplyManagedArray() { T mul = GetValue("mul"); T fill = GetValue("fill"); T[] m = GetValue("managedArray"); new Span(m).Fill(fill); for (int i = 0; i < m.Length; i++) { m[i] = GM.Multiply(m[i], mul); } this.SetStatistic($"{nameof(ArithmeticMutiplyManagedArray)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); } [Benchmark(Description = "Vector multiply all values of a native array with a single value.")] [BenchmarkCategory("Arithmetic")] public void ArithmeticMultiplyNativeArray() { DebugInfoThis(); T mul = GetValue("mul"); T fill = GetValue("fill"); FixedBuffer array = GetValue>("nativeArray"); array.Fill(fill); array.VectorMultiply(mul); this.SetStatistic($"{nameof(ArithmeticMultiplyNativeArray)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); } [GlobalCleanup(Target = nameof(ArithmeticMultiplyNativeArray))] public void ArithmeticMultiplyValidateAndCleanup() { InfoThis(); int index; FixedBuffer nativeArray = GetValue>("nativeArray"); T[] managedArray = GetValue("managedArray"); T mul = GetValue("mul"); T fill = GetValue("fill"); T val = GM.Multiply(fill, mul); for (int i = 0; i < ArraySize % 100; i++) { index = GM.Rng.Next(0, ArraySize); if (!nativeArray[index].Equals(val)) { Log.WriteLineError($"Native array at index {index} is {nativeArray[index]} not {val}."); throw new Exception(); } else if (!managedArray[index].Equals(val)) { Log.WriteLineError($"Managed array at index {index} is {managedArray[index]} not {val}."); throw new Exception(); } } managedArray = null; nativeArray.Release(); nativeArray.Free(); RemoveValue("managedArray"); RemoveValue("nativeArray"); RemoveValue("fill"); RemoveValue("mul"); } #endregion #region Create [BenchmarkCategory("Create")] [Benchmark(Description = "Create arrays on the .NET LOH", Baseline = true)] public void CreateManagedArray() { T[] someData = new T[ArraySize]; } [BenchmarkCategory("Create")] [Benchmark(Description = "Create SafeArrays on the system unmanaged heap")] public void CreateNativeArray() { SafeArray array = new SafeArray(ArraySize); } #endregion } } ================================================ FILE: jemalloc.Benchmarks/Benchmarks/HugeNativeVsManagedArray.cs ================================================ using System; using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Order; namespace jemalloc.Benchmarks { [OrderProvider(methodOrderPolicy: MethodOrderPolicy.Declared)] public class HugeNativeVsManagedArrayBenchmark : JemBenchmark where T : struct, IEquatable, IComparable, IConvertible { public ulong ArraySize => Parameter; public readonly int MaxManagedArraySize = 2146435071; public T fill = typeof(T) == typeof(TestUDT) ? JemUtil.ValToGenericStruct(TestUDT.MakeTestRecord(JemUtil.Rng)) : GM.Random(); public (T factor, T max) mul = GM.RandomMultiplyFactorAndValue(); public override void GlobalSetup() { DebugInfoThis(); base.GlobalSetup(); Info($"Unmanaged array size is {ArraySize}."); Info($"Managed array size is {MaxManagedArraySize}."); T[] managedArray = new T[MaxManagedArraySize]; SetValue("managedArray", managedArray); HugeArray hugeArray = new HugeArray(ArraySize); hugeArray.Acquire(); SetValue("hugeArray", hugeArray); if (Operation == Operation.FILL) { SetValue("fill", fill); Info($"Array fill value is {fill}."); SetValue("fill", fill); } } #region Fill [Benchmark(Description = "Fill a managed array with the maximum size [2146435071] with a single value.")] [BenchmarkCategory("Fill")] public void FillManagedArray() { DebugInfoThis(); T[] managedArray = GetValue("managedArray"); T fill = GetValue("fill"); for (int i = 0; i < managedArray.Length; i++) { managedArray[i] = fill; } } [Benchmark(Description = "Fill a HugeArray on the system unmanaged heap with a single value.")] [BenchmarkCategory("Fill")] public void FillHugeNativeArray() { DebugInfoThis(); HugeArray hugeArray = GetValue>("hugeArray"); T fill = GetValue("fill"); hugeArray.Fill(fill); } [Benchmark(Description = "Create and Fill a managed array with a single value.")] [BenchmarkCategory("Fill")] public void FillManagedArrayWithCreate() { DebugInfoThis(); T[] managedArray = new T[MaxManagedArraySize]; T fill = GetValue("fill"); for (int i = 0; i < managedArray.Length; i++) { managedArray[i] = fill; } managedArray = null; } [Benchmark(Description = "Create and Fill a HugeArray on the system unmanaged heap with a single value.")] [BenchmarkCategory("Fill")] public void FillHugeNativeArrayWithCreate() { DebugInfoThis(); HugeArray hugeArray = new HugeArray(ArraySize); T fill = GetValue("fill"); hugeArray.Fill(fill); hugeArray.Close(); hugeArray = null; } [GlobalCleanup(Target = nameof(FillHugeNativeArray))] public void CleanupFillArray() { InfoThis(); T[] managedArray = GetValue("managedArray"); HugeArray hugeArray = GetValue>("hugeArray"); T fill = GetValue("fill"); ulong index = GM.Random(ArraySize); if (!hugeArray[index].Equals(fill)) { Error($"Native array at index {index} is {hugeArray[index]} not {fill}."); throw new Exception(); } hugeArray.Release(); managedArray = null; RemoveValue("managedArray"); RemoveValue("hugeArray"); RemoveValue("fill"); } #endregion #region Create [BenchmarkCategory("Create")] [Benchmark(Description = "Create an array with the maximum size [2146435071] on the .NET managed heap")] public void CreateManagedArray() { T[] someData = new T[2146435071]; } [BenchmarkCategory("Create")] [Benchmark(Description = "Create a HugeArray on the system unmanaged heap")] public void CreateHugeNativeArray() { HugeArray array = new HugeArray(ArraySize); array.Release(); } #endregion } } ================================================ FILE: jemalloc.Benchmarks/Benchmarks/MallocVsArray.cs ================================================ using System; using System.Collections.Generic; using System.Runtime; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Order; namespace jemalloc.Benchmarks { [OrderProvider(methodOrderPolicy: MethodOrderPolicy.Declared)] public class MallocVsArrayBenchmark : JemBenchmark where T : struct, IEquatable, IComparable, IConvertible { public int ArraySize => Parameter; public const int InitialLargeBlockSize = 64 * 1024 * 1024; //64 MB public const int SmallBlockSize = 90000; public int LoopCount => Parameter; public override void GlobalSetup() { base.GlobalSetup(); Jem.Init("dirty_decay_ms:0,muzzy_decay_ms:0,junk:false,tcache:false"); } #region Create [Benchmark(Description = "Create array of data on the managed heap.")] [BenchmarkCategory("Create")] public void CreateManagedArray() { T[] someData = new T[ArraySize]; SetMemoryStatistics(); someData = null; } [Benchmark(Description = "Malloc buffer and Span on the system managed heap.")] [BenchmarkCategory("Create")] public void CreateSpan() { ulong msize = (ulong)(ArraySize * JemUtil.SizeOfStruct()); IntPtr ptr = Jem.Malloc(msize); unsafe { Span s = new Span(ptr.ToPointer(), ArraySize); } SetMemoryStatistics(); Jem.Free(ptr); } #endregion #region Fill [Benchmark(Description = "Fill array of data on the managed heap.")] [BenchmarkCategory("Fill")] public void FillManagedArray() { T fill = GM.Random(); T[] someData = new T[ArraySize]; for (int i = 0; i < someData.Length; i++) { someData[i] = fill; } T r = someData[ArraySize / 2]; } [Benchmark(Description = "Fill memory on the system unmanaged heap using Span with a single value.")] [BenchmarkCategory("Fill")] public void FillSpan() { T fill = GM.Random(); ulong msize = (ulong)(ArraySize * JemUtil.SizeOfStruct()); IntPtr ptr = Jem.Malloc(msize); Span s = JemUtil.PtrToSpan(ptr, ArraySize); s.Fill(fill); T r = s[ArraySize / 2]; Jem.Free(ptr); } #endregion #region Fragment [Benchmark(Description = "Run an allocation pattern that won't fragment the Large Object Heap.", Baseline = true)] [BenchmarkCategory("Managed", "Fragment")] public void FragmentLOHBaseline() { int largeBlockSize = InitialLargeBlockSize; int i = 0; try { for (i = 0; i < LoopCount; i++) { T[] bigBlock = new T[largeBlockSize]; T[] smallBlock = new T[SmallBlockSize]; largeBlockSize = largeBlockSize + (1 * 1024 * 1024); } this.SetStatistic($"{nameof(FragmentLOHBaseline)}_WorkingSet", JemUtil.PrintBytes(JemUtil.ProcessWorkingSet)); this.SetStatistic($"{nameof(FragmentLOHBaseline)}_JemResident", JemUtil.PrintBytes(Jem.ResidentBytes)); this.SetStatistic($"{nameof(FragmentLOHBaseline)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); this.SetStatistic($"{nameof(FragmentLOHBaseline)}_JemAllocated", JemUtil.PrintBytes(Jem.AllocatedBytes)); } catch (OutOfMemoryException) { Error($"OOM at index {i}."); throw; } finally { GC.Collect(); } } [Benchmark(Description = "Run an allocation pattern that fragments the Large Object Heap.")] [BenchmarkCategory("Managed", "Fragment")] public void FragmentLOH() { int largeBlockSize = InitialLargeBlockSize; List smallBlocks = new List(); int i = 0; try { for (i = 0; i < LoopCount; i++) { T[] bigBlock = new T[largeBlockSize]; T[] smallBlock = new T[SmallBlockSize]; smallBlocks.Add(smallBlock); largeBlockSize = largeBlockSize + (1 * 1024 * 1024); } this.SetStatistic($"{nameof(FragmentLOH)}_WorkingSet", JemUtil.PrintBytes(JemUtil.ProcessWorkingSet)); this.SetStatistic($"{nameof(FragmentLOH)}_JemResident", JemUtil.PrintBytes(Jem.ResidentBytes)); this.SetStatistic($"{nameof(FragmentLOH)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); this.SetStatistic($"{nameof(FragmentLOH)}_JemAllocated", JemUtil.PrintBytes(Jem.AllocatedBytes)); } catch (OutOfMemoryException) { Error($"OOM at index {i}."); throw; } finally { smallBlocks = null; GC.Collect(); } } [Benchmark(Description = "Run an allocation pattern that fragments the Large Object Heap with regular GC compaction.")] [BenchmarkCategory("Managed", "Fragment")] public void FragmentLOHWithCompact() { int largeBlockSize = InitialLargeBlockSize; List smallBlocks = new List(); int i = 0; try { for (i = 0; i < LoopCount; i++) { if ((i + 1)% 10 == 0) { //Info("Compacting LOH."); GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); } T[] bigBlock = new T[largeBlockSize]; T[] smallBlock = new T[SmallBlockSize]; smallBlocks.Add(smallBlock); largeBlockSize = largeBlockSize + (1 * 1024 * 1024); } this.SetStatistic($"{nameof(FragmentLOHWithCompact)}_WorkingSet", JemUtil.PrintBytes(JemUtil.ProcessWorkingSet)); this.SetStatistic($"{nameof(FragmentLOHWithCompact)}_JemResident", JemUtil.PrintBytes(Jem.ResidentBytes)); this.SetStatistic($"{nameof(FragmentLOHWithCompact)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); this.SetStatistic($"{nameof(FragmentLOHWithCompact)}_JemAllocated", JemUtil.PrintBytes(Jem.AllocatedBytes)); } catch (OutOfMemoryException) { Error($"OOM at index {i}."); throw; } finally { smallBlocks = null; GC.Collect(); } } [Benchmark(Description = "Run an allocation pattern that won't fragment the unmanaged heap.")] [BenchmarkCategory("Unmanaged", "Fragment")] public void FragmentNativeHeapBaseline() { Info($"Dirty decay time: {Jem.GetMallCtlSInt64("arenas.dirty_decay_ms")} ms"); int largeBlockSize = InitialLargeBlockSize; int i = 0; FixedBuffer bigBlock = default; try { for (i = 0; i < LoopCount; i++) { bigBlock = new FixedBuffer(largeBlockSize); FixedBuffer smallBlock = new FixedBuffer(SmallBlockSize); int j = JemUtil.Rng.Next(0, ArraySize); T r = GM.Random(); smallBlock[j] = r; if (!smallBlock[j].Equals(r)) { throw new Exception($"Cannot validate small block at index {i}."); } if (!smallBlock.Free()) throw new Exception("Cannot free smallBlock."); if (!bigBlock.Free()) throw new Exception("Cannot free bigBlock."); largeBlockSize = largeBlockSize + (1 * 1024 * 1024); } this.SetStatistic($"{nameof(FragmentNativeHeapBaseline)}_WorkingSet", JemUtil.PrintBytes(JemUtil.ProcessWorkingSet)); this.SetStatistic($"{nameof(FragmentNativeHeapBaseline)}_JemResident", JemUtil.PrintBytes(Jem.ResidentBytes)); this.SetStatistic($"{nameof(FragmentNativeHeapBaseline)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); this.SetStatistic($"{nameof(FragmentNativeHeapBaseline)}_JemAllocated", JemUtil.PrintBytes(Jem.AllocatedBytes)); } catch (OutOfMemoryException) { Info(Jem.MallocStats); Error($"Out-of-Memory at index {i} with large block size {largeBlockSize}."); throw; } finally { GC.Collect(); } } [Benchmark(Description = "Run an allocation pattern that fragments the unmanaged heap.")] [BenchmarkCategory("Unmanaged", "Fragment")] public void FragmentNativeHeap() { Info($"Dirty decay time: {Jem.GetMallCtlSInt64("arenas.dirty_decay_ms")} ms"); int largeBlockSize = InitialLargeBlockSize; SafeArray> smallBlocks = new SafeArray>(LoopCount); int i = 0; FixedBuffer bigBlock = default; try { for (i = 0; i < LoopCount; i++) { bigBlock = new FixedBuffer(largeBlockSize); FixedBuffer smallBlock = new FixedBuffer(SmallBlockSize); int j = JemUtil.Rng.Next(0, ArraySize); T r = GM.Random(); smallBlock[j] = r; smallBlocks[i] = smallBlock; if (!smallBlocks[i][j].Equals(r)) { throw new Exception($"Cannot validate small block at index {i}."); } if (!bigBlock.Free()) throw new Exception("Cannot free bigBlock."); largeBlockSize = largeBlockSize + (1 * 1024 * 1024); } this.SetStatistic($"{nameof(FragmentNativeHeap)}_WorkingSet", JemUtil.PrintBytes(JemUtil.ProcessWorkingSet)); this.SetStatistic($"{nameof(FragmentNativeHeap)}_JemResident", JemUtil.PrintBytes(Jem.ResidentBytes)); this.SetStatistic($"{nameof(FragmentNativeHeap)}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); this.SetStatistic($"{nameof(FragmentNativeHeap)}_JemAllocated", JemUtil.PrintBytes(Jem.AllocatedBytes)); foreach (FixedBuffer b in smallBlocks) { if (!b.Free()) throw new Exception($"Cannot free small block at index {i}."); } } catch (OutOfMemoryException) { Info(Jem.MallocStats); Error($"Out-of-Memory at index {i} with large block size {largeBlockSize}."); foreach (FixedBuffer b in smallBlocks) { b.Free(); } throw; } finally { GC.Collect(); } } #endregion } } ================================================ FILE: jemalloc.Benchmarks/Benchmarks/SafeVsManagedArray.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Order; using BenchmarkDotNet.Loggers; namespace jemalloc.Benchmarks { [OrderProvider(methodOrderPolicy: MethodOrderPolicy.Declared)] public class SafeVsManagedArrayBenchmark : JemBenchmark where T : struct, IEquatable, IComparable, IConvertible { public int ArraySize => Parameter; public readonly T fill = typeof(T) == typeof(TestUDT) ? JemUtil.ValToGenericStruct(TestUDT.MakeTestRecord(JemUtil.Rng)) : GM.Random(); public readonly (T factor, T max) mul = GM.RandomMultiplyFactorAndValue(); public override void GlobalSetup() { DebugInfoThis(); base.GlobalSetup(); Info($"Array size is {ArraySize}."); T[] managedArray = new T[ArraySize]; SetValue("managedArray", managedArray); SafeArray nativeArray = new SafeArray(ArraySize); SetValue("nativeArray", nativeArray); if (Operation == Operation.FILL) { Info($"Array fill value is {fill}."); SetValue("fill", fill); } else if (Operation == Operation.MATH) { Info($"Array fill value is {mul.max}."); nativeArray.Fill(mul.max); new Span(managedArray).Fill(mul.max); SetValue("fill", mul.max); Info($"Array multiply factor is {mul.factor}."); SetValue("mul", mul.factor); } } #region Fill [Benchmark(Description = "Fill a managed array with a single value.")] [BenchmarkCategory("Fill")] public void FillManagedArray() { DebugInfoThis(); T[] managedArray = GetValue("managedArray"); T fill = GetValue("fill"); for (int i = 0; i < managedArray.Length; i++) { managedArray[i] = fill; } } [Benchmark(Description = "Fill a SafeArray on the system unmanaged heap with a single value.")] [BenchmarkCategory("Fill")] public void FillNativeArray() { DebugInfoThis(); SafeArray nativeArray = GetValue>("nativeArray"); T fill = GetValue("fill"); nativeArray.Fill(fill); } [Benchmark(Description = "Create and Fill a managed array with a single value.")] [BenchmarkCategory("Fill")] public void FillManagedArrayWithCreate() { DebugInfoThis(); T[] managedArray = new T[ArraySize]; T fill = GetValue("fill"); for (int i = 0; i < managedArray.Length; i++) { managedArray[i] = fill; } managedArray = null; } [Benchmark(Description = "Create and Fill a SafeArray on the system unmanaged heap with a single value.")] [BenchmarkCategory("Fill")] public void FillNativeArrayWithCreate() { DebugInfoThis(); SafeArray nativeArray = new SafeArray(ArraySize); T fill = GetValue("fill"); nativeArray.Fill(fill); nativeArray.Close(); } [GlobalCleanup(Target = nameof(FillNativeArrayWithCreate))] public void FillArrayValidateAndCleanup() { InfoThis(); T[] managedArray = GetValue("managedArray"); SafeArray nativeArray = GetValue>("nativeArray"); T fill = GetValue("fill"); int index = JemUtil.Rng.Next(0, ArraySize); if (!nativeArray[index].Equals(managedArray[index])) { Log.WriteLineError($"Native array at index {index} is {nativeArray[index]} not {fill}."); throw new Exception(); } managedArray = null; nativeArray.Close(); RemoveValue("managedArray"); RemoveValue("nativeArray"); RemoveValue("fill"); } #endregion #region Arithmetic [Benchmark(Description = "Multiply all values of a managed array with a single value.")] [BenchmarkCategory("Arithmetic")] public void ArithmeticMutiplyManagedArray() { T mul = GetValue("mul"); T fill = GetValue("fill"); T[] m = GetValue("managedArray"); new Span(m).Fill(fill); for (int i = 0; i < m.Length; i++) { m[i] = GM.Multiply(m[i], mul); } } [Benchmark(Description = "Vector multiply all values of a native array with a single value.")] [BenchmarkCategory("Arithmetic")] public void ArithmeticMultiplyNativeArray() { T mul = GetValue("mul"); T fill = GetValue("fill"); SafeArray array = GetValue>("nativeArray"); array.Fill(fill); array.VectorMultiply(mul); } [GlobalCleanup(Target = nameof(ArithmeticMultiplyNativeArray))] public void ArithmeticMultiplyValidateAndCleanup() { InfoThis(); int index = GM.Rng.Next(0, ArraySize); SafeArray nativeArray = GetValue>("nativeArray"); T[] managedArray = GetValue("managedArray"); T mul = GetValue("mul"); T fill = GetValue("fill"); T val = GM.Multiply(fill, mul); if (!nativeArray[index].Equals(val)) { Log.WriteLineError($"Native array at index {index} is {nativeArray[index]} not {val}."); throw new Exception(); } else if (!managedArray[index].Equals(val)) { Log.WriteLineError($"Managed array at index {index} is {managedArray[index]} not {val}."); throw new Exception(); } managedArray = null; nativeArray.Close(); RemoveValue("managedArray"); RemoveValue("nativeArray"); RemoveValue("fill"); RemoveValue("mul"); } #endregion } } ================================================ FILE: jemalloc.Benchmarks/Benchmarks/Vector.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Numerics; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Text; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Order; using BenchmarkDotNet.Diagnosers; namespace jemalloc.Benchmarks { [OrderProvider(methodOrderPolicy: MethodOrderPolicy.Declared)] public class VectorBenchmark : JemBenchmark where T : struct, IEquatable, IComparable, IConvertible { private const int _Mandelbrot_Width = 768, _Mandelbrot_Height = 512; public int ArraySize => Parameter; public int Scale => Parameter; public int Mandelbrot_Width => _Mandelbrot_Width * Scale; public int Mandelbrot_Height => _Mandelbrot_Height * Scale; public int MandelbrotArraySize => Mandelbrot_Width * Mandelbrot_Height; readonly Vector One = Vector.One; readonly Vector Zero = Vector.Zero; readonly Vector Limit = new Vector(4); #region Mandelbrot [GlobalSetup(Target = nameof(MandelbrotManaged))] public void MandelbrotSetup() { byte[] managedArray = new byte[MandelbrotArraySize]; SetValue("managedArray", managedArray); int[] managed3Array = new int[MandelbrotArraySize]; SetValue("managed3Array", managed3Array); int[] managed5Array = new int[MandelbrotArraySize]; SetValue("managed5Array", managed5Array); int[] managed7Array = new int[MandelbrotArraySize]; SetValue("managed7Array", managed5Array); FixedBuffer nativeArray = new FixedBuffer(MandelbrotArraySize); SetValue("nativeArray", nativeArray); FixedBuffer native2Array = new FixedBuffer(MandelbrotArraySize); SetValue("native2Array", native2Array); FixedBuffer native3Array = new FixedBuffer(MandelbrotArraySize); SetValue("native3Array", native3Array); FixedBuffer native4Array = new FixedBuffer(MandelbrotArraySize); SetValue("native4Array", native4Array); FixedBuffer native5Array = new FixedBuffer(MandelbrotArraySize); SetValue("native5Array", native4Array); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded serial using managed memory v1.", Baseline = true)] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotManaged() { byte[] managedArray = GetValue("managedArray"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotManagedv1(ref managedArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded serial using unmanaged memory v1.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotUnmanagedv1() { FixedBuffer nativeArray = GetValue>("nativeArray"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotUnmanagedv1(ref nativeArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } /*Not working [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded linear.", Baseline = true)] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotManagedv0() { byte[] managedArray = GetValue("managed0Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotManagedv0(ref managedArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"{nameof(MandelbrotManagedv0)}_ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"{nameof(MandelbrotManagedv0)}_ISPCResult", GetISPCResult()); } */ /* [Benchmark(Description = "Create Mandelbrot plot bitmap with dimensions 768 x 512 single-threaded using managed memory vBGNetCore8.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotManagedv2() { uulong start = JemUtil.GetCurrentThreadCycles(); byte[] managedArray = _MandelbrotManagedv2(); ulong end = JemUtil.GetCurrentThreadCycles(); SetValue("managed2Array", managedArray); SetStatistic($"{nameof(MandelbrotManagedv2)}_ThreadCycles", JemUtil.PrintSize(end - start)); } */ [Benchmark(Description = "Create Mandelbrot plot bitmap multi-threaded using managed memory v4.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotManagedv4() { ulong start = JemUtil.GetCurrentThreadCycles(); int[] managedArray = _MandelbrotManagedv4(); ulong end = JemUtil.GetCurrentThreadCycles(); SetValue("managed4Array", managedArray); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCTasksResult()); SetStatistic($"ISPCResult2", GetISPCTasksResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded using unmanaged memory v2.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotUnmanagedv2() { FixedBuffer nativeArray = GetValue>("native2Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotUnmanagedv2(ref nativeArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded using managed memory v5.")] [BenchmarkCategory("Mandelbrot")] public void MandelbrotManagedv5() { int[] managedArray = GetValue("managed5Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotManagedv5(ref managedArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded using managed memory v3.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotManagedv3() { int[] managedArray = GetValue("managed3Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotManagedv3(ref managedArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap multi-threaded using managed memory v6.")] [BenchmarkCategory("Mandelbrot")] public void MandelbrotManagedv6() { ulong start = JemUtil.GetCurrentThreadCycles(); int[] managedArray = _MandelbrotManagedv6(); ulong end = JemUtil.GetCurrentThreadCycles(); SetValue("managed6Array", managedArray); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCTasksResult()); SetStatistic($"ISPCResult2", GetISPCTasksResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded using managed memory v7.")] [BenchmarkCategory("Mandelbrot")] public void MandelbrotManagedv7() { int[] managedArray = GetValue("managed7Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotManagedv7(ref managedArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap multi-threaded using managed memory v8.")] [BenchmarkCategory("Mandelbrot")] public void MandelbrotManagedv8() { ulong start = JemUtil.GetCurrentThreadCycles(); int[] managedArray = _MandelbrotManagedv8(); ulong end = JemUtil.GetCurrentThreadCycles(); SetValue("managed8Array", managedArray); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCTasksResult()); SetStatistic($"ISPCResult2", GetISPCTasksResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded using unmanaged memory v3.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotUnmanagedv3() { FixedBuffer nativeArray = GetValue>("native3Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotUnmanagedv3(ref nativeArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap single-threaded using unmanaged memory v5.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotUnmanagedv5() { FixedBuffer nativeArray = GetValue>("native5Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotUnmanagedv5(ref nativeArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCResult()); SetStatistic($"ISPCResult2", GetISPCResult2()); } [Benchmark(Description = "Create Mandelbrot plot bitmap multi-threaded using unmanaged memory v4.")] [BenchmarkCategory("Mandelbrot")] public unsafe void MandelbrotUnmanagedv4() { FixedBuffer nativeArray = GetValue>("native4Array"); ulong start = JemUtil.GetCurrentThreadCycles(); _MandelbrotUnmanagedv4(nativeArray); ulong end = JemUtil.GetCurrentThreadCycles(); SetStatistic($"ThreadCycles", JemUtil.PrintSize(end - start)); SetStatistic($"ISPCResult", GetISPCTasksResult()); SetStatistic($"ISPCResult2", GetISPCTasksResult2()); } [GlobalCleanup(Target = nameof(MandelbrotUnmanagedv4))] public void MandelbrotValidateAndCleanup() { byte[] managedArray = GetValue("managedArray"); //byte[] managed2Array = GetValue("managed2Array"); int[] managed3Array = GetValue("managed3Array"); int[] managed4Array = GetValue("managed4Array"); int[] managed5Array = GetValue("managed5Array"); int[] managed6Array = GetValue("managed6Array"); int[] managed7Array = GetValue("managed7Array"); int[] managed8Array = GetValue("managed8Array"); FixedBuffer nativeArray = GetValue>("nativeArray"); FixedBuffer native2Array = GetValue>("native2Array"); FixedBuffer native3Array = GetValue>("native3Array"); FixedBuffer native4Array = GetValue>("native4Array"); FixedBuffer native5Array = GetValue>("native5Array"); for (int i = 0; i < MandelbrotArraySize; i++) { if (!nativeArray[i].Equals(managedArray[i])) { Error($"Native array at index {i} is {nativeArray[i]} not {managedArray[i]}."); throw new Exception(); } if (native2Array[i] <= 255) { if (!managedArray[i].Equals((byte)native2Array[i])) { Error($"Native array 2 at index {i} is {native2Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (native3Array[i] <= 255) { if (!managedArray[i].Equals((byte)native3Array[i])) { Error($"Native array 3 at index {i} is {native3Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (native4Array[i] <= 255) { if (!managedArray[i].Equals((byte)native4Array[i])) { Error($"Native array 4 at index {i} is {native4Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (native5Array[i] <= 255) { if (!managedArray[i].Equals((byte)native5Array[i])) { Error($"Native array 5 at index {i} is {native5Array[i]} not {managedArray[i]}."); throw new Exception(); } } /* if (!managedArray[i].Equals(managed2Array[i])) { Error($"Managed2 array at index {i} is {managed2Array[i]} not {managedArray[i]}."); throw new Exception(); } */ if (managed3Array[i] <= 255) { if (!managedArray[i].Equals((byte) managed3Array[i])) { Error($"Managed3 array at index {i} is {managed3Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (managed4Array[i] <= 255) { if (!managedArray[i].Equals((byte) managed4Array[i])) { Error($"Managed4 array at index {i} is {managed4Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (managed5Array[i] <= 255) { if (!managedArray[i].Equals( (byte) managed5Array[i])) { Error($"Managed5 array at index {i} is {managed5Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (managed6Array[i] <= 255) { if (!managedArray[i].Equals((byte)managed6Array[i])) { Error($"Managed6 array at index {i} is {managed6Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (managed7Array[i] <= 255) { if (!managedArray[i].Equals((byte)managed7Array[i])) { Error($"Managed7 array at index {i} is {managed7Array[i]} not {managedArray[i]}."); throw new Exception(); } } if (managed8Array[i] <= 255) { if (!managedArray[i].Equals((byte)managed8Array[i])) { Error($"Managed8 array at index {i} is {managed8Array[i]} not {managedArray[i]}."); throw new Exception(); } } } WriteMandelbrotPPM(managedArray, "mandelbrot-managed-v1.ppm"); //WriteMandelbrotPPM(managed0Array, "mandelbrot-managed-v0.ppm"); //WriteMandelbrotPPM(managed2Array, "mandelbrot-managed2.ppm"); WriteMandelbrotPPM(managed3Array, "mandelbrot-managed-v3.ppm"); WriteMandelbrotPPM(managed4Array, "mandelbrot-managed-v4.ppm"); WriteMandelbrotPPM(managed5Array, "mandelbrot-managed-v5.ppm"); WriteMandelbrotPPM(managed6Array, "mandelbrot-managed-v6.ppm"); WriteMandelbrotPPM(managed7Array, "mandelbrot-managed-v7.ppm"); WriteMandelbrotPPM(managed8Array, "mandelbrot-managed-v8.ppm"); WriteMandelbrotPPM(nativeArray.AcquireSpan(), "mandelbrot-unmanaged-v1.ppm"); WriteMandelbrotPPM(native2Array.AcquireSpan(), "mandelbrot-unmanaged-v2.ppm"); WriteMandelbrotPPM(native3Array.AcquireSpan(), "mandelbrot-unmanaged-v3.ppm"); WriteMandelbrotPPM(native4Array.AcquireSpan(), "mandelbrot-unmanaged-v4.ppm"); WriteMandelbrotPPM(native5Array.AcquireSpan(), "mandelbrot-unmanaged-v4.ppm"); nativeArray.Release(); native2Array.Release(); native3Array.Release(); native4Array.Release(); native5Array.Release(); } #endregion #region Fill [GlobalSetup(Target = nameof(FillManagedArray))] public void _FillGlobalSetup() { Info("Vector width is {0}.", Vector.Count); T[] mArray = new T[ArraySize]; SetValue("managedArray", mArray); FixedBuffer nativeArray = new FixedBuffer(ArraySize); SetValue("nativeArray", nativeArray); T fill = GM.Random(); SetValue("fill", fill); Info("Fill value is {0}.", fill); } [Benchmark(Description = "Serial fill managed memory array.")] [BenchmarkCategory("Fill")] public void FillManagedArray() { T[] managedArray = GetValue("managedArray"); Array.Fill(managedArray, GetValue("fill")); } [Benchmark(Description = "Vector fill managed memory array.")] [BenchmarkCategory("Fill")] public void FillManagedArrayUsingVector() { T[] managedArray = GetValue("managedArray"); Span> s = new Span(managedArray).NonPortableCast>(); Vector fill = new Vector(GetValue("fill")); for (int i = 0; i < s.Length; i ++) { s[i] = fill; } } [Benchmark(Description = "Vector fill unmanaged memory array.")] [BenchmarkCategory("Fill")] public void FillUnmanagedArray() { FixedBuffer nativeBuffer = GetValue>("nativeArray"); nativeBuffer.VectorFill(GetValue("fill")); } [GlobalCleanup(Target = nameof(FillUnmanagedArray))] public void _FillGlobalValidateAndCleanup() { FixedBuffer nativeBuffer = GetValue>("nativeArray"); T[] managedArray = GetValue("managedArray"); for (int i =0; i < managedArray.Length; i++) if (!managedArray[i].Equals(nativeBuffer[i])) { throw new Exception($"Native array at index {i} is {nativeBuffer[i]} not {managedArray[i]}."); } } #endregion #region Test [GlobalSetup(Target = nameof(TestManagedArrayLessThan))] public void TestGlobalSetup() { Info("Vector width is {0}.", Vector.Count); T[] mArray = new T[ArraySize]; SetValue("managedArray", mArray); FixedBuffer nativeArray = new FixedBuffer(ArraySize); SetValue("nativeArray", nativeArray); for (int i = 0; i < ArraySize / 100; i++) { mArray[i] = GM.Random(); nativeArray[i] = mArray[i]; } SetValue("cmp", GM.Random()); } [Benchmark(Description = "Serial test managed memory array less than.")] [BenchmarkCategory("Test")] public void TestManagedArrayLessThan() { T cmp = GetValue("cmp"); T[] managedArray = GetValue("managedArray"); int lessThanResult = Array.FindIndex(managedArray, (a) => a.CompareTo(cmp) >= 0); SetValue("managedLessThanResult", lessThanResult); } [Benchmark(Description = "Vector test managed memory array less than.")] [BenchmarkCategory("Test")] public void TestVectorManagedArrayLessThan() { int lessThanResult = -1; int c = JemUtil.VectorLength(); int i; T cmp = GetValue("cmp"); T[] managedArray = GetValue("managedArray"); Vector O = Vector.One; Vector _cmp = new Vector(cmp); for (i = 0; i < managedArray.Length - c; i+= c) { Vector v = new Vector(managedArray, i); Vector vcmp = Vector.LessThan(v, _cmp); if (vcmp == O) { continue; } else { for (int j = 0; j < c; j++) { if (vcmp[j].Equals(default)) { lessThanResult = i + j; break; } } break; } } for (; i < c; ++i) { if (managedArray[i].CompareTo(cmp) >= 0) { lessThanResult = i; break; } } SetValue("managedVectorLessThanResult", lessThanResult); } [Benchmark(Description = "Vector test native array less than.")] [BenchmarkCategory("Test")] public void TestNativeArrayLessThan() { FixedBuffer nativeArray = GetValue>("nativeArray"); T cmp = GetValue("cmp"); nativeArray.VectorLessThanAll(cmp, out int lessThanResult); SetValue("nativeLessThanResult", lessThanResult); } [GlobalCleanup(Target = nameof(TestNativeArrayLessThan))] public void _TestGlobalValidateAndCleanup() { var m = GetValue("managedLessThanResult"); var mv = GetValue("managedVectorLessThanResult"); var n = GetValue("nativeLessThanResult"); Info("{0}, {1}, {2}", m, n, mv); if (m != mv) { Error("{0}, {1}", m, mv); throw new Exception(); } else if (n != mv) { Error("{0}, {1}", mv, n); throw new Exception(); } if (m != n) { Error("{0}, {1}", m, n); throw new Exception(); } } #endregion #region Implementations int VectorWidth = Vector.Count; private byte[] _MandelbrotManagedv0(ref byte[] output) { float[] B = new float[2] { Mandelbrot_Width, Mandelbrot_Height }; float[] C0 = new float[2] { -2, -1 }; float[] C1 = new float[2] { 1, 1 }; float[] D = new float[2] { (C1[0] - C0[0]) / B[0], (C1[1] - C0[1]) / B[1] }; float[] V = new float[2]; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i++) { index = unchecked(j * Mandelbrot_Width + i); V[0] = C0[0] + (i * D[0]); V[1] = C0[1] + (j * D[1]); output[index] = GetByte(V, 256); } } return output; } private byte[] _MandelbrotManagedv1(ref byte[] output) { Vector2 B = new Vector2(Mandelbrot_Width, Mandelbrot_Height); Vector2 C0 = new Vector2(-2, -1); Vector2 C1 = new Vector2(1, 1); Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i++) { Vector2 P = new Vector2(i, j); index = unchecked(j * Mandelbrot_Width + i); Vector2 V = C0 + (P * D); output[index] = GetByte(V, 256); } } return output; byte GetByte(Vector2 c, int count) { Vector2 z = c; int i; for (i = 0; i < count; i++) { if (z.LengthSquared() > 4f) { return (byte)i; } Vector2 w = z * z; z = c + new Vector2(w.X - w.Y, 2f * z.X * z.Y); } return (byte)(i - 1); } } private unsafe byte[] _MandelbrotManagedv2() { var size = Mandelbrot_Width; var Crb = new double[size + 2]; var lineLength = size >> 3; var data = new byte[size * lineLength]; fixed (double* pCrb = &Crb[0]) fixed (byte* pdata = &data[0]) { var value = new Vector( new double[] { 0, 1, 0, 0, 0, 0, 0, 0 } ); var invN = new Vector(2.0 / size); var onePtFive = new Vector(1.5); var step = new Vector(2); for (var i = 0; i < size; i += 2) { Unsafe.Write(pCrb + i, value * invN - onePtFive); value += step; } var _Crb = pCrb; var _pdata = pdata; Parallel.For(0, size, y => { var Ciby = _Crb[y] + 0.5; for (var x = 0; x < lineLength; x++) { _pdata[y * lineLength + x] = GetByte(_Crb + x * 8, Ciby); } }); return data; byte GetByte(double* _pCrb, double Ciby) { var res = 0; for (var i = 0; i < 8; i += 2) { var vCrbx = Unsafe.Read>(_pCrb + i); var vCiby = new Vector(Ciby); var Zr = vCrbx; var Zi = vCiby; int b = 0, j = 49; do { var nZr = Zr * Zr - Zi * Zi + vCrbx; var ZrZi = Zr * Zi; Zi = ZrZi + ZrZi + vCiby; Zr = nZr; var t = Zr * Zr + Zi * Zi; if (t[0] > 4.0) { b |= 2; if (b == 3) break; } if (t[1] > 4.0) { b |= 1; if (b == 3) break; } } while (--j > 0); res = (res << 2) + b; } return (byte)(res ^ -1); } } } private int[] _MandelbrotManagedv3(ref int[] output) { int VectorWidth = Vector.Count; float[] Vectors = new float[6]; float[] P = new float[VectorWidth * 2]; Span Vector2Span = new Span(Vectors).NonPortableCast(); //Lets us read individual Vector2 Span> PSpan = new Span(P).NonPortableCast>(); //Lets us read individual Vectors Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { P[h] = C0.X + (D.X * (i + h)); P[h + VectorWidth] = C0.Y + (D.Y * j); } index = unchecked(j * Mandelbrot_Width + i); Vector Vre = PSpan[0]; Vector Vim = PSpan[1]; ; Vector outputVector = GetByte(ref Vre, ref Vim, 256); outputVector.CopyTo(output, index); } } return output; } private int[] _MandelbrotManagedv4() { int VectorWidth = Vector.Count; int[] output = new int[MandelbrotArraySize]; float[] Vectors = new float[6]; Span Vector2Span = new Span(Vectors).NonPortableCast(); //Lets us read individual Vector2 Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; Vector2 C0 = Vector2Span[0]; Vector2 C1 = Vector2Span[1]; Vector2 B = Vector2Span[2]; Vector2 D = (C1 - C0) / B; for (int j = 0; j < Mandelbrot_Height; j++) { Parallel.ForEach(MandelbrotBitmapLocation(j), (p) => { float[] Pre = new float[VectorWidth]; float[] Pim = new float[VectorWidth]; for (int h = 0; h < VectorWidth; h++) { Pre[h] = C0.X + (D.X * (p.Item1 + h)); Pim[h] = C0.Y + (D.Y * p.Item2); } int index = unchecked(p.Item2 * Mandelbrot_Width + p.Item1); Vector Vre = new Vector(Pre); Vector Vim = new Vector(Pim); Vector outputVector = GetByte(ref Vre, ref Vim, 256); outputVector.CopyTo(output, index); }); } return output; } private unsafe int[] _MandelbrotManagedv5(ref int[] output) { int VectorWidth = Vector.Count; Vector C0re = new Vector(-2); Vector C0im = new Vector(-1); Vector C1re = Vector.One; Vector C1im = Vector.One; Vector Bx = new Vector(Mandelbrot_Width); Vector By = new Vector(Mandelbrot_Height); Vector Dx = (C1re - C0re) / Bx; Vector Dy = (C1im - C0im) / By; int index; Vector Pre = new Vector(); Vector Pim; Span sPre = new Span(Unsafe.AsPointer(ref Pre), VectorWidth); for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { sPre[h] = C0re[0] + (Dx[0] * (i + h)); } Pim = C0im + (Dy * j); index = unchecked(j * Mandelbrot_Width + i); Vector outputVector = GetByte(ref Pre, ref Pim, 256); outputVector.CopyTo(output, index); } } return output; } private int[] _MandelbrotManagedv6() { int VectorWidth = Vector.Count; int[] output = new int[MandelbrotArraySize]; Vector C0re = new Vector(-2); Vector C0im = new Vector(-1); Vector C1re = Vector.One; Vector C1im = Vector.One; Vector Bx = new Vector(Mandelbrot_Width); Vector By = new Vector(Mandelbrot_Height); Vector Dx = (C1re - C0re) / Bx; Vector Dy = (C1im - C0im) / By; for (int j = 0; j < Mandelbrot_Height; j++) { Parallel.ForEach(MandelbrotBitmapLocation(j), (p) => { int i = p.Item1; Vector Pre = new Vector(); unsafe { Span sPre = new Span(Unsafe.AsPointer(ref Pre), VectorWidth); for (int h = 0; h < VectorWidth; h++) { sPre[h] = C0re[0] + (Dx[0] * (p.Item1 + h)); } } Vector Pim = C0im + (Dy * p.Item2); int index = unchecked(p.Item2 * Mandelbrot_Width + p.Item1); Vector outputVector = GetByte(ref Pre, ref Pim, 256); outputVector.CopyTo(output, index); }); } return output; } private unsafe int[] _MandelbrotManagedv7(ref int[] output) { int VectorWidth = Vector.Count; float[] Vectors = new float[6]; Span Vector2Span = new Span(Vectors).NonPortableCast(); //Lets us read individual Vector2 Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; Vector2 C0 = Vector2Span[0]; Vector2 C1 = Vector2Span[1]; Vector2 B = Vector2Span[2]; Vector2 D = (C1 - C0) / B; int index; float[] PreArray = new float[VectorWidth]; float[] PimArray = new float[VectorWidth]; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { PreArray[h] = C0.X + (D.X * (i + h)); PimArray[h] = C0.Y + (D.Y * j); } Vector Pre = new Vector(PreArray); Vector Pim = new Vector(PimArray); index = unchecked(j * Mandelbrot_Width + i); Vector outputVector = GetByte(ref Pre, ref Pim, 256); outputVector.CopyTo(output, index); } } return output; } private int[] _MandelbrotManagedv8() { int[] output = new int[MandelbrotArraySize]; Vector2 C0 = new Vector2(-2, -1); Vector2 C1 = new Vector2(1, 1); Vector2 B = new Vector2(Mandelbrot_Width, Mandelbrot_Height); Vector2 D = (C1 - C0) / B; Parallel.For(0, Mandelbrot_Height, (j) => { float[] scanLine = new float[Mandelbrot_Width]; for (int x = 0; x < Mandelbrot_Width; x++) { scanLine[x] = C0.X + (D.X * (x)); } Vector Vim = new Vector(C0.Y + (D.Y * j)); int index; for (int h = 0; h < Mandelbrot_Width; h += VectorWidth) { Vector Vre = new Vector(scanLine, h); index = unchecked(j * Mandelbrot_Width + h); Vector outputVector = GetByte(ref Vre, ref Vim, 256); outputVector.CopyTo(output, index); } }); return output; } private FixedBuffer _MandelbrotUnmanagedv1(ref FixedBuffer output) { FixedBuffer Vectors = new FixedBuffer(10); Span Vector2Span = Vectors.AcquireWriteSpan().NonPortableCast(); //Lets us read individual vectors Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; ref Vector2 P = ref Vector2Span[3]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i++) { Vectors[6] = i; Vectors[7] = j; index = unchecked(j * Mandelbrot_Width + i); Vector2 V = C0 + (P * D); output[index] = GetByte(ref V, 256); } } Vectors.Release(); return output; } private FixedBuffer _MandelbrotUnmanagedv2(ref FixedBuffer output) { int VectorWidth = Vector.Count; Span> outputVectorSpan = output.AcquireVectorWriteSpan(); Vector One = Vector.One; Vector Zero = Vector.Zero; FixedBuffer Vectors = new FixedBuffer(6); FixedBuffer P = new FixedBuffer(VectorWidth * 2); Span Vector2Span = Vectors.AcquireWriteSpan().NonPortableCast(); //Lets us read individual Vector2 Span> PSpan = P.AcquireWriteSpan().NonPortableCast>(); //Lets us read individual Vectors Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { P[h] = C0.X + (D.X * (i + h)); } index = unchecked(j * Mandelbrot_Width + i); Vector Vre = PSpan[0]; Vector Vim = new Vector(C0.Y + (D.Y * j)); Vector outputVector = GetByte(ref Vre, ref Vim, 256); outputVectorSpan[index / Vector.Count] = outputVector; } } Vectors.Release(); Vectors.Free(); P.Release(); P.Free(); output.Release(); return output; } private FixedBuffer _MandelbrotUnmanagedv3(ref FixedBuffer output) { int VectorWidth = Vector.Count; FixedBuffer P = new FixedBuffer(VectorWidth); Vector2 C0 = new Vector2(-2, -1); Vector2 C1 = new Vector2(1, 1); Vector2 B = new Vector2(Mandelbrot_Width, Mandelbrot_Height); Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { P[h] = C0.X + (D.X * (i + h)); } index = unchecked(j * Mandelbrot_Width + i); Vector Vre = P.Read>(0); Vector Vim = new Vector(C0.Y + (D.Y * j)); Vector outputVector = GetByte(ref Vre, ref Vim, 256); output.Write(index, ref outputVector); } } P.Free(); return output; } private unsafe void _MandelbrotUnmanagedv4(FixedBuffer output) { Vector2 C0 = new Vector2(-2, -1); Vector2 C1 = new Vector2(1, 1); Vector2 B = new Vector2(Mandelbrot_Width, Mandelbrot_Height); Vector2 D = (C1 - C0) / B; Parallel.For(0, Mandelbrot_Height, (j) => { FixedBuffer scanLine = new FixedBuffer(Mandelbrot_Width); for (int x = 0; x < Mandelbrot_Width; x++) { float px = C0.X + (D.X * (x)); scanLine.Write(x, ref px); } Vector Vim = new Vector(C0.Y + (D.Y * j)); for (int h = 0; h < Mandelbrot_Width; h+=VectorWidth) { int index = j * Mandelbrot_Width + h; Vector Vre = scanLine.Read>(h); Vector outputVector = GetByte(ref Vre, ref Vim, 256); output.Write(index, ref outputVector); } scanLine.Free(); }); return; } private unsafe void _MandelbrotUnmanagedv5(ref FixedBuffer output) { Vector2 C0 = new Vector2(-2, -1); Vector2 C1 = new Vector2(1, 1); Vector2 B = new Vector2(Mandelbrot_Width, Mandelbrot_Height); Vector2 D = (C1 - C0) / B; FixedBuffer scanLine = new FixedBuffer(Mandelbrot_Width); for (int j = 0; j < Mandelbrot_Height; j++) { for (int x = 0; x < Mandelbrot_Width; x++) { float px = C0.X + (D.X * (x)); scanLine.Write(x, ref px); } Vector Vim = new Vector(C0.Y + (D.Y * j)); for (int h = 0; h < Mandelbrot_Width; h += VectorWidth) { int index = j * Mandelbrot_Width + h; Vector Vre = scanLine.Read>(h); Vector outputVector = GetByte(ref Vre, ref Vim, 256); output.Write(index, ref outputVector); } } scanLine.Free(); return; } [MethodImpl(MethodImplOptions.AggressiveInlining)] byte GetByte(float[] c, int maxIterations) { float[] Z = new float[] { c[0], c[1] }; // make a copy int i; for (i = 0; i < maxIterations; i++) { if ((Z[0] * Z[0]) + (Z[1] * Z[1]) > 4f) { return (byte)i; } float z0 = Z[0]; float z1 = Z[1]; float[] w = new float[] { (z0 * z0), (z1 * z1) }; Z[0] = c[0] + w[0] - w[1]; Z[1] = c[1] + 2f * z0 * z1; } return (byte)(i - 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] byte GetByte(ref Vector2 c, int max_iterations) { Vector2 z = c; //make a copy int i; for (i = 0; i < max_iterations; i++) { if (z.LengthSquared() > 4f) { return (byte)i; } Vector2 w = z * z; z = c + new Vector2(w.X - w.Y, 2f * z.X * z.Y); } return (byte)(i - 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] Vector GetByte(ref Vector Cre, ref Vector Cim, int max_iterations) { Vector Zre = Cre; //make a copy Vector Zim = Cim; //make a copy Vector MaxIterations = new Vector(max_iterations); Vector Increment = One; Vector I; for (I = Zero; Increment != Zero; I += Vector.Abs(Increment)) { Vector S = SquareAbs(Zre, Zim); Increment = Vector.LessThanOrEqual(S, Limit) & Vector.LessThan(I, MaxIterations); if (Increment.Equals(Zero)) { break; } else { Vector Tre = Zre; Zre = Cre + (Zre * Zre - Zim * Zim); Zim = Cim + 2f * Tre * Zim; } } return I; } [MethodImpl(MethodImplOptions.AggressiveInlining)] Vector SquareAbs(Vector Vre, Vector Vim) { return (Vre * Vre) + (Vim * Vim); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteMandelbrotPPM(ReadOnlySpan output, string name) { using (StreamWriter sw = new StreamWriter(name)) { sw.Write("P6\n"); sw.Write(string.Format("{0} {1}\n", Mandelbrot_Width, Mandelbrot_Height)); sw.Write("255\n"); sw.Close(); } using (BinaryWriter bw = new BinaryWriter(new FileStream(name, FileMode.Append))) { for (int i = 0; i < Mandelbrot_Width * Mandelbrot_Height; i++) { byte b = (output[i] & 0x01) == 1 ? (byte)20 : (byte)240; bw.Write(b); bw.Write(b); bw.Write(b); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteMandelbrotPPM(ReadOnlySpan output, string name) { using (StreamWriter sw = new StreamWriter(name)) { sw.Write("P6\n"); sw.Write(string.Format("{0} {1}\n", Mandelbrot_Width, Mandelbrot_Height)); sw.Write("255\n"); sw.Close(); } using (BinaryWriter bw = new BinaryWriter(new FileStream(name, FileMode.Append))) { for (int i = 0; i < Mandelbrot_Width * Mandelbrot_Height; i++) { byte b = (output[i] & 0x01) == 1 ? (byte)20 : (byte)240; bw.Write(b); bw.Write(b); bw.Write(b); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteMandelbrotPPM(int[] output, string name) { using (StreamWriter sw = new StreamWriter(name)) { sw.Write("P6\n"); sw.Write(string.Format("{0} {1}\n", Mandelbrot_Width, Mandelbrot_Height)); sw.Write("255\n"); sw.Close(); } using (BinaryWriter bw = new BinaryWriter(new FileStream(name, FileMode.Append))) { for (int i = 0; i < Mandelbrot_Width * Mandelbrot_Height; i++) { byte b = (output[i] & 0x01) == 1 ? (byte)20 : (byte)240; bw.Write(b); bw.Write(b); bw.Write(b); } } } public IEnumerable> MandelbrotBitmapLocation(int j) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { yield return (i, j); } } public string GetISPCBaselineResult() { switch (Scale) { case 1: return "328 M"; case 3: return "3 G"; case 6: return "11.9 G"; default: return string.Empty; } } public string GetISPCResult() { switch (Scale) { case 1: return "26.8 M"; case 3: return "236 M"; case 6: return "940 M"; default: return string.Empty; } } public string GetISPCTasksResult() { switch (Scale) { case 1: return "5.6 M"; case 3: return "48.2M"; case 6: return "196.2M"; default: return string.Empty; } } public string GetISPCResult2() { switch (Scale) { case 1: return "95 M"; case 3: return "819.5 M"; case 6: return "3.3 G"; default: return string.Empty; } } public string GetISPCTasksResult2() { switch (Scale) { case 1: return "7.3 M"; case 3: return "61.6 M"; case 6: return "244.6 M"; default: return string.Empty; } } #endregion } } ================================================ FILE: jemalloc.Benchmarks/Design.md ================================================ ## Catgeories 1. Malloc: Benchmark low-level malloc operations and creating Span 2. Arrays: Bench 1. Allocate uint arrays of various sizes from small to very large. Benchmark how long it takes 2. Generate random values and assign to random indices of the array. Benchmark. 3. Allocate memory and use span Things to test: Fragmentation: allocate small and large sizes consecutively. LOH stress: https://www.red-gate.com/simple-talk/dotnet/net-framework/the-dangers-of-the-large-object-heap/ Create an Int32[100000000]; //goes on the LOH Create an Int32[30000]; //smaller but still big enough to go on the LOH Throw away the big array but keep the small array. //Creates a 100000000 hole on the LOH immediately followed by an in-use region for the small array. next iteration will create another big array slightly bigger the first. //This won't fit in the 'hole' of the previous array. The heap must be extended now for the new array to fit. Loop for a while. We should find it taking longer and longer to allocate the large arrays and using lots of memory until eventually an OOM happens. We can do the exact same operations with SafeArray and then compare the performance. ================================================ FILE: jemalloc.Benchmarks/JemBenchmark.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using BenchmarkDotNet; using BenchmarkDotNet.Order; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes.Columns; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Code; using BenchmarkDotNet.Loggers; namespace jemalloc.Benchmarks { #region Enums public enum Category { MALLOC, NARRAY, HUGEARRAY, BUFFER, VECTOR } public enum Operation { CREATE, FILL, MATH, FRAGMENT, MANDELBROT, TEST } #endregion [JemBenchmarkJob] [MemoryDiagnoser] public abstract class JemBenchmark where TData : struct, IEquatable, IComparable, IConvertible where TParam : struct { #region Constructors static JemBenchmark() { } public JemBenchmark() { } #endregion #region Properties [ParamsSource(nameof(GetParameters))] public TParam Parameter; public static List BenchmarkParameters { get; set; } public static Category Category { get; set; } public static Operation Operation { get; set; } public static bool Debug { get; set; } public static bool Validate { get; set; } public static ILogger Log { get; } = new ConsoleLogger(); public static Process CurrentProcess { get; } = Process.GetCurrentProcess(); public static long InitialPrivateMemorySize { get; protected set; } public static long PrivateMemorySize { get { CurrentProcess.Refresh(); return CurrentProcess.PrivateMemorySize64; } } public static long PeakWorkingSet { get { CurrentProcess.Refresh(); return CurrentProcess.PeakWorkingSet64; } } #endregion #region Methods public IEnumerable GetParameters() { IEnumerable param; param = BenchmarkParameters.Select(p => new JemBenchmarkParam(p)); return param; } public static int GetBenchmarkMethodCount() where TBench : JemBenchmark { return typeof(TBench).GenericTypeArguments.First().GetMethods(BindingFlags.Public).Count(); } [GlobalSetup] public virtual void GlobalSetup() { DebugInfoThis(); Info("Data type is {0}.", typeof(TData).Name); CurrentProcess.Refresh(); InitialPrivateMemorySize = CurrentProcess.PeakWorkingSet64; } public static void SetColdStartOverride(bool value) { JemBenchmarkJobAttribute.ColdStartOverride = value; } public static void SetTargetCountOverride(int value) { JemBenchmarkJobAttribute.TargetCountOverride = value; } public static void SetInvocationCountOverride(int value) { JemBenchmarkJobAttribute.InvocationCountOverride = value; } public static void SetWarmupCountOverride(int value) { JemBenchmarkJobAttribute.WarmupCountOverride = value; } public TValue GetValue(string name, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { if (JemUtil.BenchmarkValues.TryGetValue($"{name}_{Parameter.GetHashCode()}", out object v)) { return (TValue) v; } else throw new Exception($"Could not get value {name}."); } public void SetValue(string name, TValue value, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { JemUtil.BenchmarkValues.GetOrAdd($"{name}_{Parameter.GetHashCode()}", value); } public void RemoveValue(string name, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { JemUtil.BenchmarkValues.Remove($"{name}_{Parameter.GetHashCode()}", out object o); } public void SetStatistic(string name, string value, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { JemUtil.BenchmarkStatistics.AddOrUpdate($"{memberName}_{name}", value, ((k, v) => value)); } public void SetMemoryStatistics([CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { this.SetStatistic($"{memberName}_WorkingSet", JemUtil.PrintBytes(JemUtil.ProcessWorkingSet)); this.SetStatistic($"{memberName}_JemResident", JemUtil.PrintBytes(Jem.ResidentBytes)); this.SetStatistic($"{memberName}_PrivateMemory", JemUtil.PrintBytes(JemUtil.ProcessPrivateMemory)); this.SetStatistic($"{memberName}_JemAllocated", JemUtil.PrintBytes(Jem.AllocatedBytes)); } #region Log public static void Info(string format, params object[] values) => Log.WriteLineInfo(string.Format(format, values)); public static void DebugInfo(string format, params object[] values) { if (Debug) { Info(format, values); } } public static void InfoThis([CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) => Info("Executing {0}() on thread {1}.", memberName, Thread.CurrentThread.ManagedThreadId); public static void DebugInfoThis([CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { if (Debug) { InfoThis(memberName, fileName, lineNumber); } } public static void Error(string text, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) => Log.WriteLineError(string.Format("Error : {0} At {1} in {2} on line {3}.", text, memberName, fileName, lineNumber)); public static void Error(string format, params object[] values) => Log.WriteLineError(string.Format(format, values)); #endregion public static string PrintBytes(double bytes, string suffix = "") { if (bytes >= 0 && bytes <= 1024) { return string.Format("{0:N0} B{1}", bytes, suffix); } else if (bytes >= 1024 && bytes < (1024 * 1024)) { return string.Format("{0:N1} KB{1}", bytes / 1024, suffix); } else if (bytes >= (1024 * 1024) && bytes < (1024 * 1024 * 1024)) { return string.Format("{0:N1} MB{1}", bytes / (1024 * 1024), suffix); } else if (bytes >= (1024 * 1024 * 1024)) { return string.Format("{0:N1} GB{1}", bytes / (1024 * 1024 * 1024), suffix); } else throw new ArgumentOutOfRangeException(); } public static Tuple PrintBytesToTuple(double bytes, string suffix = "") { string[] s = PrintBytes(bytes, suffix).Split(' '); return new Tuple(Double.Parse(s[0]), s[1]); } private static object benchmarkLock = new object(); #endregion } } ================================================ FILE: jemalloc.Benchmarks/JemBenchmarkAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Attributes; namespace jemalloc.Benchmarks { public class JemBenchmarkAttribute : BenchmarkAttribute { public static Category Category { get; set; } public static Operation Operation { get; set; } public static IConfig CurrentConfig { get; set; } } } ================================================ FILE: jemalloc.Benchmarks/JemBenchmarkJobAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Order; using BenchmarkDotNet.Toolchains.InProcess; namespace jemalloc.Benchmarks { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false, Inherited = true)] public class JemBenchmarkJobAttribute : Attribute, IConfigSource { public JemBenchmarkJobAttribute( RunStrategy RunStrategy = RunStrategy.Throughput, int TargetCount = DefaultValue, int InvocationCount = DefaultValue, int WarmupCount = DefaultValue ) { Job job = new Job() .WithGcAllowVeryLargeObjects(true) .WithId("JemBenchmark"); job.Env.Platform = Platform.X64; job.Env.Runtime = Runtime.Core; job.Env.Jit = Jit.RyuJit; if (WarmupCountOverride.HasValue) { job.Run.WarmupCount = WarmupCountOverride.Value; } else if (WarmupCount != DefaultValue) { job.Run.WarmupCount = WarmupCount; } if (TargetCountOverride.HasValue) { job.Run.TargetCount = TargetCountOverride.Value; } else if (TargetCount != DefaultValue) { job.Run.TargetCount = TargetCount; } if (InvocationCountOverride.HasValue) { job.Run.InvocationCount = InvocationCountOverride.Value; } else if (InvocationCount != DefaultValue) { job.Run.InvocationCount = InvocationCount; } if (ColdStartOverride.HasValue) { job.Run.RunStrategy = ColdStartOverride.Value ? RunStrategy.ColdStart : RunStrategy.Throughput; } else { job.Run.RunStrategy = RunStrategy; } job.Infrastructure.Toolchain = new InProcessToolchain(TimeSpan.FromMinutes(TimeoutInMinutes), BenchmarkActionCodegen.ReflectionEmit, true); Config = ManualConfig.CreateEmpty() .With(job); } #region Properties public IConfig Config { get; } public int TimeoutInMinutes { get; set; } = 10; public static int? TargetCountOverride { get; set; } = null; public static int? WarmupCountOverride { get; set; } = null; public static int? InvocationCountOverride { get; set; } = null; public static bool? ColdStartOverride { get; set; } = null; #endregion #region Fields private const int DefaultValue = -1; #endregion } } ================================================ FILE: jemalloc.Benchmarks/JemBenchmarkParam.cs ================================================ using System; using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Code; using BenchmarkDotNet.Parameters; namespace jemalloc.Benchmarks { public class JemBenchmarkParam : IParam where T : struct { private static string ctorName = typeof(T).Name; private readonly T value; public JemBenchmarkParam(T value) => this.value = value; public object Value => value; public string DisplayText => $"{value}"; public string ToSourceCode() => $"new {ctorName}({value})"; } } ================================================ FILE: jemalloc.Benchmarks/JemStatisticColumn.cs ================================================ using System; using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Running; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Columns; namespace jemalloc.Benchmarks { public class JemStatisticColumn : IColumn { #region Constructors public JemStatisticColumn(string columnName, Func jemStatFunction, string legend) { ColumnName = columnName; JemStatFunction = jemStatFunction; Legend = legend; } #endregion #region Implemented properties // // Summary: // An unique identificator of the column. If there are several columns with the // same Id, only one of them will be shown in the summary. public string Id => ColumnName; // // Summary: // Defines order of column in the same category. public int PriorityInCategory { get; } = 99; // Summary: // Defines how to format column's value public UnitType UnitType { get; } = UnitType.Size; // // Summary: // Column description. public string Legend { get; protected set; } public bool AlwaysShow => true; public bool IsNumeric => true; public ColumnCategory Category => ColumnCategory.Statistics; #endregion #region Implemented methods public bool IsDefault(Summary summary, Benchmark benchmark) => false; public string GetValue(Summary summary, Benchmark benchmark) => JemStatFunction.Invoke(); public bool IsAvailable(Summary summary) => Jem.Initialized; // // Summary: // Value in this column formatted using the specified style. public string GetValue(Summary summary, Benchmark benchmark, ISummaryStyle style) => (JemStatFunction.Invoke()); public string ColumnName { get; } #endregion #region Overriden methods public override string ToString() => ColumnName; #endregion #region Fields Func JemStatFunction; #endregion #region Available columns public static readonly IColumn Allocated = new JemStatisticColumn("JEM allocated", () => JemUtil.PrintBytes(Jem.AllocatedBytes), "Allocated pages using jemalloc (native only, inclusive, 1KB = 1024B)"); public static readonly IColumn Active = new JemStatisticColumn("JEM active", () => JemUtil.PrintBytes(Jem.ActiveBytes), "Active pages using jemalloc (native only, inclusive, 1KB = 1024B)"); public static readonly IColumn Mapped = new JemStatisticColumn("JEM mapped", () => JemUtil.PrintBytes(Jem.MappedBytes), "Mapped memory using jemalloc (native only, inclusive, 1KB = 1024B)"); #endregion } } ================================================ FILE: jemalloc.Benchmarks/ProcessStatisticColumn.cs ================================================ using System; using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Running; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Columns; namespace jemalloc.Benchmarks { public class ProcessStatisticColumn : IColumn { #region Constructors public ProcessStatisticColumn(string columnName, Func jemStatFunction, string legend) { ColumnName = columnName; JemStatFunction = jemStatFunction; Legend = legend; } #endregion #region Implemented properties // // Summary: // An unique identificator of the column. If there are several columns with the // same Id, only one of them will be shown in the summary. public string Id => ColumnName; // // Summary: // Defines order of column in the same category. public int PriorityInCategory { get; } = 99; // Summary: // Defines how to format column's value public UnitType UnitType { get; } = UnitType.Size; // // Summary: // Column description. public string Legend { get; protected set; } public bool AlwaysShow => true; public bool IsNumeric => true; public ColumnCategory Category => ColumnCategory.Statistics; #endregion #region Implemented methods public bool IsDefault(Summary summary, Benchmark benchmark) => false; public string GetValue(Summary summary, Benchmark benchmark) => JemStatFunction.Invoke(); public bool IsAvailable(Summary summary) => Jem.Initialized; // // Summary: // Value in this column formatted using the specified style. public string GetValue(Summary summary, Benchmark benchmark, ISummaryStyle style) => (JemStatFunction.Invoke()); public string ColumnName { get; } #endregion #region Overriden methods public override string ToString() => ColumnName; #endregion #region Fields Func JemStatFunction; #endregion #region Available columns public static readonly IColumn PeakVirtualMemory = new JemStatisticColumn("PeakVirtualMem", () => JemUtil.PrintBytes(JemUtil.ProcessPeakVirtualMem), "Peak virtual memory for entire process (native and managed, inclusive, 1KB = 1024B)"); public static readonly IColumn PeakWorkingSet = new JemStatisticColumn("PeakWorkingSet", () => JemUtil.ProcessPeakWorkingSet.ToString(), "Peak working set for entire process per single operation (native and managed, inclusive, 1KB = 1024B)"); public static readonly IColumn VirtualMemory = new JemStatisticColumn("VirtualMemory", () => JemUtil.PrintBytes(JemUtil.ProcessVirtualMemory), "Virtual memory allocated for entire process per single operation (native and managed, inclusive, 1KB = 1024B)"); #endregion } } ================================================ FILE: jemalloc.Benchmarks/TestUDT.cs ================================================ using System; using System.Collections.Generic; namespace jemalloc.Benchmarks { public struct TestUDT : IEquatable, IComparable, IConvertible { public Guid ID { get; set; } public Utf8Buffer FirstName { get; set; } public Utf8Buffer LastName { get; set; } public DateTime? DOB { get; set; } public decimal Balance { get; set; } public FixedBuffer Data { get; set; } public FixedBuffer Photo { get; set; } public static TestUDT MakeTestRecord(Random rng) { Guid _id = Guid.NewGuid(); string _ids = _id.ToString("N"); byte[] photo = new byte[4096]; rng.NextBytes(photo); return new TestUDT() { ID = _id, FirstName = new Utf8Buffer("Gavial-" + _id.ToString("D").Substring(0, 4)), LastName = new Utf8Buffer("Buxarinovich-" + _id.ToString("D").Substring(0, 4)), DOB = _ids.StartsWith("7") ? (DateTime?)null : DateTime.UtcNow, Balance = 2131m, Photo = new FixedBuffer(photo) }; } public bool Equals(TestUDT r) { return this.ID == r.ID; } #region IConvertible public TypeCode GetTypeCode() { return TypeCode.Object; } bool IConvertible.ToBoolean(IFormatProvider provider) { throw new InvalidCastException(); } double GetDoubleValue() { throw new InvalidCastException(); } byte IConvertible.ToByte(IFormatProvider provider) { throw new InvalidCastException(); } char IConvertible.ToChar(IFormatProvider provider) { throw new InvalidCastException(); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new InvalidCastException(); } decimal IConvertible.ToDecimal(IFormatProvider provider) { throw new InvalidCastException(); } double IConvertible.ToDouble(IFormatProvider provider) { throw new InvalidCastException(); } short IConvertible.ToInt16(IFormatProvider provider) { throw new InvalidCastException(); } int IConvertible.ToInt32(IFormatProvider provider) { throw new InvalidCastException(); } long IConvertible.ToInt64(IFormatProvider provider) { throw new InvalidCastException(); } sbyte IConvertible.ToSByte(IFormatProvider provider) { throw new InvalidCastException(); } float IConvertible.ToSingle(IFormatProvider provider) { throw new InvalidCastException(); } string IConvertible.ToString(IFormatProvider provider) { return String.Format("({0}, {1})", FirstName, LastName); } object IConvertible.ToType(Type conversionType, IFormatProvider provider) { throw new InvalidCastException(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { throw new InvalidCastException(); } uint IConvertible.ToUInt32(IFormatProvider provider) { throw new InvalidCastException(); } ulong IConvertible.ToUInt64(IFormatProvider provider) { throw new InvalidCastException(); } #endregion #region IComparable public int CompareTo(TestUDT other) { return 0; } #endregion } } ================================================ FILE: jemalloc.Benchmarks/jemalloc.Benchmarks.csproj ================================================ netcoreapp2.0 x64 latest AnyCPU;x64 x64 x64 x64 latest x64 x64 x64 ..\x64\Release\ true latest ================================================ FILE: jemalloc.Bindings/App.config ================================================  ================================================ FILE: jemalloc.Bindings/JemallocLibrary.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CppSharp; using CppSharp.AST; using CppSharp.Generators; using CppSharp.Passes; namespace jemalloc.Bindings { public class JemallocLibrary : ILibrary { /// Setup the driver options here. public void Setup(Driver driver) { DriverOptions options = driver.Options; options.GeneratorKind = GeneratorKind.CSharp; Module module = options.AddModule("jemalloc"); module.Defines.AddRange(@"JEMALLOC_NO_PRIVATE_NAMESPACE;REENTRANT;WINDLL;DLLEXPORT;JEMALLOC_DEBUG;DEBUG".Split(';')); module.IncludeDirs.Add(@"..\..\jemalloc\include\jemalloc"); module.IncludeDirs.Add(@"..\..\jemalloc\include\jemalloc\internal"); module.IncludeDirs.Add(@"..\..\jemalloc\include\msvc_compat"); module.IncludeDirs.Add(@".\"); module.Headers.Add("jemalloc-win-msvc.h"); module.LibraryDirs.Add(@".\"); module.Libraries.Add("jemallocd.lib"); module.OutputNamespace = "jemalloc"; options.OutputDir = @".\"; options.Verbose = true; } /// Setup your passes here. public void SetupPasses(Driver driver) { } /// Do transformations that should happen before passes are processed. public void Preprocess(Driver driver, ASTContext ctx) { } /// Do transformations that should happen after passes are processed. public void Postprocess(Driver driver, ASTContext ctx) { ctx.SetClassBindName("ExtentHooksS", "ExtentHooks"); } } } ================================================ FILE: jemalloc.Bindings/Program.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CppSharp; namespace jemalloc.Bindings { class Program { static void Main(string[] args) { ConsoleDriver.Run(new JemallocLibrary()); } } } ================================================ FILE: jemalloc.Bindings/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("jemalloc.Bindings")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("jemalloc.Bindings")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("c24a6949-ba9e-443b-a67e-188ab489057e")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: jemalloc.Bindings/jemalloc-win-msvc.h ================================================ #define JEMALLOC_H_ extern "C" { /* Defined if __attribute__((...)) syntax is supported. */ /* #undef JEMALLOC_HAVE_ATTR */ /* Defined if alloc_size attribute is supported. */ /* #undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE */ /* Defined if format(gnu_printf, ...) attribute is supported. */ /* #undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF */ /* Defined if format(printf, ...) attribute is supported. */ /* #undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF */ /* * Define overrides for non-standard allocator-related functions if they are * present on the system. */ /* #undef JEMALLOC_OVERRIDE_MEMALIGN */ /* #undef JEMALLOC_OVERRIDE_VALLOC */ /* * At least Linux omits the "const" in: * * size_t malloc_usable_size(const void *ptr); * * Match the operating system's prototype. */ #define JEMALLOC_USABLE_SIZE_CONST const /* * If defined, specify throw() for the public function prototypes when compiling * with C++. The only justification for this is to match the prototypes that * glibc defines. */ /* #undef JEMALLOC_USE_CXX_THROW */ #define LG_SIZEOF_PTR_WIN 3 /* sizeof(void *) == 2^LG_SIZEOF_PTR. */ #define LG_SIZEOF_PTR LG_SIZEOF_PTR_WIN /* * Name mangling for public symbols is controlled by --with-mangling and * --with-jemalloc-prefix. With default settings the je_ prefix is stripped by * these macro definitions. */ # define je_aligned_alloc je_aligned_alloc # define je_calloc je_calloc # define je_dallocx je_dallocx # define je_free je_free # define je_mallctl je_mallctl # define je_mallctlbymib je_mallctlbymib # define je_mallctlnametomib je_mallctlnametomib # define je_malloc je_malloc # define je_malloc_conf je_malloc_conf # define je_malloc_message je_malloc_message # define je_malloc_stats_print je_malloc_stats_print # define je_malloc_usable_size je_malloc_usable_size # define je_mallocx je_mallocx # define je_nallocx je_nallocx # define je_posix_memalign je_posix_memalign # define je_rallocx je_rallocx # define je_realloc je_realloc # define je_sallocx je_sallocx # define je_sdallocx je_sdallocx # define je_xallocx je_xallocx /* #include #include #include #include #include */ #define JEMALLOC_VERSION "5.0.1-64-gcb3b72b9756d124565ed12e005065ad6f0769568" #define JEMALLOC_VERSION_MAJOR 5 #define JEMALLOC_VERSION_MINOR 0 #define JEMALLOC_VERSION_BUGFIX 1 #define JEMALLOC_VERSION_NREV 64 #define JEMALLOC_VERSION_GID "cb3b72b9756d124565ed12e005065ad6f0769568" #define MALLOCX_LG_ALIGN(la) ((int)(la)) #define MALLOCX_ALIGN(a) \ ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 : \ ffs((int)(((size_t)(a))>>32))+31)) #define MALLOCX_ZERO ((int)0x40) /* * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1 * encodes MALLOCX_TCACHE_NONE. */ #define MALLOCX_TCACHE(tc) ((int)(((tc)+2) << 8)) #define MALLOCX_TCACHE_NONE MALLOCX_TCACHE(-1) /* * Bias arena index bits so that 0 encodes "use an automatically chosen arena". */ #define MALLOCX_ARENA(a) ((((int)(a))+1) << 20) /* * Use as arena index in "arena..{purge,decay,dss}" and * "stats.arenas..*" mallctl interfaces to select all arenas. This * definition is intentionally specified in raw decimal format to support * cpp-based string concatenation, e.g. * * #define STRINGIFY_HELPER(x) #x * #define STRINGIFY(x) STRINGIFY_HELPER(x) * * mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", NULL, NULL, NULL, * 0); */ #define MALLCTL_ARENAS_ALL 4096 /* * Use as arena index in "stats.arenas..*" mallctl interfaces to select * destroyed arenas. */ #define MALLCTL_ARENAS_DESTROYED 4097 #if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW) # define JEMALLOC_CXX_THROW throw() #else # define JEMALLOC_CXX_THROW #endif # define JEMALLOC_ATTR(s) # define JEMALLOC_ALIGNED(s) __declspec(align(s)) # define JEMALLOC_ALLOC_SIZE(s) # define JEMALLOC_ALLOC_SIZE2(s1, s2) # define JEMALLOC_EXPORT __declspec(dllexport) # define JEMALLOC_FORMAT_PRINTF(s, i) # define JEMALLOC_NOINLINE __declspec(noinline) # define JEMALLOC_NOTHROW # define JEMALLOC_SECTION(s) __declspec(allocate(s)) # define JEMALLOC_RESTRICT_RETURN __declspec(restrict) # define JEMALLOC_ALLOCATOR /* * The je_ prefix on the following public symbol declarations is an artifact * of namespace management, and should be omitted in application code unless * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle.h). */ extern JEMALLOC_EXPORT const char *je_malloc_conf; extern JEMALLOC_EXPORT void (*je_malloc_message)(void *cbopaque, const char *s); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN void JEMALLOC_NOTHROW *je_malloc(size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN void JEMALLOC_NOTHROW *je_calloc(size_t num, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2); JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_posix_memalign(void **memptr, size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1)); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN void JEMALLOC_NOTHROW *je_aligned_alloc(size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN void JEMALLOC_NOTHROW *je_realloc(void *ptr, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2); JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_free(void *ptr) JEMALLOC_CXX_THROW; JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN void JEMALLOC_NOTHROW *je_mallocx(size_t size, int flags) JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN void JEMALLOC_NOTHROW *je_rallocx(void *ptr, size_t size, int flags) JEMALLOC_ALLOC_SIZE(2); JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_xallocx(void *ptr, size_t size, size_t extra, int flags); JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_sallocx(const void *ptr, int flags) JEMALLOC_ATTR(pure); JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_dallocx(void *ptr, int flags); JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_sdallocx(void *ptr, size_t size, int flags); JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_nallocx(size_t size, int flags) JEMALLOC_ATTR(pure); JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen); JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp); JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_malloc_stats_print( void (*write_cb)(void *, const char *), void *je_cbopaque, const char *opts); JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_malloc_usable_size( JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW; JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_set_malloc_conf(const char *name); JEMALLOC_EXPORT char* JEMALLOC_NOTHROW je_get_malloc_conf(); typedef void(*je_malloc_message_ptr)(void *, const char *); JEMALLOC_EXPORT je_malloc_message_ptr je_get_malloc_message_ptr(); JEMALLOC_EXPORT void je_set_malloc_message_ptr(je_malloc_message_ptr p); typedef struct extent_hooks_s extent_hooks_t; /* * void * * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size, * size_t alignment, bool *zero, bool *commit, unsigned arena_ind); */ typedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *, bool *, unsigned); /* * bool * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size, * bool committed, unsigned arena_ind); */ typedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool, unsigned); /* * void * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size, * bool committed, unsigned arena_ind); */ typedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool, unsigned); /* * bool * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size, * size_t offset, size_t length, unsigned arena_ind); */ typedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t, unsigned); /* * bool * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size, * size_t offset, size_t length, unsigned arena_ind); */ typedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t, size_t, unsigned); /* * bool * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size, * size_t offset, size_t length, unsigned arena_ind); */ typedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t, unsigned); /* * bool * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size, * size_t size_a, size_t size_b, bool committed, unsigned arena_ind); */ typedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t, bool, unsigned); /* * bool * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a, * void *addr_b, size_t size_b, bool committed, unsigned arena_ind); */ typedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t, bool, unsigned); struct extent_hooks_s { extent_alloc_t *alloc; extent_dalloc_t *dalloc; extent_destroy_t *destroy; extent_commit_t *commit; extent_decommit_t *decommit; extent_purge_t *purge_lazy; extent_purge_t *purge_forced; extent_split_t *split; extent_merge_t *merge; }; } ================================================ FILE: jemalloc.Bindings/jemalloc.Bindings.csproj ================================================  Debug AnyCPU {C24A6949-BA9E-443B-A67E-188AB489057E} Exe jemalloc.Bindings jemalloc.Bindings v4.6.1 512 true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 x64 pdbonly true bin\Release\ TRACE prompt 4 false true ..\x64\Debug\ DEBUG;TRACE full x64 prompt MinimumRecommendedRules.ruleset true ..\x64\Release\ TRACE true pdbonly x64 prompt MinimumRecommendedRules.ruleset true ..\packages\CppSharp.0.8.16\lib\CppSharp.dll ..\packages\CppSharp.0.8.16\lib\CppSharp.AST.dll ..\packages\CppSharp.0.8.16\lib\CppSharp.Generator.dll ..\packages\CppSharp.0.8.16\lib\CppSharp.Parser.dll ..\packages\CppSharp.0.8.16\lib\CppSharp.Parser.CLI.dll ..\packages\CppSharp.0.8.16\lib\CppSharp.Runtime.dll Always This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: jemalloc.Bindings/packages.config ================================================  ================================================ FILE: jemalloc.Buffers/JemPinnable.cs ================================================ using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Text; namespace jemalloc { [StructLayout(LayoutKind.Sequential)] internal sealed class JemPinnable { public T Data; } } ================================================ FILE: jemalloc.Buffers/NativeHelpers.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Reflection; using System.Diagnostics; using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; namespace jemalloc { internal static partial class NativeHelpers { public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); if (valueLength == 0) return 0; // A zero-length sequence is always treated as "found" at the start of the search space. byte valueHead = value; ref byte valueTail = ref Unsafe.Add(ref value, 1); int valueTailLength = valueLength - 1; int index = 0; for (; ; ) { Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; if (remainingSearchSpaceLength <= 0) break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. // Do a quick search for the first element of "value". int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength); if (relativeIndex == -1) break; index += relativeIndex; // Found the first element of "value". See if the tail matches. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength)) return index; // The tail matched. Return a successful find. index++; } return -1; } public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); if (valueLength == 0) return 0; // A zero-length sequence is always treated as "found" at the start of the search space. int index = -1; for (int i = 0; i < valueLength; i++) { var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); if (tempIndex != -1) { index = (index == -1 || index > tempIndex) ? tempIndex : index; } } return index; } public static unsafe int IndexOf(ref byte searchSpace, byte value, int length) { Debug.Assert(length >= 0); uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr nLength = (IntPtr)(uint)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { unchecked { int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); nLength = (IntPtr)(uint)((Vector.Count - unaligned) & (Vector.Count - 1)); } } SequentialScan: while ((byte*)nLength >= (byte*)8) { nLength -= 8; if (uValue == Unsafe.Add(ref searchSpace, index)) goto Found; if (uValue == Unsafe.Add(ref searchSpace, index + 1)) goto Found1; if (uValue == Unsafe.Add(ref searchSpace, index + 2)) goto Found2; if (uValue == Unsafe.Add(ref searchSpace, index + 3)) goto Found3; if (uValue == Unsafe.Add(ref searchSpace, index + 4)) goto Found4; if (uValue == Unsafe.Add(ref searchSpace, index + 5)) goto Found5; if (uValue == Unsafe.Add(ref searchSpace, index + 6)) goto Found6; if (uValue == Unsafe.Add(ref searchSpace, index + 7)) goto Found7; index += 8; } if ((byte*)nLength >= (byte*)4) { nLength -= 4; if (uValue == Unsafe.Add(ref searchSpace, index)) goto Found; if (uValue == Unsafe.Add(ref searchSpace, index + 1)) goto Found1; if (uValue == Unsafe.Add(ref searchSpace, index + 2)) goto Found2; if (uValue == Unsafe.Add(ref searchSpace, index + 3)) goto Found3; index += 4; } while ((byte*)nLength > (byte*)0) { nLength -= 1; if (uValue == Unsafe.Add(ref searchSpace, index)) goto Found; index += 1; } if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) { nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); // Get comparison Vector Vector vComparison = GetVector(value); while ((byte*)nLength > (byte*)index) { var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index))); if (Vector.Zero.Equals(vMatches)) { index += Vector.Count; continue; } // Find offset of first match return (int)(byte*)index + LocateFirstFoundByte(vMatches); } if ((int)(byte*)index < length) { unchecked { nLength = (IntPtr)(length - (int)(byte*)index); } goto SequentialScan; } } return -1; Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return (int)(byte*)index; Found1: return (int)(byte*)(index + 1); Found2: return (int)(byte*)(index + 2); Found3: return (int)(byte*)(index + 3); Found4: return (int)(byte*)(index + 4); Found5: return (int)(byte*)(index + 5); Found6: return (int)(byte*)(index + 6); Found7: return (int)(byte*)(index + 7); } public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, int length) { Debug.Assert(length >= 0); uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr nLength = (IntPtr)(uint)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { unchecked { int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); nLength = (IntPtr)(uint)((Vector.Count - unaligned) & (Vector.Count - 1)); } } SequentialScan: uint lookUp; while ((byte*)nLength >= (byte*)8) { nLength -= 8; lookUp = Unsafe.Add(ref searchSpace, index); if (uValue0 == lookUp || uValue1 == lookUp) goto Found; lookUp = Unsafe.Add(ref searchSpace, index + 1); if (uValue0 == lookUp || uValue1 == lookUp) goto Found1; lookUp = Unsafe.Add(ref searchSpace, index + 2); if (uValue0 == lookUp || uValue1 == lookUp) goto Found2; lookUp = Unsafe.Add(ref searchSpace, index + 3); if (uValue0 == lookUp || uValue1 == lookUp) goto Found3; lookUp = Unsafe.Add(ref searchSpace, index + 4); if (uValue0 == lookUp || uValue1 == lookUp) goto Found4; lookUp = Unsafe.Add(ref searchSpace, index + 5); if (uValue0 == lookUp || uValue1 == lookUp) goto Found5; lookUp = Unsafe.Add(ref searchSpace, index + 6); if (uValue0 == lookUp || uValue1 == lookUp) goto Found6; lookUp = Unsafe.Add(ref searchSpace, index + 7); if (uValue0 == lookUp || uValue1 == lookUp) goto Found7; index += 8; } if ((byte*)nLength >= (byte*)4) { nLength -= 4; lookUp = Unsafe.Add(ref searchSpace, index); if (uValue0 == lookUp || uValue1 == lookUp) goto Found; lookUp = Unsafe.Add(ref searchSpace, index + 1); if (uValue0 == lookUp || uValue1 == lookUp) goto Found1; lookUp = Unsafe.Add(ref searchSpace, index + 2); if (uValue0 == lookUp || uValue1 == lookUp) goto Found2; lookUp = Unsafe.Add(ref searchSpace, index + 3); if (uValue0 == lookUp || uValue1 == lookUp) goto Found3; index += 4; } while ((byte*)nLength > (byte*)0) { nLength -= 1; lookUp = Unsafe.Add(ref searchSpace, index); if (uValue0 == lookUp || uValue1 == lookUp) goto Found; index += 1; } if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) { nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); // Get comparison Vector Vector values0 = GetVector(value0); Vector values1 = GetVector(value1); while ((byte*)nLength > (byte*)index) { var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); var vMatches = Vector.BitwiseOr( Vector.Equals(vData, values0), Vector.Equals(vData, values1)); if (Vector.Zero.Equals(vMatches)) { index += Vector.Count; continue; } // Find offset of first match return (int)(byte*)index + LocateFirstFoundByte(vMatches); } if ((int)(byte*)index < length) { unchecked { nLength = (IntPtr)(length - (int)(byte*)index); } goto SequentialScan; } } return -1; Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return (int)(byte*)index; Found1: return (int)(byte*)(index + 1); Found2: return (int)(byte*)(index + 2); Found3: return (int)(byte*)(index + 3); Found4: return (int)(byte*)(index + 4); Found5: return (int)(byte*)(index + 5); Found6: return (int)(byte*)(index + 6); Found7: return (int)(byte*)(index + 7); } public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length) { Debug.Assert(length >= 0); uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr nLength = (IntPtr)(uint)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { unchecked { int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); nLength = (IntPtr)(uint)((Vector.Count - unaligned) & (Vector.Count - 1)); } } SequentialScan: uint lookUp; while ((byte*)nLength >= (byte*)8) { nLength -= 8; lookUp = Unsafe.Add(ref searchSpace, index); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found; lookUp = Unsafe.Add(ref searchSpace, index + 1); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found1; lookUp = Unsafe.Add(ref searchSpace, index + 2); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found2; lookUp = Unsafe.Add(ref searchSpace, index + 3); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found3; lookUp = Unsafe.Add(ref searchSpace, index + 4); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found4; lookUp = Unsafe.Add(ref searchSpace, index + 5); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found5; lookUp = Unsafe.Add(ref searchSpace, index + 6); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found6; lookUp = Unsafe.Add(ref searchSpace, index + 7); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found7; index += 8; } if ((byte*)nLength >= (byte*)4) { nLength -= 4; lookUp = Unsafe.Add(ref searchSpace, index); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found; lookUp = Unsafe.Add(ref searchSpace, index + 1); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found1; lookUp = Unsafe.Add(ref searchSpace, index + 2); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found2; lookUp = Unsafe.Add(ref searchSpace, index + 3); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found3; index += 4; } while ((byte*)nLength > (byte*)0) { nLength -= 1; lookUp = Unsafe.Add(ref searchSpace, index); if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found; index += 1; } if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) { nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); // Get comparison Vector Vector values0 = GetVector(value0); Vector values1 = GetVector(value1); Vector values2 = GetVector(value2); while ((byte*)nLength > (byte*)index) { var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); var vMatches = Vector.BitwiseOr( Vector.BitwiseOr( Vector.Equals(vData, values0), Vector.Equals(vData, values1)), Vector.Equals(vData, values2)); if (Vector.Zero.Equals(vMatches)) { index += Vector.Count; continue; } // Find offset of first match return (int)(byte*)index + LocateFirstFoundByte(vMatches); } if ((int)(byte*)index < length) { unchecked { nLength = (IntPtr)(length - (int)(byte*)index); } goto SequentialScan; } } return -1; Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return (int)(byte*)index; Found1: return (int)(byte*)(index + 1); Found2: return (int)(byte*)(index + 2); Found3: return (int)(byte*)(index + 3); Found4: return (int)(byte*)(index + 4); Found5: return (int)(byte*)(index + 5); Found6: return (int)(byte*)(index + 6); Found7: return (int)(byte*)(index + 7); } public static unsafe bool SequenceEqual(ref byte first, ref byte second, int length) { Debug.Assert(length >= 0); if (Unsafe.AreSame(ref first, ref second)) goto Equal; IntPtr i = (IntPtr)0; // Use IntPtr and byte* for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr n = (IntPtr)length; #if !netstandard11 if (Vector.IsHardwareAccelerated && (byte*)n >= (byte*)Vector.Count) { n -= Vector.Count; while ((byte*)n > (byte*)i) { if (Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref first, i)) != Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref second, i))) { goto NotEqual; } i += Vector.Count; } return Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref first, n)) == Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref second, n)); } #endif if ((byte*)n >= (byte*)sizeof(UIntPtr)) { n -= sizeof(UIntPtr); while ((byte*)n > (byte*)i) { if (Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref first, i)) != Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref second, i))) { goto NotEqual; } i += sizeof(UIntPtr); } return Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref first, n)) == Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref second, n)); } while ((byte*)n > (byte*)i) { if (Unsafe.AddByteOffset(ref first, i) != Unsafe.AddByteOffset(ref second, i)) goto NotEqual; i += 1; } Equal: return true; NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return false; } #if !netstandard11 // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(Vector match) { var vector64 = Vector.AsVectorUInt64(match); ulong candidate = 0; int i = 0; // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001 for (; i < Vector.Count; i++) { candidate = vector64[i]; if (candidate != 0) { break; } } // Single LEA instruction with jitted const (using function result) return i * 8 + LocateFirstFoundByte(candidate); } #endif #if !netstandard11 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(ulong match) { unchecked { // Flag least significant power of two bit var powerOfTwoFlag = match ^ (match - 1); // Shift all powers of two into the high byte and extract return (int)((powerOfTwoFlag * XorPowerOfTwoToHighByte) >> 57); } } #endif #if !netstandard11 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector GetVector(byte vectorByte) { #if !netcoreapp // Vector .ctor doesn't become an intrinsic due to detection issue // However this does cause it to become an intrinsic (with additional multiply and reg->reg copy) // https://github.com/dotnet/coreclr/issues/7459#issuecomment-253965670 return Vector.AsVectorByte(new Vector(vectorByte * 0x01010101u)); #else return new Vector(vectorByte); #endif } #endif #if !netstandard11 private const ulong XorPowerOfTwoToHighByte = (0x07ul | 0x06ul << 8 | 0x05ul << 16 | 0x04ul << 24 | 0x03ul << 32 | 0x02ul << 40 | 0x01ul << 48) + 1; #endif public static int IndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); if (valueLength == 0) return 0; // A zero-length sequence is always treated as "found" at the start of the search space. T valueHead = value; ref T valueTail = ref Unsafe.Add(ref value, 1); int valueTailLength = valueLength - 1; int index = 0; for (;;) { Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; if (remainingSearchSpaceLength <= 0) break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. // Do a quick search for the first element of "value". int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength); if (relativeIndex == -1) break; index += relativeIndex; // Found the first element of "value". See if the tail matches. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength)) return index; // The tail matched. Return a successful find. index++; } return -1; } public static unsafe int IndexOf(ref T searchSpace, T value, int length) where T : IEquatable { Debug.Assert(length >= 0); IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations while (length >= 8) { length -= 8; if (value.Equals(Unsafe.Add(ref searchSpace, index))) goto Found; if (value.Equals(Unsafe.Add(ref searchSpace, index + 1))) goto Found1; if (value.Equals(Unsafe.Add(ref searchSpace, index + 2))) goto Found2; if (value.Equals(Unsafe.Add(ref searchSpace, index + 3))) goto Found3; if (value.Equals(Unsafe.Add(ref searchSpace, index + 4))) goto Found4; if (value.Equals(Unsafe.Add(ref searchSpace, index + 5))) goto Found5; if (value.Equals(Unsafe.Add(ref searchSpace, index + 6))) goto Found6; if (value.Equals(Unsafe.Add(ref searchSpace, index + 7))) goto Found7; index += 8; } if (length >= 4) { length -= 4; if (value.Equals(Unsafe.Add(ref searchSpace, index))) goto Found; if (value.Equals(Unsafe.Add(ref searchSpace, index + 1))) goto Found1; if (value.Equals(Unsafe.Add(ref searchSpace, index + 2))) goto Found2; if (value.Equals(Unsafe.Add(ref searchSpace, index + 3))) goto Found3; index += 4; } while (length > 0) { if (value.Equals(Unsafe.Add(ref searchSpace, index))) goto Found; index += 1; length--; } return -1; Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return (int)(byte*)index; Found1: return (int)(byte*)(index + 1); Found2: return (int)(byte*)(index + 2); Found3: return (int)(byte*)(index + 3); Found4: return (int)(byte*)(index + 4); Found5: return (int)(byte*)(index + 5); Found6: return (int)(byte*)(index + 6); Found7: return (int)(byte*)(index + 7); } public static bool SequenceEqual(ref T first, ref T second, int length) where T : IEquatable { Debug.Assert(length >= 0); if (Unsafe.AreSame(ref first, ref second)) goto Equal; IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations while (length >= 8) { length -= 8; if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index))) goto NotEqual; if (!Unsafe.Add(ref first, index + 1).Equals(Unsafe.Add(ref second, index + 1))) goto NotEqual; if (!Unsafe.Add(ref first, index + 2).Equals(Unsafe.Add(ref second, index + 2))) goto NotEqual; if (!Unsafe.Add(ref first, index + 3).Equals(Unsafe.Add(ref second, index + 3))) goto NotEqual; if (!Unsafe.Add(ref first, index + 4).Equals(Unsafe.Add(ref second, index + 4))) goto NotEqual; if (!Unsafe.Add(ref first, index + 5).Equals(Unsafe.Add(ref second, index + 5))) goto NotEqual; if (!Unsafe.Add(ref first, index + 6).Equals(Unsafe.Add(ref second, index + 6))) goto NotEqual; if (!Unsafe.Add(ref first, index + 7).Equals(Unsafe.Add(ref second, index + 7))) goto NotEqual; index += 8; } if (length >= 4) { length -= 4; if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index))) goto NotEqual; if (!Unsafe.Add(ref first, index + 1).Equals(Unsafe.Add(ref second, index + 1))) goto NotEqual; if (!Unsafe.Add(ref first, index + 2).Equals(Unsafe.Add(ref second, index + 2))) goto NotEqual; if (!Unsafe.Add(ref first, index + 3).Equals(Unsafe.Add(ref second, index + 3))) goto NotEqual; index += 4; } while (length > 0) { if (!Unsafe.Add(ref first, index).Equals(Unsafe.Add(ref second, index))) goto NotEqual; index += 1; length--; } Equal: return true; NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return false; } /// /// Implements the copy functionality used by Span and ReadOnlySpan. /// /// NOTE: Fast span implements TryCopyTo in corelib and therefore this implementation /// is only used by portable span. The code must live in code that only compiles /// for portable span which means either each individual span implementation /// of this shared code file. Other shared SpanHelper.X.cs files are compiled /// for both portable and fast span implementations. /// public static unsafe void CopyTo(ref T dst, int dstLength, ref T src, int srcLength) { Debug.Assert(dstLength != 0); IntPtr srcByteCount = Unsafe.ByteOffset(ref src, ref Unsafe.Add(ref src, srcLength)); IntPtr dstByteCount = Unsafe.ByteOffset(ref dst, ref Unsafe.Add(ref dst, dstLength)); IntPtr diff = Unsafe.ByteOffset(ref src, ref dst); bool isOverlapped = (sizeof(IntPtr) == sizeof(int)) ? (uint)diff < (uint)srcByteCount || (uint)diff > (uint)-(int)dstByteCount : (ulong)diff < (ulong)srcByteCount || (ulong)diff > (ulong)-(long)dstByteCount; if (!isOverlapped && !NativeHelpers.IsReferenceOrContainsReferences()) { ref byte dstBytes = ref Unsafe.As(ref dst); ref byte srcBytes = ref Unsafe.As(ref src); ulong byteCount = (ulong)srcByteCount; ulong index = 0; while (index < byteCount) { uint blockSize = (byteCount - index) > uint.MaxValue ? uint.MaxValue : (uint)(byteCount - index); Unsafe.CopyBlock( ref Unsafe.Add(ref dstBytes, (IntPtr)index), ref Unsafe.Add(ref srcBytes, (IntPtr)index), blockSize); index += blockSize; } } else { bool srcGreaterThanDst = (sizeof(IntPtr) == sizeof(int)) ? (uint)diff > (uint)-(int)dstByteCount : (ulong)diff > (ulong)-(long)dstByteCount; int direction = srcGreaterThanDst ? 1 : -1; int runCount = srcGreaterThanDst ? 0 : srcLength - 1; int loopCount = 0; for (; loopCount < (srcLength & ~7); loopCount += 8) { Unsafe.Add(ref dst, runCount + direction * 0) = Unsafe.Add(ref src, runCount + direction * 0); Unsafe.Add(ref dst, runCount + direction * 1) = Unsafe.Add(ref src, runCount + direction * 1); Unsafe.Add(ref dst, runCount + direction * 2) = Unsafe.Add(ref src, runCount + direction * 2); Unsafe.Add(ref dst, runCount + direction * 3) = Unsafe.Add(ref src, runCount + direction * 3); Unsafe.Add(ref dst, runCount + direction * 4) = Unsafe.Add(ref src, runCount + direction * 4); Unsafe.Add(ref dst, runCount + direction * 5) = Unsafe.Add(ref src, runCount + direction * 5); Unsafe.Add(ref dst, runCount + direction * 6) = Unsafe.Add(ref src, runCount + direction * 6); Unsafe.Add(ref dst, runCount + direction * 7) = Unsafe.Add(ref src, runCount + direction * 7); runCount += direction * 8; } if (loopCount < (srcLength & ~3)) { Unsafe.Add(ref dst, runCount + direction * 0) = Unsafe.Add(ref src, runCount + direction * 0); Unsafe.Add(ref dst, runCount + direction * 1) = Unsafe.Add(ref src, runCount + direction * 1); Unsafe.Add(ref dst, runCount + direction * 2) = Unsafe.Add(ref src, runCount + direction * 2); Unsafe.Add(ref dst, runCount + direction * 3) = Unsafe.Add(ref src, runCount + direction * 3); runCount += direction * 4; loopCount += 4; } for (; loopCount < srcLength; ++loopCount) { Unsafe.Add(ref dst, runCount) = Unsafe.Add(ref src, runCount); runCount += direction; } } } /// /// Computes "start + index * sizeof(T)", using the unsigned IntPtr-sized multiplication for 32 and 64 bits. /// /// Assumptions: /// Start and index are non-negative, and already pre-validated to be within the valid range of their containing Span. /// /// If the byte length (Span.Length * sizeof(T)) does an unsigned overflow (i.e. the buffer wraps or is too big to fit within the address space), /// the behavior is undefined. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IntPtr Add(this IntPtr start, int index) { Debug.Assert(start.ToInt64() >= 0); Debug.Assert(index >= 0); unsafe { if (sizeof(IntPtr) == sizeof(int)) { // 32-bit path. uint byteLength = (uint)index * (uint)Unsafe.SizeOf(); return (IntPtr)(((byte*)start) + byteLength); } else { // 64-bit path. ulong byteLength = (ulong)index * (ulong)Unsafe.SizeOf(); return (IntPtr)(((byte*)start) + byteLength); } } } /// /// Determine if a type is eligible for storage in unmanaged memory. /// Portable equivalent of RuntimeHelpers.IsReferenceOrContainsReferences{T}() /// public static bool IsReferenceOrContainsReferences() => PerTypeValues.IsReferenceOrContainsReferences; private static bool IsReferenceOrContainsReferencesCore(Type type) { if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references. return false; if (!type.GetTypeInfo().IsValueType) return true; // If type is a Nullable<> of something, unwrap it first. Type underlyingNullable = Nullable.GetUnderlyingType(type); if (underlyingNullable != null) type = underlyingNullable; if (type.GetTypeInfo().IsEnum) return false; foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields) { if (field.IsStatic) continue; if (IsReferenceOrContainsReferencesCore(field.FieldType)) return true; } return false; } public static class PerTypeValues { // // Latch to ensure that excruciatingly expensive validation check for constructing a Span around a raw pointer is done // only once per type. // public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferencesCore(typeof(T)); public static readonly T[] EmptyArray = new T[0]; public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); // Array header sizes are a runtime implementation detail and aren't the same across all runtimes. (The CLR made a tweak after 4.5, and Mono has an extra Bounds pointer.) private static IntPtr MeasureArrayAdjustment() { T[] sampleArray = new T[1]; return Unsafe.ByteOffset(ref Unsafe.As>(sampleArray).Data, ref sampleArray[0]); } } public unsafe static void ClearLessThanPointerSized(byte* ptr, UIntPtr byteLength) { if (sizeof(UIntPtr) == sizeof(uint)) { Unsafe.InitBlockUnaligned(ptr, 0, (uint)byteLength); } else { // PERF: Optimize for common case of length <= uint.MaxValue ulong bytesRemaining = (ulong)byteLength; uint bytesToClear = (uint)(bytesRemaining & uint.MaxValue); Unsafe.InitBlockUnaligned(ptr, 0, bytesToClear); bytesRemaining -= bytesToClear; ptr += bytesToClear; // Clear any bytes > uint.MaxValue while (bytesRemaining > 0) { bytesToClear = (bytesRemaining >= uint.MaxValue) ? uint.MaxValue : (uint)bytesRemaining; Unsafe.InitBlockUnaligned(ptr, 0, bytesToClear); ptr += bytesToClear; bytesRemaining -= bytesToClear; } } } public static unsafe void ClearLessThanPointerSized(ref byte b, UIntPtr byteLength) { if (sizeof(UIntPtr) == sizeof(uint)) { Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength); } else { // PERF: Optimize for common case of length <= uint.MaxValue ulong bytesRemaining = (ulong)byteLength; uint bytesToClear = (uint)(bytesRemaining & uint.MaxValue); Unsafe.InitBlockUnaligned(ref b, 0, bytesToClear); bytesRemaining -= bytesToClear; long byteOffset = bytesToClear; // Clear any bytes > uint.MaxValue while (bytesRemaining > 0) { bytesToClear = (bytesRemaining >= uint.MaxValue) ? uint.MaxValue : (uint)bytesRemaining; ref byte bOffset = ref Unsafe.Add(ref b, (IntPtr)byteOffset); Unsafe.InitBlockUnaligned(ref bOffset, 0, bytesToClear); byteOffset += bytesToClear; bytesRemaining -= bytesToClear; } } } public unsafe static void ClearPointerSizedWithoutReferences(ref byte b, UIntPtr byteLength) { // TODO: Perhaps do switch casing to improve small size perf var i = IntPtr.Zero; while (i.LessThanEqual(byteLength - sizeof(Reg64))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = default(Reg64); i += sizeof(Reg64); } if (i.LessThanEqual(byteLength - sizeof(Reg32))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = default(Reg32); i += sizeof(Reg32); } if (i.LessThanEqual(byteLength - sizeof(Reg16))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = default(Reg16); i += sizeof(Reg16); } if (i.LessThanEqual(byteLength - sizeof(long))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = 0; i += sizeof(long); } // JIT: Should elide this if 64-bit if (sizeof(IntPtr) == sizeof(int)) { if (i.LessThanEqual(byteLength - sizeof(int))) { Unsafe.As(ref Unsafe.Add(ref b, i)) = 0; i += sizeof(int); } } } public unsafe static void ClearPointerSizedWithReferences(ref IntPtr ip, UIntPtr pointerSizeLength) { // TODO: Perhaps do switch casing to improve small size perf var i = IntPtr.Zero; var n = IntPtr.Zero; while ((n = i + 8).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i + 0) = default(IntPtr); Unsafe.Add(ref ip, i + 1) = default(IntPtr); Unsafe.Add(ref ip, i + 2) = default(IntPtr); Unsafe.Add(ref ip, i + 3) = default(IntPtr); Unsafe.Add(ref ip, i + 4) = default(IntPtr); Unsafe.Add(ref ip, i + 5) = default(IntPtr); Unsafe.Add(ref ip, i + 6) = default(IntPtr); Unsafe.Add(ref ip, i + 7) = default(IntPtr); i = n; } if ((n = i + 4).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i + 0) = default(IntPtr); Unsafe.Add(ref ip, i + 1) = default(IntPtr); Unsafe.Add(ref ip, i + 2) = default(IntPtr); Unsafe.Add(ref ip, i + 3) = default(IntPtr); i = n; } if ((n = i + 2).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i + 0) = default(IntPtr); Unsafe.Add(ref ip, i + 1) = default(IntPtr); i = n; } if ((i + 1).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, i) = default(IntPtr); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static bool LessThanEqual(this IntPtr index, UIntPtr length) { return (sizeof(UIntPtr) == sizeof(uint)) ? (int)index <= (int)length : (long)index <= (long)length; } [StructLayout(LayoutKind.Sequential, Size = 64)] private struct Reg64 { } [StructLayout(LayoutKind.Sequential, Size = 32)] private struct Reg32 { } [StructLayout(LayoutKind.Sequential, Size = 16)] private struct Reg16 { } } } ================================================ FILE: jemalloc.Buffers/NativeMemory.cs ================================================ using System; using System.Buffers; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; using System.Reflection; using System.Runtime.ConstrainedExecution; using System.Collections.Generic; using System.Text; using System.Threading; namespace jemalloc.Buffers { public class NativeMemory : OwnedMemory where T : struct { #region Constructors public NativeMemory(int length) : base() { if (RuntimeHelpers.IsReferenceOrContainsReferences()) { throw new ArgumentException("Only structures without reference fields can be used with this class."); } this.length = length; Allocate(); } public NativeMemory(params T[] values) : this(values.Length) { Span span = Span; for (int i = 0; i < length; i++) { span[i] = values[i]; } } #endregion #region Overriden members public override int Length => length; protected override bool IsRetained => referenceCount > 0; public override bool IsDisposed => disposed; public unsafe override Span Span { get { return new Span(ptr.ToPointer(), Length); } } public override void Retain() { if (IsDisposed) { throw new InvalidOperationException("This buffer has been disposed."); } else { Interlocked.Increment(ref referenceCount); } } public override bool Release() { if (IsDisposed) { throw new InvalidOperationException("This buffer has been disposed."); } int newRefCount = Interlocked.Decrement(ref referenceCount); if (newRefCount < 0) { throw new InvalidOperationException("Reference count was decremented to < 0."); } else if (newRefCount == 0) { OnNoReferences(); return false; } else { return true; } } public override MemoryHandle Pin() { Retain(); unsafe { return new MemoryHandle(this, ptr.ToPointer()); } } protected override bool TryGetArray(out ArraySegment arraySegment) { arraySegment = default; return false; } #endregion #region Properties public ulong SizeInBytes => sizeInBytes; public int ReferenceCount => referenceCount; #endregion #region Methods [MethodImpl(MethodImplOptions.AggressiveInlining)] public Tensor AsTensor(params int[] dimensions) { Retain(); return new DenseTensor(this.Memory, new ReadOnlySpan(dimensions), false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Vector AsVector() { int count = Vector.Count; if (!NumericTypes.Contains(CLRType)) { throw new InvalidOperationException($"{CLRType.Name} is not a numeric type."); } else if (length != count) { throw new InvalidOperationException($"The length of the memory buffer must be {Vector.Count} elements to create a vector of type {CLRType.Name}."); } else { Retain(); object[] args = new object[2] { ptr, 0 }; Vector v = (Vector)VectorInternalConstructorUsingPointer.Invoke(args); return v; } } protected virtual IntPtr Allocate() { ptr = Jem.Calloc((ulong)length, (ulong) ElementSizeInBytes); sizeInBytes = (ulong)Length * (ulong) ElementSizeInBytes; return ptr; } protected virtual void OnNoReferences() {} protected bool IsNumericType() { if ( CLRType == ByteCLRType || CLRType == SByteCLRType || CLRType == UInt16CLRType || CLRType == Int16CLRType || CLRType == UInt32CLRType || CLRType == Int32CLRType || CLRType == UInt64CLRType || CLRType == Int64CLRType ) { return true; } else { return false; } } #endregion #region Operators public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Contract.Requires(!IsDisposed); if ((uint)index >= (Length)) throw new IndexOutOfRangeException(); unsafe { return ref Unsafe.Add(ref Unsafe.AsRef(ptr.ToPointer()), index); } } } #endregion #region Fields protected static readonly Type CLRType = typeof(T); protected static readonly T Element = default; protected static readonly int ElementSizeInBytes = Marshal.SizeOf(Element); private static ConstructorInfo VectorInternalConstructorUsingPointer = typeof(Vector).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(void*), typeof(int) }, null); private static readonly Type ByteCLRType = typeof(Byte); private static readonly Type SByteCLRType = typeof(SByte); private static readonly Type Int16CLRType = typeof(Int16); private static readonly Type UInt16CLRType = typeof(UInt16); private static readonly Type Int32CLRType = typeof(Int32); private static readonly Type UInt32CLRType = typeof(UInt32); private static readonly Type Int64CLRType = typeof(Int64); private static readonly Type UInt64CLRType = typeof(UInt64); private static readonly Type SingleCLRType = typeof(Single); private static readonly Type DoubleCLRType = typeof(Double); private static readonly Type StringCLRType = typeof(String); private static readonly HashSet NumericTypes = new HashSet(new Type[] { ByteCLRType, SByteCLRType, Int16CLRType, UInt16CLRType, Int32CLRType, UInt32CLRType, Int64CLRType, UInt64CLRType, SingleCLRType, DoubleCLRType }); IntPtr ptr; int length; ulong sizeInBytes; int referenceCount; bool disposed; #endregion #region Disposer and finalizer protected override void Dispose(bool disposing) { if (disposing && referenceCount > 0) { throw new InvalidOperationException($"This buffer cannot be disposed until all references are released. Reference count is {referenceCount}."); } if (ptr != null) { Jem.Free(ptr); } ptr = IntPtr.Zero; disposed = disposing; } ~NativeMemory() { Dispose(false); } #endregion } } ================================================ FILE: jemalloc.Buffers/jemalloc.Buffers.csproj ================================================ netcoreapp2.0 AnyCPU;x64 7.1 7.1 true ..\x64\Debug\ x64 7.1 true ..\x64\Debug\ x64 x64 7.1 x64 ..\x64\Release true 7.1 ================================================ FILE: jemalloc.Cli/Options.cs ================================================ using System; using System.Collections.Generic; using System.Text; using CommandLine; using CommandLine.Text; namespace jemalloc.Cli { class Options { [Option('u', "unsigned", Required = false, HelpText = "Use the unsigned version of the underlying data type.")] public bool Unsigned { get; set; } [Option('b', "int8", Required = false, HelpText = "Use byte as the underlying data type.", SetName = "type")] public bool Int8 { get; set; } [Option('h', "int16", Required = false, HelpText = "Use Int16 short integer as the underlying data type.", SetName = "type")] public bool Int16 { get; set; } [Option('i', "int32", Required = false, HelpText = "Use Int32 integer as the underlying data type.", SetName = "type")] public bool Int32 { get; set; } [Option('l', "int64", Required = false, HelpText = "Use Int64 long integer as the underlying data type.", SetName = "type")] public bool Int64 { get; set; } [Option('f', "float", Required = false, HelpText = "Use single-precision floating point as the underlying data type.", SetName = "type")] public bool Float { get; set; } [Option('d', "double", Required = false, HelpText = "Use double-precision floating point as the underlying data type.", SetName = "type")] public bool Double { get; set; } [Option('s', "string", Required = false, HelpText = "Use String as the underlying data type.", SetName = "type")] public bool String { get; set; } [Option("udt", Required = false, HelpText = "Use a user-defined data type as the underlying data type.", SetName = "type")] public bool Udt { get; set; } [Option('c', "cold-start", Required = false, HelpText = "Don't run warmup phase of benchmarks.")] public bool ColdStart { get; set; } [Option("once", Required = false, HelpText = "Run 1 iteration of benchmarks.")] public bool Once { get; set; } [Option('t', "target-count", Required = false, HelpText = "Set the target count of benchmark runs.", Default = 0)] public int TargetCount { get; set; } [Option("debug", Required = false, HelpText = "Run benchmarks in debug mode.")] public bool Debug { get; set; } } [Verb("malloc", HelpText = "Benchmark native memory allocation using jemalloc vs. .NET managed heap and Large Object Heap allocation.")] class MallocBenchmarkOptions : Options { [Option("create", Required = false, HelpText = "Benchmark malloc vs managed array alloc.")] public bool Create { get; set; } [Option("fill", Required = false, HelpText = "Benchmark fill Span on system unmanaged heap vs fill managed arrays.")] public bool Fill { get; set; } [Option("fragment", Required = false, HelpText = "Run an allocation pattern that fragments the LOH vs. native memory.")] public bool Fragment { get; set; } [Value(0, Required = true, HelpText = "The sizes of data structures to benchmark.")] public IEnumerable Sizes { get; set; } } [Verb("buffer", HelpText = "Benchmark FixedBuffer arrays backed by native memory allocated using jemalloc vs. .NET managed arrays.")] class FixedBufferBenchmarkOptions : Options { [Option("create", Required = false, HelpText = "Benchmark native array creation vs managed arrays.")] public bool Create { get; set; } [Option("fill", Required = false, HelpText = "Benchmark fill native array vs managed arrays.")] public bool Fill { get; set; } [Option("math", Required = false, HelpText = "Benchmark arithmetic and other math operations on native array vs managed arrays.")] public bool Math { get; set; } [Value(0, Required = true, HelpText = "The sizes of data structures to benchmark.")] public IEnumerable Sizes { get; set; } } [Verb("safe", HelpText = "Benchmark SafeArray arrays backed by native memory allocated using jemalloc vs. .NET managed arrays.")] class SafeArrayBenchmarkOptions : Options { [Option("create", Required = false, HelpText = "Benchmark native array creation vs managed arrays.")] public bool Create { get; set; } [Option("fill", Required = false, HelpText = "Benchmark fill native array vs managed arrays.")] public bool Fill { get; set; } [Option("math", Required = false, HelpText = "Benchmark arithmetic and other math operations on native array vs managed arrays.")] public bool Math { get; set; } [Value(0, Required = true, HelpText = "The sizes of data structures to benchmark.")] public IEnumerable Sizes { get; set; } } [Verb("huge", HelpText = "Benchmark HugeArray arrays backed by native memory allocated using jemalloc vs. .NET managed arrays.")] class HugeNativeArrayBenchmarkOptions : Options { [Option("create", Required = false, HelpText = "Benchmark huge native array creation vs managed arrays.")] public bool Create { get; set; } [Option("fill", Required = false, HelpText = "Benchmark huge native array fill vs managed arrays.")] public bool Fill { get; set; } [Value(0, Required = true, HelpText = "The sizes of data structures to benchmark.")] public IEnumerable Sizes { get; set; } } [Verb("vector", HelpText = "Benchmark SIMD vectorized algorithms on native memory data structures vs. SIMD using .NET managed arrays.")] class VectorBenchmarkOptions : Options { [Option("mandel", Required = false, HelpText = "Benchmark Mandelbrot bitmap generation.")] public bool Mandelbrot { get; set; } [Option("fill", Required = false, HelpText = "Benchmark Vector fill.")] public bool Fill { get; set; } [Option("test", Required = false, HelpText = "Benchmark Vector logical comparison and test.")] public bool Test { get; set; } [Value(0, Required = false, HelpText = "The scales of operations on vectors.")] public IEnumerable Scales { get; set; } } } ================================================ FILE: jemalloc.Cli/Program.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Reflection; using Serilog; using CommandLine; using CommandLine.Text; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Running; using BenchmarkDotNet.Reports; using jemalloc.Benchmarks; namespace jemalloc.Cli { class Program { public enum ExitResult { SUCCESS = 0, UNHANDLED_EXCEPTION = 1, INVALID_OPTIONS = 2 } static Version Version = Assembly.GetExecutingAssembly().GetName().Version; static LoggerConfiguration LConfig; static ILogger L; static Dictionary BenchmarkOptions = new Dictionary(); static Summary BenchmarkSummary; static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; LConfig = new LoggerConfiguration() .Enrich.FromLogContext() .Enrich.WithThreadId() .Enrich.WithProcessId() .MinimumLevel.Debug() .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss}<{ThreadId:d2}> [{Level:u3}] {Message}{NewLine}{Exception}"); L = Log.Logger = LConfig.CreateLogger(); Type[] BenchmarkOptionTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof(Options))).ToArray(); MethodInfo parseArgumentsMethod = typeof(ParserExtensions).GetMethods().Where(m => m.IsGenericMethod && m.Name == "ParseArguments" && m.GetGenericArguments().Count() == BenchmarkOptionTypes.Count()).First(); Parser p = new Parser(); ParserResult result = (ParserResult) parseArgumentsMethod.MakeGenericMethod(BenchmarkOptionTypes).Invoke(p , new object[] { p, args }); result.WithNotParsed((IEnumerable errors) => { HelpText help = GetAutoBuiltHelpText(result); help.MaximumDisplayWidth = Console.WindowWidth; help.Copyright = string.Empty; help.Heading = new HeadingInfo("jemalloc.NET", Version.ToString(3)); help.AddPreOptionsLine(string.Empty); if (errors.Any(e => e.Tag == ErrorType.VersionRequestedError)) { Log.Information(help); Exit(ExitResult.SUCCESS); } else if (errors.Any(e => e.Tag == ErrorType.HelpVerbRequestedError)) { HelpVerbRequestedError error = (HelpVerbRequestedError)errors.First(e => e.Tag == ErrorType.HelpVerbRequestedError); if (error.Type != null) { help.AddVerbs(error.Type); } Log.Information(help); Exit(ExitResult.SUCCESS); } else if (errors.Any(e => e.Tag == ErrorType.HelpRequestedError)) { help.AddVerbs(BenchmarkOptionTypes); L.Information(help); Exit(ExitResult.SUCCESS); } else if (errors.Any(e => e.Tag == ErrorType.NoVerbSelectedError)) { help.AddVerbs(BenchmarkOptionTypes); help.AddPreOptionsLine("No category selected. Select a category from the options below:"); L.Information(help); Exit(ExitResult.INVALID_OPTIONS); } else if (errors.Any(e => e.Tag == ErrorType.MissingRequiredOptionError)) { MissingRequiredOptionError error = (MissingRequiredOptionError)errors.First(e => e is MissingRequiredOptionError); help.AddOptions(result); help.AddPreOptionsLine($"A required option or value is missing. The options and values for this benchmark category are: "); L.Information(help); Exit(ExitResult.INVALID_OPTIONS); } else if (errors.Any(e => e.Tag == ErrorType.MissingValueOptionError)) { MissingValueOptionError error = (MissingValueOptionError)errors.First(e => e.Tag == ErrorType.MissingValueOptionError); help.AddOptions(result); help.AddPreOptionsLine($"A required option or value is missing. The options and values for this benchmark category are: "); L.Information(help); Exit(ExitResult.INVALID_OPTIONS); } else if (errors.Any(e => e.Tag == ErrorType.UnknownOptionError)) { UnknownOptionError error = (UnknownOptionError)errors.First(e => e.Tag == ErrorType.UnknownOptionError); help.AddOptions(result); help.AddPreOptionsLine($"Unknown option: {error.Token}."); L.Information(help); Exit(ExitResult.INVALID_OPTIONS); } else { help.AddPreOptionsLine($"An error occurred parsing the program options: {string.Join(' ', errors.Select(e => e.Tag.ToString()).ToArray())}"); help.AddVerbs(BenchmarkOptionTypes); L.Information(help); Exit(ExitResult.INVALID_OPTIONS); } }) .WithParsed((Options o) => { foreach (PropertyInfo prop in o.GetType().GetProperties()) { BenchmarkOptions.Add(prop.Name, prop.GetValue(o)); } if (o.ColdStart) { JemBenchmarkJobAttribute.ColdStartOverride = true; } if (o.TargetCount > 0) { JemBenchmarkJobAttribute.TargetCountOverride = o.TargetCount; } if (o.Once) { JemBenchmarkJobAttribute.ColdStartOverride = true; JemBenchmarkJobAttribute.TargetCountOverride = 1; } }) .WithParsed(o => { BenchmarkOptions.Add("Category", Category.MALLOC); if (o.Create) { BenchmarkOptions.Add("Operation", Operation.CREATE); } else if (o.Fill) { BenchmarkOptions.Add("Operation", Operation.FILL); } else if (o.Fragment) { BenchmarkOptions.Add("Operation", Operation.FRAGMENT); } if (!BenchmarkOptions.ContainsKey("Operation")) { Log.Error("You must select an operation to benchmark with --fill."); Exit(ExitResult.SUCCESS); } else { Benchmark(o); } }) .WithParsed(o => { BenchmarkOptions.Add("Category", Category.BUFFER); if (o.Create) { BenchmarkOptions.Add("Operation", Operation.CREATE); } else if (o.Fill) { BenchmarkOptions.Add("Operation", Operation.FILL); } else if (o.Math) { BenchmarkOptions.Add("Operation", Operation.MATH); } if (!BenchmarkOptions.ContainsKey("Operation")) { Log.Error("You must select an operation to benchmark with --create or --fill."); Exit(ExitResult.SUCCESS); } else { Benchmark(o); } }) .WithParsed(o => { BenchmarkOptions.Add("Category", Category.NARRAY); if (o.Create) { BenchmarkOptions.Add("Operation", Operation.CREATE); } else if (o.Fill) { BenchmarkOptions.Add("Operation", Operation.FILL); } else if (o.Math) { BenchmarkOptions.Add("Operation", Operation.MATH); } if (!BenchmarkOptions.ContainsKey("Operation")) { Log.Error("You must select an operation to benchmark with --create or --fill."); Exit(ExitResult.INVALID_OPTIONS); } else { Benchmark(o); } }) .WithParsed(o => { BenchmarkOptions.Add("Category", Category.HUGEARRAY); if (o.Create) { BenchmarkOptions.Add("Operation", Operation.CREATE); } else if (o.Fill) { BenchmarkOptions.Add("Operation", Operation.FILL); } if (!BenchmarkOptions.ContainsKey("Operation")) { Log.Error("You must select an operation to benchmark with --create or --fill or --math."); Exit(ExitResult.INVALID_OPTIONS); } else { Benchmark(o); } }) .WithParsed(o => { BenchmarkOptions.Add("Category", Category.VECTOR); if (o.Mandelbrot) { BenchmarkOptions.Add("Operation", Operation.MANDELBROT); o.Float = true; o.Int8 = o.Int16 = o.Int32 = o.Int64 = o.Double = o.String = o.Udt = false; } else if (o.Fill) { BenchmarkOptions.Add("Operation", Operation.FILL); } else if (o.Test) { BenchmarkOptions.Add("Operation", Operation.TEST); } if (!BenchmarkOptions.ContainsKey("Operation")) { Log.Error("You must select a vector operation to benchmark with --mandel or --fill or --test."); Exit(ExitResult.INVALID_OPTIONS); } else { Benchmark(o); } }); } static void Benchmark(Options o) { Contract.Requires(BenchmarkOptions.ContainsKey("Operation")); Contract.Requires(BenchmarkOptions.ContainsKey("Category")); Contract.Requires(BenchmarkOptions.ContainsKey("Sizes") || BenchmarkOptions.ContainsKey("Scales")); if (o.Int8 && o.Unsigned) { Benchmark(); } else if (o.Int8) { Benchmark(); } else if (o.Int16 && o.Unsigned) { Benchmark(); } else if (o.Int16) { Benchmark(); } else if (o.Int32 && o.Unsigned) { Benchmark(); } else if (o.Int32) { Benchmark(); } else if (o.Int64 && o.Unsigned) { Benchmark(); } else if (o.Int64) { Benchmark(); } else if (o.Float) { Benchmark(); } else if (o.Double) { Benchmark(); } else if (o.Udt) { Benchmark(); } else { L.Error("You must select a data type to benchmark with: -b, -i, -h, -l, -d, -s, and -u."); Exit(ExitResult.INVALID_OPTIONS); } } static void Benchmark() where T : struct, IEquatable, IComparable, IConvertible { Contract.Requires(BenchmarkOptions.ContainsKey("Category")); Contract.Requires(BenchmarkOptions.ContainsKey("Operation")); Contract.Requires(BenchmarkOptions.ContainsKey("Sizes")); IConfig config = ManualConfig .Create(DefaultConfig.Instance); JemBenchmarkAttribute.CurrentConfig = config; JemBenchmarkAttribute.Category = (Category)BenchmarkOptions["Category"]; JemBenchmarkAttribute.Operation = (Operation)BenchmarkOptions["Operation"]; try { switch ((Category)BenchmarkOptions["Category"]) { case Category.MALLOC: MallocVsArrayBenchmark.Debug = (bool)BenchmarkOptions["Debug"]; MallocVsArrayBenchmark.Category = JemBenchmarkAttribute.Category; MallocVsArrayBenchmark.Operation = JemBenchmarkAttribute.Operation; MallocVsArrayBenchmark.BenchmarkParameters = ((IEnumerable)BenchmarkOptions["Sizes"]).ToList(); switch ((Operation)BenchmarkOptions["Operation"]) { case Operation.CREATE: config = config.With(new NameFilter(name => name.Contains("Create"))); L.Information("Starting {num} create benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, MallocVsArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); break; case Operation.FILL: config = config.With(new NameFilter(name => name.Contains("Fill"))); L.Information("Starting {num} fill benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, MallocVsArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); break; case Operation.FRAGMENT: config = config.With(new NameFilter(name => name.Contains("Fragment"))); L.Information("Starting {num} fragment benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, MallocVsArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); break; default: throw new InvalidOperationException($"Unknown operation: {(Operation)BenchmarkOptions["Operation"]} for category {(Category)BenchmarkOptions["Category"]}."); } BenchmarkSummary = BenchmarkRunner.Run>(config); break; case Category.BUFFER: FixedBufferVsManagedArrayBenchmark.BenchmarkParameters = ((IEnumerable)BenchmarkOptions["Sizes"]).ToList(); FixedBufferVsManagedArrayBenchmark.Debug = (bool)BenchmarkOptions["Debug"]; FixedBufferVsManagedArrayBenchmark.Category = JemBenchmarkAttribute.Category; FixedBufferVsManagedArrayBenchmark.Operation = JemBenchmarkAttribute.Operation; switch ((Operation)BenchmarkOptions["Operation"]) { case Operation.CREATE: L.Information("Starting {num} create array benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, FixedBufferVsManagedArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); config = config .With(new NameFilter(name => name.StartsWith("Create"))); break; case Operation.FILL: L.Information("Starting {num} array fill benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, FixedBufferVsManagedArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); config = config .With(new NameFilter(name => name.StartsWith("Fill"))); break; case Operation.MATH: L.Information("Starting {num} array math benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, FixedBufferVsManagedArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); config = config .With(new NameFilter(name => name.StartsWith("Arith"))); break; default: throw new InvalidOperationException($"Unknown operation: {(Operation)BenchmarkOptions["Operation"]} for category {(Category)BenchmarkOptions["Category"]}."); } BenchmarkSummary = BenchmarkRunner.Run>(config); break; case Category.NARRAY: SafeVsManagedArrayBenchmark.BenchmarkParameters = ((IEnumerable)BenchmarkOptions["Sizes"]).ToList(); SafeVsManagedArrayBenchmark.Debug = (bool)BenchmarkOptions["Debug"]; SafeVsManagedArrayBenchmark.Category = JemBenchmarkAttribute.Category; SafeVsManagedArrayBenchmark.Operation = JemBenchmarkAttribute.Operation; switch ((Operation)BenchmarkOptions["Operation"]) { case Operation.CREATE: L.Information("Starting {num} create array benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, SafeVsManagedArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); config = config .With(new NameFilter(name => name.StartsWith("Create"))); break; case Operation.FILL: L.Information("Starting {num} array fill benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, SafeVsManagedArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); config = config .With(new NameFilter(name => name.StartsWith("Fill"))); break; case Operation.MATH: L.Information("Starting {num} array math benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, SafeVsManagedArrayBenchmark.BenchmarkParameters); L.Information("Please allow some time for the pilot and warmup phases of the benchmark."); config = config .With(new NameFilter(name => name.StartsWith("Arith"))); break; default: throw new InvalidOperationException($"Unknown operation: {(Operation)BenchmarkOptions["Operation"]} for category {(Category)BenchmarkOptions["Category"]}."); } BenchmarkSummary = BenchmarkRunner.Run>(config); break; case Category.HUGEARRAY: HugeNativeVsManagedArrayBenchmark.BenchmarkParameters = ((IEnumerable)BenchmarkOptions["Sizes"]).ToList(); HugeNativeVsManagedArrayBenchmark.Debug = (bool)BenchmarkOptions["Debug"]; HugeNativeVsManagedArrayBenchmark.Category = JemBenchmarkAttribute.Category; HugeNativeVsManagedArrayBenchmark.Operation = JemBenchmarkAttribute.Operation; switch ((Operation)BenchmarkOptions["Operation"]) { case Operation.CREATE: config = config.With(new NameFilter(name => name.Contains("Create"))); L.Information("Starting {num} huge array create benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, HugeNativeVsManagedArrayBenchmark.BenchmarkParameters); L.Information("This benchmark does not have a warmup phase but can still take a while (10-15 minutes.)"); break; case Operation.FILL: config = config.With(new NameFilter(name => name.Contains("Fill"))); L.Information("Starting {num} huge array fill benchmarks for data type {t} with array sizes: {s}", JemBenchmark.GetBenchmarkMethodCount>(), typeof(T).Name, HugeNativeVsManagedArrayBenchmark.BenchmarkParameters); L.Information("This benchmark does not have a warmup phase but can still take a while (10-15 minutes.)"); break; default: throw new InvalidOperationException($"Unknown operation: {(Operation)BenchmarkOptions["Operation"]} for category {(Category)BenchmarkOptions["Category"]}."); } BenchmarkSummary = BenchmarkRunner.Run>(config); break; case Category.VECTOR: VectorBenchmark.BenchmarkParameters = ((IEnumerable)BenchmarkOptions["Scales"]).ToList(); VectorBenchmark.Debug = (bool)BenchmarkOptions["Debug"]; VectorBenchmark.Category = JemBenchmarkAttribute.Category; VectorBenchmark.Operation = JemBenchmarkAttribute.Operation; switch ((Operation)BenchmarkOptions["Operation"]) { case Operation.MANDELBROT: config = config.With(BenchmarkStatisticColumn.ThreadCycles); config = config.With(BenchmarkStatisticColumn.ISPCResult); config = config.With(BenchmarkStatisticColumn.ISPCResult2); config = config.With(new NameFilter(name => name.StartsWith("Mandelbrot"))); L.Information("Starting vector Mandelbrot benchmarks with scale: {s}", VectorBenchmark.BenchmarkParameters); break; case Operation.FILL: config = config.With(new NameFilter(name => name.StartsWith("Fill"))); L.Information("Starting vector fill benchmarks with array sizes: {s}", VectorBenchmark.BenchmarkParameters); break; case Operation.TEST: config = config.With(new NameFilter(name => name.StartsWith("Test"))); L.Information("Starting vector logical comparison and test benchmarks with array sizes: {s}", VectorBenchmark.BenchmarkParameters); break; default: throw new InvalidOperationException($"Unknown operation: {(Operation)BenchmarkOptions["Operation"]} for category {(Category)BenchmarkOptions["Category"]}."); } BenchmarkSummary = BenchmarkRunner.Run>(config); break; default: throw new InvalidOperationException($"Unknown category: {(Category)BenchmarkOptions["Category"]}."); } } catch (Exception e) { Log.Error(e, "Exception thrown during benchmark."); Exit(ExitResult.UNHANDLED_EXCEPTION); } } static void Exit(ExitResult result) { Log.CloseAndFlush(); Environment.Exit((int)result); } static int ExitWithCode(ExitResult result) { Log.CloseAndFlush(); return (int)result; } static HelpText GetAutoBuiltHelpText(ParserResult result) { return HelpText.AutoBuild(result, h => { h.AddOptions(result); return h; }, e => { return e; }); } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception exception = (Exception)e.ExceptionObject; Log.Error(exception, "An unhandled exception occurred. The program will now shutdown."); Log.Error(exception.StackTrace); Exit(ExitResult.UNHANDLED_EXCEPTION); } } } ================================================ FILE: jemalloc.Cli/Properties/PublishProfiles/ReleaseProfile.pubxml ================================================  FileSystem Benchmark netcoreapp2.0 ..\x64\Publish\netcoreapp2.0 ================================================ FILE: jemalloc.Cli/jemalloc.Cli.csproj ================================================ Exe netcoreapp2.0 AnyCPU;x64 jemalloc.Cli 0.2.4.0 0.2.3.0 0.2.4-alpha Allister Beharry jemalloc.NET jemalloc CLI Native memory manager for .NET https://github.com/allisterb/jemalloc.NET/LICENSE https://github.com/allisterb/jemalloc.NET https://github.com/allisterb/jemalloc.NET ..\x64\Debug ..\x64\Debug ..\x64\Release default ================================================ FILE: jemalloc.Examples/TestUDT.cs ================================================ using System; using System.Collections.Generic; namespace jemalloc.Examples { public struct TestUDT : IEquatable, IComparable, IConvertible { public Guid ID { get; set; } public Utf8Buffer FirstName { get; set; } public Utf8Buffer LastName { get; set; } public DateTime? DOB { get; set; } public decimal Balance { get; set; } public FixedBuffer Data { get; set; } public FixedBuffer Photo { get; set; } public static TestUDT MakeTestRecord(Random rng) { Guid _id = Guid.NewGuid(); string _ids = _id.ToString("N"); byte[] photo = new byte[4096]; rng.NextBytes(photo); return new TestUDT() { ID = _id, FirstName = new Utf8Buffer("Gavial-" + _id.ToString("D").Substring(0, 4)), LastName = new Utf8Buffer("Buxarinovich-" + _id.ToString("D").Substring(0, 4)), DOB = _ids.StartsWith("7") ? (DateTime?)null : DateTime.UtcNow, Balance = 2131m, Photo = new FixedBuffer(photo) }; } public bool Equals(TestUDT r) { return this.ID == r.ID; } #region IConvertible public TypeCode GetTypeCode() { return TypeCode.Object; } bool IConvertible.ToBoolean(IFormatProvider provider) { throw new InvalidCastException(); } double GetDoubleValue() { throw new InvalidCastException(); } byte IConvertible.ToByte(IFormatProvider provider) { throw new InvalidCastException(); } char IConvertible.ToChar(IFormatProvider provider) { throw new InvalidCastException(); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new InvalidCastException(); } decimal IConvertible.ToDecimal(IFormatProvider provider) { throw new InvalidCastException(); } double IConvertible.ToDouble(IFormatProvider provider) { throw new InvalidCastException(); } short IConvertible.ToInt16(IFormatProvider provider) { throw new InvalidCastException(); } int IConvertible.ToInt32(IFormatProvider provider) { throw new InvalidCastException(); } long IConvertible.ToInt64(IFormatProvider provider) { throw new InvalidCastException(); } sbyte IConvertible.ToSByte(IFormatProvider provider) { throw new InvalidCastException(); } float IConvertible.ToSingle(IFormatProvider provider) { throw new InvalidCastException(); } string IConvertible.ToString(IFormatProvider provider) { return String.Format("({0}, {1})", FirstName, LastName); } object IConvertible.ToType(Type conversionType, IFormatProvider provider) { throw new InvalidCastException(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { throw new InvalidCastException(); } uint IConvertible.ToUInt32(IFormatProvider provider) { throw new InvalidCastException(); } ulong IConvertible.ToUInt64(IFormatProvider provider) { throw new InvalidCastException(); } #endregion #region IComparable public int CompareTo(TestUDT other) { return 0; } #endregion //public static ulong size = } } ================================================ FILE: jemalloc.Examples/jemalloc.Examples.csproj ================================================ netstandard2.0 x64 ..\x64\Debug\ ================================================ FILE: jemalloc.NET.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "jemalloc.Bindings", "jemalloc.Bindings\jemalloc.Bindings.csproj", "{C24A6949-BA9E-443B-A67E-188AB489057E}" ProjectSection(ProjectDependencies) = postProject {8D6BB292-9E1C-413D-9F98-4864BDC1514A} = {8D6BB292-9E1C-413D-9F98-4864BDC1514A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jemalloc", "jemalloc\msvc\projects\vc2017\jemalloc\jemalloc.vcxproj", "{8D6BB292-9E1C-413D-9F98-4864BDC1514A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.Api", "jemalloc.Api\jemalloc.Api.csproj", "{9CD27E9F-1F2C-4BEC-8019-743FF8D86589}" ProjectSection(ProjectDependencies) = postProject {8D6BB292-9E1C-413D-9F98-4864BDC1514A} = {8D6BB292-9E1C-413D-9F98-4864BDC1514A} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.Cli", "jemalloc.Cli\jemalloc.Cli.csproj", "{1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.Tests", "jemalloc.Tests\jemalloc.Tests.csproj", "{072F89BE-D090-49F5-928F-995AA8C2E87E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{42D17CA7-794A-4D51-A144-6039B9571E75}" ProjectSection(SolutionItems) = preProject build.cmd = build.cmd jembench.cmd = jembench.cmd README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.Buffers", "jemalloc.Buffers\jemalloc.Buffers.csproj", "{C4032FF9-E677-4ABD-9150-F292971959E9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.Benchmarks", "jemalloc.Benchmarks\jemalloc.Benchmarks.csproj", "{E5044104-376A-44A4-A1C0-D4F1C88D1149}" ProjectSection(ProjectDependencies) = postProject {C4032FF9-E677-4ABD-9150-F292971959E9} = {C4032FF9-E677-4ABD-9150-F292971959E9} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.StressTests", "jemalloc.StressTests\jemalloc.StressTests.csproj", "{F242A777-036C-4AB8-9393-3931A98A92F1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jemalloc.Examples", "jemalloc.Examples\jemalloc.Examples.csproj", "{5C4F8F7C-D264-4C59-89C6-F3D701EFA613}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Benchmark|Any CPU = Benchmark|Any CPU Benchmark|x64 = Benchmark|x64 Benchmark|x86 = Benchmark|x86 Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Debug-static|Any CPU = Debug-static|Any CPU Debug-static|x64 = Debug-static|x64 Debug-static|x86 = Debug-static|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 Release-static|Any CPU = Release-static|Any CPU Release-static|x64 = Release-static|x64 Release-static|x86 = Release-static|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C24A6949-BA9E-443B-A67E-188AB489057E}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Benchmark|Any CPU.Build.0 = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Benchmark|x64.ActiveCfg = Release|x64 {C24A6949-BA9E-443B-A67E-188AB489057E}.Benchmark|x86.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Benchmark|x86.Build.0 = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug|Any CPU.Build.0 = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug|x64.ActiveCfg = Debug|x64 {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug|x86.ActiveCfg = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug|x86.Build.0 = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug-static|x64.ActiveCfg = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug-static|x64.Build.0 = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug-static|x86.ActiveCfg = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Debug-static|x86.Build.0 = Debug|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release|Any CPU.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release|Any CPU.Build.0 = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release|x64.ActiveCfg = Release|x64 {C24A6949-BA9E-443B-A67E-188AB489057E}.Release|x86.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release|x86.Build.0 = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release-static|Any CPU.Build.0 = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release-static|x64.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release-static|x64.Build.0 = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release-static|x86.ActiveCfg = Release|Any CPU {C24A6949-BA9E-443B-A67E-188AB489057E}.Release-static|x86.Build.0 = Release|Any CPU {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Benchmark|Any CPU.ActiveCfg = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Benchmark|x64.ActiveCfg = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Benchmark|x64.Build.0 = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Benchmark|x86.ActiveCfg = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Benchmark|x86.Build.0 = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Benchmark|x86.Deploy.0 = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|Any CPU.ActiveCfg = Debug|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.ActiveCfg = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.Build.0 = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.Deploy.0 = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.ActiveCfg = Debug|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.Build.0 = Debug|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.Deploy.0 = Debug|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|Any CPU.ActiveCfg = Debug-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.ActiveCfg = Debug-static|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.Build.0 = Debug-static|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.Deploy.0 = Debug-static|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.ActiveCfg = Debug-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.Build.0 = Debug-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.Deploy.0 = Debug-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|Any CPU.ActiveCfg = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.ActiveCfg = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.Build.0 = Debug|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.ActiveCfg = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.Build.0 = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.Deploy.0 = Release|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|Any CPU.ActiveCfg = Release-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.ActiveCfg = Release-static|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.Build.0 = Release-static|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.Deploy.0 = Release-static|x64 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.ActiveCfg = Release-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.Build.0 = Release-static|Win32 {8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.Deploy.0 = Release-static|Win32 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Benchmark|Any CPU.Build.0 = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Benchmark|x64.ActiveCfg = Release|x64 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Benchmark|x64.Build.0 = Release|x64 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Benchmark|x86.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Benchmark|x86.Build.0 = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug|x64.ActiveCfg = Debug|x64 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug|x64.Build.0 = Debug|x64 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug|x86.ActiveCfg = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug|x86.Build.0 = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug-static|x64.ActiveCfg = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug-static|x64.Build.0 = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug-static|x86.ActiveCfg = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Debug-static|x86.Build.0 = Debug|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release|Any CPU.Build.0 = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release|x64.ActiveCfg = Release|x64 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release|x64.Build.0 = Release|x64 {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release|x86.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release|x86.Build.0 = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release-static|Any CPU.Build.0 = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release-static|x64.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release-static|x64.Build.0 = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release-static|x86.ActiveCfg = Release|Any CPU {9CD27E9F-1F2C-4BEC-8019-743FF8D86589}.Release-static|x86.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Benchmark|Any CPU.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Benchmark|x64.ActiveCfg = Release|x64 {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Benchmark|x64.Build.0 = Release|x64 {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Benchmark|x86.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Benchmark|x86.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug|x64.ActiveCfg = Debug|x64 {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug|x64.Build.0 = Debug|x64 {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug|x86.ActiveCfg = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug|x86.Build.0 = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug-static|x64.ActiveCfg = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug-static|x64.Build.0 = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug-static|x86.ActiveCfg = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Debug-static|x86.Build.0 = Debug|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release|Any CPU.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release|Any CPU.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release|x64.ActiveCfg = Release|x64 {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release|x86.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release|x86.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release-static|Any CPU.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release-static|x64.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release-static|x64.Build.0 = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release-static|x86.ActiveCfg = Release|Any CPU {1FE3EBE7-3922-4CAE-A142-B1C4DF935B0F}.Release-static|x86.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Benchmark|Any CPU.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Benchmark|x64.ActiveCfg = Release|x64 {072F89BE-D090-49F5-928F-995AA8C2E87E}.Benchmark|x86.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Benchmark|x86.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug|Any CPU.Build.0 = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug|x64.ActiveCfg = Debug|x64 {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug|x64.Build.0 = Debug|x64 {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug|x86.ActiveCfg = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug|x86.Build.0 = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug-static|x64.ActiveCfg = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug-static|x64.Build.0 = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug-static|x86.ActiveCfg = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Debug-static|x86.Build.0 = Debug|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release|Any CPU.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release|Any CPU.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release|x64.ActiveCfg = Release|x64 {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release|x86.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release|x86.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release-static|Any CPU.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release-static|x64.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release-static|x64.Build.0 = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release-static|x86.ActiveCfg = Release|Any CPU {072F89BE-D090-49F5-928F-995AA8C2E87E}.Release-static|x86.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Benchmark|Any CPU.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Benchmark|x64.ActiveCfg = Release|x64 {C4032FF9-E677-4ABD-9150-F292971959E9}.Benchmark|x64.Build.0 = Release|x64 {C4032FF9-E677-4ABD-9150-F292971959E9}.Benchmark|x86.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Benchmark|x86.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug|x64.ActiveCfg = Debug|x64 {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug|x64.Build.0 = Debug|x64 {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug|x86.ActiveCfg = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug|x86.Build.0 = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug-static|x64.ActiveCfg = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug-static|x64.Build.0 = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug-static|x86.ActiveCfg = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Debug-static|x86.Build.0 = Debug|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release|Any CPU.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release|x64.ActiveCfg = Release|x64 {C4032FF9-E677-4ABD-9150-F292971959E9}.Release|x86.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release|x86.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release-static|Any CPU.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release-static|x64.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release-static|x64.Build.0 = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release-static|x86.ActiveCfg = Release|Any CPU {C4032FF9-E677-4ABD-9150-F292971959E9}.Release-static|x86.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Benchmark|Any CPU.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Benchmark|x64.ActiveCfg = Release|x64 {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Benchmark|x64.Build.0 = Release|x64 {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Benchmark|x86.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Benchmark|x86.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug|x64.ActiveCfg = Release|x64 {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug|x86.ActiveCfg = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug|x86.Build.0 = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug-static|x64.ActiveCfg = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug-static|x64.Build.0 = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug-static|x86.ActiveCfg = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Debug-static|x86.Build.0 = Debug|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release|Any CPU.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release|x64.ActiveCfg = Release|x64 {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release|x86.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release|x86.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release-static|Any CPU.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release-static|x64.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release-static|x64.Build.0 = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release-static|x86.ActiveCfg = Release|Any CPU {E5044104-376A-44A4-A1C0-D4F1C88D1149}.Release-static|x86.Build.0 = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Benchmark|Any CPU.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Benchmark|Any CPU.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Benchmark|x64.ActiveCfg = Debug|x64 {F242A777-036C-4AB8-9393-3931A98A92F1}.Benchmark|x86.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Benchmark|x86.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug|x64.ActiveCfg = Debug|x64 {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug|x64.Build.0 = Debug|x64 {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug|x86.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug|x86.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug-static|x64.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug-static|x64.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug-static|x86.ActiveCfg = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Debug-static|x86.Build.0 = Debug|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release|Any CPU.Build.0 = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release|x64.ActiveCfg = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release|x64.Build.0 = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release|x86.ActiveCfg = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release|x86.Build.0 = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release-static|Any CPU.Build.0 = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release-static|x64.ActiveCfg = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release-static|x64.Build.0 = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release-static|x86.ActiveCfg = Release|Any CPU {F242A777-036C-4AB8-9393-3931A98A92F1}.Release-static|x86.Build.0 = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Benchmark|Any CPU.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Benchmark|Any CPU.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Benchmark|x64.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Benchmark|x64.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Benchmark|x86.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Benchmark|x86.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug|x64.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug|x64.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug|x86.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug|x86.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug-static|Any CPU.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug-static|Any CPU.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug-static|x64.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug-static|x64.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug-static|x86.ActiveCfg = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Debug-static|x86.Build.0 = Debug|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release|Any CPU.Build.0 = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release|x64.ActiveCfg = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release|x64.Build.0 = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release|x86.ActiveCfg = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release|x86.Build.0 = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release-static|Any CPU.ActiveCfg = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release-static|Any CPU.Build.0 = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release-static|x64.ActiveCfg = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release-static|x64.Build.0 = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release-static|x86.ActiveCfg = Release|Any CPU {5C4F8F7C-D264-4C59-89C6-F3D701EFA613}.Release-static|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {03EC717B-98F6-4CEE-A5D7-236D8AF7B581} EndGlobalSection EndGlobal ================================================ FILE: jemalloc.StressTests/FixedBufferStressTests.cs ================================================ using System; using Xunit; namespace jemalloc.StressTests { public class FixedBufferStressTests { [Fact(DisplayName ="Can allocate fixed buffers")] public void Test1() { int count = 0; while (count < 10) { FixedBuffer b = new FixedBuffer(JemUtil.Rng.Next(100000, 1000000)); int r = JemUtil.Rng.Next(0, 64); b.Fill(r); for(int i = 0; i < b.Length; i++) { Assert.Equal(r, b[i]); } Assert.True(b.Free()); count++; } } } } ================================================ FILE: jemalloc.StressTests/jemalloc.StressTests.csproj ================================================ netcoreapp2.0 false AnyCPU;x64 ..\x64\Debug\ ================================================ FILE: jemalloc.Tests/BufferTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace jemalloc.Tests { public class BufferTests : jemallocTest { [Fact] public void CanConstructBuffer() { Buffer buffer = new Buffer(1000000000); buffer[32] = 12; Assert.Equal(12, buffer[32]); } } } ================================================ FILE: jemalloc.Tests/FixedBufferTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace jemalloc.Tests { public class FixedBufferTests : jemallocTest { [Fact(DisplayName = "Can create a fixed buffer of bytes")] public void CanCreateFixedArray() { FixedBuffer buffer = new FixedBuffer(4096); ref byte z = ref buffer[0]; byte[] managedArray = new byte[4096]; SafeArray> byteBuffer = new SafeArray>(1000); byteBuffer[0] = new FixedBuffer(100); byteBuffer[0][16]= 0xff; Assert.Equal(0xff, byteBuffer[0][16]); byteBuffer[0][16] = 4; ref FixedBuffer a = ref byteBuffer[0]; for (int i = 0; i < buffer.Length; i++) { byte v = (byte)Rng.Next(0, 255); managedArray[i] = v; buffer[i] = v; } FixedBuffer copy = buffer; Assert.True(buffer.EqualTo(managedArray)); Assert.True(copy.EqualTo(managedArray)); buffer.Free(); Assert.Throws(() => buffer[0] = 1); Assert.Throws(() => copy[0]); } [Fact(DisplayName = "Can deallocate a fixed buffer of bytes")] public void CanDeAllocateFixedArray() { SafeArray> byteBuffer = new SafeArray>(100); for (int i = 0; i < 100; i++) { ulong jem_before_alloc = Jem.AllocatedBytes; byteBuffer[i] = new FixedBuffer((i * 16) + (1024 * 1024)); ulong jem_after_alloc = Jem.AllocatedBytes; //Assert.True(jem_after_alloc > jem_before_alloc); byteBuffer.Release(); long mem_after_free = JemUtil.ProcessPrivateMemory; //Assert.True(mem_after_free < mem_after_alloc); } } } } ================================================ FILE: jemalloc.Tests/HugeArrayTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using Xunit; namespace jemalloc.Tests { public class HugeArrayTests : jemallocTest { Random rng = new Random(); [Fact(DisplayName = "Can construct huge array")] public void CanConstructHugeArray() { ulong arraySize = 2L * Int32.MaxValue; ulong point = 1L * Int32.MaxValue; HugeArray array = new HugeArray(arraySize); array[point] = 1; Assert.Equal(1, array[point]); array.Close(); } [Fact(DisplayName = "Can correctly assign to HugeArray elements")] public void CanAssignToHugeArrayElements() { ulong arraySize = 2L * Int32.MaxValue; HugeArray array = new HugeArray(arraySize); ulong[] indices = new ulong[10000]; int scale = 0, value = 0; ulong v = 0; for (int i = 0; i < indices.Length; i++) { v = indices[0]; while (indices.Contains(v)) { scale = rng.Next(1, 2); value = rng.Next(0, Int32.MaxValue - 1); v = (ulong)scale * (ulong)value; } array[v] = i; indices[i] = v; } for (int i = 0; i < indices.Length; i++) { Assert.Equal(i, array[indices[i]]); } array.Close(); } [Fact(DisplayName = "Can convert to Vector")] public void CanConvertToVector() { HugeArray a = new HugeArray(8, 1, 11, 94, 5, 0, 0, 0, 8); Vector v = a.GetAsSingleVector(); Assert.Equal(a[0], v[0]); Assert.Equal(a[3], v[3]); Assert.Equal(a[7], v[7]); HugeArray a2 = new HugeArray(12, 11, 112, 594, 65, 0, 0, 0, 8, 14, 90, 2, 8); Vector v2 = a2.GetSliceAsSingleVector(0); Assert.Equal(11u, v2[0]); Assert.Equal(8u, v2[7]); HugeArray a3 = new HugeArray((ulong)Int32.MaxValue + 10000); a3.Fill(7u); a3[(ulong)Int32.MaxValue + 100] = 9; a3[(ulong)Int32.MaxValue + 101] = 4; Vector v3 = a3.GetSliceAsSingleVector((ulong)Int32.MaxValue + 99); Assert.Equal(9u, v3[1]); Assert.Equal(4u, v3[2]); Assert.Equal(a3[(ulong)Int32.MaxValue + 99], v3[0]); Assert.Equal(7u, v3[0]); Assert.Equal(7u, v3[7]); a.Close(); a2.Close(); a3.Close(); } [Fact(DisplayName = "Can correctly fill")] public void CanFill() { HugeArray array = new HugeArray(1000); array.Fill(33); Assert.Equal(33, array[999]); array.Close(); } } } ================================================ FILE: jemalloc.Tests/MallCtlTests.cs ================================================ using System; using System.Diagnostics; using Xunit; namespace jemalloc.Tests { public class MallCtlTests : jemallocTest { public MallCtlTests() : base() {} [Fact] public void CanReadMallCtlInt32() { Assert.Equal(3, Jem.GetMallCtlInt32("opt.narenas")); } [Fact] public void CanReadMallCtlBool() { Assert.True(Jem.GetMallCtlBool("config.debug")); Assert.False(Jem.GetMallCtlBool("config.xmalloc")); } [Fact] public void CanReadMallCtlStr() { Assert.StartsWith("5", Jem.GetMallCtlStr("version")); } } } ================================================ FILE: jemalloc.Tests/MallocConfTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace jemalloc.Tests { public class MallocConfTests : jemallocTest { public MallocConfTests() : base() {} [Fact] public void CanGetConf() { Assert.Equal("tcache:false,narenas:3", Jem.MallocConf); } } } ================================================ FILE: jemalloc.Tests/MallocMessageTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace jemalloc.Tests { public class MallocMessageTests : jemallocTest { [Fact] public void CanPrintMallocStats() { Assert.Contains("opt.narenas: 3", Jem.MallocStats); } } } ================================================ FILE: jemalloc.Tests/MallocTests.cs ================================================ using System; using System.Diagnostics; using Xunit; namespace jemalloc.Tests { public class MallocTests { public Process CurrentProcess { get; protected set; } = Process.GetCurrentProcess(); public MallocTests() : base() { init_privateMemorySize = CurrentProcess.PrivateMemorySize64; init_peakPagedMem = CurrentProcess.PeakPagedMemorySize64; init_peakWorkingSet = CurrentProcess.PeakWorkingSet64; init_peakVirtualMem = CurrentProcess.PeakVirtualMemorySize64; init_allocated = Jem.GetMallCtlUInt64("stats.allocated"); } [Fact] public void CanMallocandFree() { Jem.Init("retain=false"); long size = 100 * 1000 * 1000; Assert.True(init_privateMemorySize < size); Assert.True(init_allocated < (ulong) size); IntPtr p = Jem.Malloc((ulong) size); string stats = Jem.MallocStats; ulong allocated = Jem.GetMallCtlUInt64("stats.allocated"); CurrentProcess.Refresh(); Assert.True((CurrentProcess.PrivateMemorySize64 - init_privateMemorySize) >= size); Assert.True(allocated > (ulong)size); Jem.Free(p); } #region Fields long init_privateMemorySize = 0; long init_peakPagedMem = 0; long init_peakWorkingSet = 0; long init_peakVirtualMem = 0; ulong init_allocated; #endregion } } ================================================ FILE: jemalloc.Tests/SafeArrayTests.cs ================================================ using System; using System.Numerics; using Xunit; namespace jemalloc.Tests { public class SafeArrayTests : jemallocTest { [Fact(DisplayName = "Can construct SafeArray")] public void CanConstructSafeArray() { SafeArray a = new SafeArray(500); a[1] = 1000; Assert.Equal(1000, a[1]); a.Acquire(); Assert.Equal(1000, a[1]); a.Acquire(); a.Release(); Assert.Equal(1000, a[1]); a.Release(); Assert.Equal(1000, a[1]); a.Close(); Assert.True(a.IsClosed); Assert.Throws(() => a[1] == 1000); //int r = a[(2,3)] } [Fact(DisplayName = "Can convert to Vector")] public void CanConvertToVector() { SafeArray a = new SafeArray(8, 1, 11, 94, 5, 0, 0, 0, 8); Vector v = a.GetVector(); Assert.Equal(a[0], v[0]); Assert.Equal(a[3], v[3]); Assert.Equal(a[7], v[7]); SafeArray a2 = new SafeArray(12, 11, 112, 594, 65, 0, 0, 0, 8, 14, 90, 2, 8); Vector v2 = a2.GetSliceAsVector(2); Assert.Equal(594u, v2[0]); } [Fact] public void CanVectorizedMultiply() { SafeArray a = new SafeArray(8, 1, 2, 3, 4, 5, 6, 7, 8); SafeArray b = new SafeArray(8, 111, 22, 345, 40888, 3 , 777, 99, 6); a.VectorMultiply(2); Assert.Equal(2u, a[0]); Assert.Equal(8u, a[3]); Assert.Equal(16u, a[7]); b.VectorMultiply(6); Assert.Equal(666, b[0]); } } } ================================================ FILE: jemalloc.Tests/UDTTests.cs ================================================ using System; using System.Collections.Generic; using System.Buffers; using System.Text; using jemalloc.Examples; namespace jemalloc.Tests { public class UDTTests : jemallocTest { public SafeArray Employees; public UDTTests() { Employees = new SafeArray(1024 * 1024); for (int i = 0; i < Employees.Length; i++) { Employees[i] = TestUDT.MakeTestRecord(JemUtil.Rng); } } public void CanVectorize() { Span s = Employees.GetSpan(); int size = JemUtil.SizeOfStruct(); /* for (int i = 0; i < Employees.Length; i+= size * Employees[i].) { //s.Slice } */ } } } ================================================ FILE: jemalloc.Tests/Utf8BufferTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace jemalloc.Tests { public class Utf8BufferTests : jemallocTest { [Fact(DisplayName = "Can construct Utf8Buffer")] public void CanConstructUtf8String() { Utf8Buffer s = new Utf8Buffer("Hello World"); Assert.Equal(6, s.IndexOf("W")); } } } ================================================ FILE: jemalloc.Tests/VectorTests.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Xunit; using jemalloc.Buffers; namespace jemalloc.Tests { public class VectorTests { public const int Mandelbrot_Width = 768, Mandelbrot_Height = 512; public const int Mandelbrot_Size = (int)Mandelbrot_Width * (int)Mandelbrot_Height; public int VectorWidth = Vector.Count; public readonly Vector Limit = new Vector(4f); public readonly Vector Zero = Vector.Zero; public readonly Vector MinusOne = Vector.Negate(Vector.One); public VectorTests() { } [Fact(DisplayName = "Buffer elements can be accessed as vector.")] public void CanConstructVectors() { NativeMemory memory = new NativeMemory(1, 11, 94, 5, 0, 0, 0, 8); Vector v = memory.AsVector(); Assert.True(v[0] == 1); Assert.True(v[1] == 11); Assert.True(v[2] == 94); NativeMemory> vectors = new NativeMemory>(4); vectors.Retain(); } [Fact(DisplayName = "Can correctly run Mandelbrot algorithm using Vector2 and managed arrays.")] public void CanVectorizeMandelbrotManaged() { byte[] o = _MandelbrotManagedv4(); Assert.Equal(0, o[0]); Assert.Equal(2, o[1000]); Assert.Equal(10, o[500]); WriteMandelbrotPPM(o, "mandelbrot-managed-v4.ppm"); } [Fact(DisplayName = "Can correctly run Mandelbrot algorithm using Vector2 and managed arrays.")] public void CanVectorizeMandelbrotManaged5() { int[] o = new int[Mandelbrot_Height * Mandelbrot_Width]; _MandelbrotManagedv5(ref o); Assert.Equal(0, o[0]); Assert.Equal(2, o[1000]); Assert.Equal(10, o[500]); WriteMandelbrotPPM(o, "mandelbrot-managed-v5.ppm"); } [Fact(DisplayName = "Can correctly run Mandelbrot algorithm using Vector2 and unmanaged arrays.")] public void CanVectorizeMandelbrotUnmanaged() { FixedBuffer output = new FixedBuffer(Mandelbrot_Height * Mandelbrot_Width); FixedBuffer o = _Mandelbrotv2Unmanaged(ref output); Assert.Equal(0, o[0]); Assert.Equal(2, o[1000]); Assert.Equal(10, o[500]); WriteMandelbrotPPM(o.CopyToArray(), "mandelbrot_unmanaged.ppm"); o.Free(); } #region Mandelbrot algorithms private int[] VectorizeMandelbrotManaged() { int[] output = new int[((int)Mandelbrot_Width * (int)Mandelbrot_Height)]; Vector2 B = new Vector2(Mandelbrot_Width, Mandelbrot_Height); Vector2 C0 = new Vector2(-2, -1); Vector2 C1 = new Vector2(1, 1); Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i++) { Vector2 P = new Vector2(i, j); index = unchecked(j * (int)Mandelbrot_Width + i); Vector2 V = C0 + (P * D); output[index] = GetByte(ref V, 256); } } return output; int GetByte(ref Vector2 c, int count) { Vector2 z = c; int i; for (i = 0; i < count; i++) { if (z.LengthSquared() > 4f) { break; } Vector2 w = z * z; z = c + new Vector2(w.X - w.Y, 2f * z.X * z.Y); } return i; } } private FixedBuffer Mandelbrotv1Unmanaged() { SafeArray> Vectors = new SafeArray>(8); // New unmanaged array of vectors FixedBuffer output = new FixedBuffer(((int)Mandelbrot_Width * (int)Mandelbrot_Height)); //New unmanaged array for bitmap output Span VectorSpan = Vectors.GetSpan(); //Lets us write to individual vector elements Span Vector2Span = Vectors.GetSpan(); //Lets us read to individual vectors VectorSpan[0] = -2f; VectorSpan[1] = -1f; VectorSpan[2] = 1f; VectorSpan[3] = 1f; VectorSpan[4] = Mandelbrot_Width; VectorSpan[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; ref Vector2 P = ref Vector2Span[3]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i++) { VectorSpan[6] = i; VectorSpan[7] = j; index = unchecked(j * (int)Mandelbrot_Width + i); Vector2 V = C0 + (P * D); output[index] = GetByte(ref V, 256); } } Vectors.Close(); return output; int GetByte(ref Vector2 c, int max_iterations) { Vector2 z = c; //make a copy int i; for (i = 0; i < max_iterations; i++) { if (z.LengthSquared() > 4f) { break; } Vector2 w = z * z; z = c + new Vector2(w.X - w.Y, 2f * z.X * z.Y); } return i; } } private void WriteMandelbrotPPM(int[] output, string name) { using (StreamWriter sw = new StreamWriter(name)) { sw.Write("P6\n"); sw.Write(string.Format("{0} {1}\n", Mandelbrot_Width, Mandelbrot_Height)); sw.Write("255\n"); sw.Close(); } using (BinaryWriter bw = new BinaryWriter(new FileStream(name, FileMode.Append))) { for (int i = 0; i < Mandelbrot_Width * Mandelbrot_Height; i++) { byte b = output[i] == 256 ? (byte) 20 : (byte) 240; bw.Write(b); bw.Write(b); bw.Write(b); } } } private void WriteMandelbrotPPM(byte[] output, string name) { using (StreamWriter sw = new StreamWriter(name)) { sw.Write("P6\n"); sw.Write(string.Format("{0} {1}\n", Mandelbrot_Width, Mandelbrot_Height)); sw.Write("255\n"); sw.Close(); } using (BinaryWriter bw = new BinaryWriter(new FileStream(name, FileMode.Append))) { for (int i = 0; i < Mandelbrot_Width * Mandelbrot_Height; i++) { byte b = output[i] == 255 ? (byte)20 : (byte)240; bw.Write(b); bw.Write(b); bw.Write(b); } } } #region WIP private unsafe FixedBuffer VectorDoubleMandelbrot() { //Allocate heap and stack memory for our Vector constants and variables FixedBuffer output = new FixedBuffer(Mandelbrot_Size); //Output bitmap on unmanaged heap double* ptrC0 = stackalloc double[VectorWidth * 2]; double* ptrC1 = stackalloc double[VectorWidth * 2]; double* ptrB = stackalloc double[VectorWidth * 2]; double* ptrD = stackalloc double[VectorWidth * 2]; double* ptrP = stackalloc double[VectorWidth * 2]; double* ptrV = stackalloc double[VectorWidth * 2]; //Fill memory with the constant values for vectors C0, C1, B for (int i = 0; i < VectorWidth; i++) { ptrC0[i] = -2f; //x0 ptrC0[i + VectorWidth] = -1; //y0 ptrC1[i] = 1; //x1 ptrC1[i + VectorWidth] = 1; //y1 ptrB[i] = Mandelbrot_Width; //width ptrB[i + VectorWidth] = Mandelbrot_Height; //height } //Declare spans for reading and writing to memory locations of Vector constants and variables Span> C0 = new Span>(ptrC0, 2); Span> C1 = new Span>(ptrC1, 2); Span> B = new Span>(ptrB, 2); Span> D = new Span>(ptrD, 2); Span> P = new Span>(ptrP, 2); Span> V = new Span>(ptrV, 2); Span> O = output.AcquireVectorWriteSpan(); D[0] = (C1[0] - C0[0]) / B[0]; D[1] = (C1[0] - C0[0]) / B[0]; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { ptrP[h] = i + h; } index = unchecked((int)Mandelbrot_Width * j + i); V[0] = C0[0] + (P[0] * D[0]); V[1] = C0[1] + (P[1] * D[1]); Vector G = GetValue(V[0], V[1], 256); O[index] = G; } } output.Release(); return output; } private Vector SquareAbs(Vector Vre, Vector Vim) { return (Vre * Vre) + (Vim * Vim); } private unsafe Vector GetValue(Vector Cx, Vector Cy, int maxIterations) { //int* ptrIterations = stackalloc int[VectorWidth]; double[] iterationsArr = new double[VectorWidth]; //memory for Iterations vector Span sIterations = new Span(iterationsArr); //write to Iterations vector Vector Iterations = sIterations.NonPortableCast>()[0]; Vector MaxIterations = new Vector(256); double[] zArr = new double[VectorWidth * 2]; //double* ptrZ = stackalloc double[VectorWidth * 2]; Span sZ = new Span(zArr); //Write to individual components of Z Span> Z = sZ.NonPortableCast>(); Z[0] = Cx; Z[1] = Cy; for (int i = 0; i < maxIterations; i++) { sIterations.Fill(i); Vector S = SquareAbs(Z[0], Z[1]); if (Vector.GreaterThanAll(S, Limit)) { break; } else { Vector increment; do { Z[0] = Cx + (Z[0] * Z[0]) - (Z[0] * Z[1]); Z[1] = Cy + 2f * Z[0] * Z[1]; S = SquareAbs(Z[0], Z[1]); Vector greaterThanLimitMask = Vector.GreaterThan(S, Limit); Vector lessThanOrEqualMaxIterationsMask = Vector.LessThanOrEqual(Iterations, MaxIterations); increment = greaterThanLimitMask & lessThanOrEqualMaxIterationsMask; i += 1; } while (increment != Vector.Zero); } } return Vector.ConvertToInt64(new Vector(iterationsArr)); } private FixedBuffer _Mandelbrotv2Unmanaged(ref FixedBuffer output) { VectorWidth = Vector.Count; Vector One = Vector.One; Vector Zero = Vector.Zero; FixedBuffer Vectors = new FixedBuffer(6); FixedBuffer P = new FixedBuffer(VectorWidth * 2); Span Vector2Span = Vectors.AcquireWriteSpan().NonPortableCast(); //Lets us read individual Vector2 Span> PSpan = P.AcquireWriteSpan().NonPortableCast>(); //Lets us read individual Vectors Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { P[h] = C0.X + (D.X * (i + h)); P[h + VectorWidth] = C0.Y + (D.Y * j); } index = unchecked(j * Mandelbrot_Width + i); Vector Vre = PSpan[0]; Vector Vim = PSpan[1]; ; Vector outputVector = GetByte(ref Vre, ref Vim, 256); for (int h = 0; h < VectorWidth; h++) { output[index + h] = outputVector[h] < 255 ? (byte)outputVector[h] : (byte) 255; } } } Vectors.Release(); Vectors.Free(); P.Release(); P.Free(); return output; Vector GetByte(ref Vector Cre, ref Vector Cim, int max_iterations) { Vector Zre = Cre; //make a copy Vector Zim = Cim; //make a copy Vector Limit = new Vector(4); Vector MaxIterations = new Vector(max_iterations); Vector Increment = One; Vector I; for (I = Zero; Increment != Zero; I += Vector.Abs(Increment)) { Vector S = SquareAbs(Zre, Zim); Increment = Vector.LessThanOrEqual(S, Limit) & Vector.LessThanOrEqual(I, MaxIterations); if (Increment == Zero) { break; } else { Vector Tre = Zre; Vector Tim = Zim; Zre = Cre + (Tre * Tre - Tim * Tim); Zim = Cim + 2f * Tre * Tim; } } return I; } Vector SquareAbs(Vector Vre, Vector Vim) { return (Vre * Vre) + (Vim * Vim); } } private byte[] _MandelbrotManagedv4() { byte[] output = new byte[Mandelbrot_Height * Mandelbrot_Width]; Vector One = Vector.One; Vector Zero = Vector.Zero; float[] Vectors = new float[6]; Span Vector2Span = new Span(Vectors).NonPortableCast(); //Lets us read individual Vector2 Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; Vector2 C0 = Vector2Span[0]; Vector2 C1 = Vector2Span[1]; Vector2 B = Vector2Span[2]; Vector2 D = (C1 - C0) / B; for (int j = 0; j < Mandelbrot_Height; j++) { Parallel.ForEach(MandelbrotBitmapLocation(j), (p) => { int i = p.Item1; float[] Pre = new float[VectorWidth]; float[] Pim = new float[VectorWidth]; for (int h = 0; h < VectorWidth; h++) { Pre[h] = C0.X + (D.X * (i + h)); Pim[h] = C0.Y + (D.Y * p.Item2); } int index = unchecked(p.Item2 * Mandelbrot_Width + i); Vector Vre = new Vector(Pre); Vector Vim = new Vector(Pim); Vector outputVector = GetByte(ref Vre, ref Vim, 255); for (int h = 0; h < VectorWidth; h++) { output[index + h] = outputVector[h] < 255 ? (byte)outputVector[h] : (byte)255; } }); } return output; Vector GetByte(ref Vector Cre, ref Vector Cim, int max_iterations) { Vector Limit = new Vector(4); Vector MaxIterations = new Vector(max_iterations); Vector Zre = Cre; //make a copy Vector Zim = Cim; //make a copy Vector Increment = One; Vector I; for (I = Zero; Increment != Zero; I += Vector.Abs(Increment)) { Vector S = SquareAbs(Zre, Zim); Increment = Vector.LessThanOrEqual(S, Limit) & Vector.LessThan(I, MaxIterations); if (Increment == Zero) { break; } else { Vector Tre = Zre; Vector Tim = Zim; Zre = Cre + (Tre * Tre - Tim * Tim); Zim = Cim + 2f * Tre * Tim; } } return I; } Vector SquareAbs(Vector Vre, Vector Vim) { return (Vre * Vre) + (Vim * Vim); } IEnumerable> MandelbrotBitmapLocation(int j) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { yield return (i, j); } } } private unsafe int[] _MandelbrotManagedv5(ref int[] output) { Vector One = Vector.One; Vector Zero = Vector.Zero; Vector Limit = new Vector(4); float[] Vectors = new float[6]; float[] P = new float[VectorWidth * 2]; Span Vector2Span = new Span(Vectors).NonPortableCast(); //Lets us read individual Vector2 Span> PSpan = new Span(P).NonPortableCast>(); //Lets us read individual Vectors Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i += VectorWidth) { for (int h = 0; h < VectorWidth; h++) { P[h] = C0.X + (D.X * (i + h)); P[h + VectorWidth] = C0.Y + (D.Y * j); } index = unchecked(j * Mandelbrot_Width + i); Vector Vre = PSpan[0]; Vector Vim = PSpan[1]; ; Vector outputVector = GetByte(ref Vre, ref Vim, 256); outputVector.CopyTo(output, index); } } return output; Vector GetByte(ref Vector Cre, ref Vector Cim, int max_iterations) { Vector Zre = Cre; //make a copy Vector Zim = Cim; //make a copy Vector Increment = One; Vector MaxIterations = new Vector(max_iterations); Vector I; for (I = Zero; Increment != Zero; I += Vector.Abs(Increment)) { Vector S = SquareAbs(Zre, Zim); Increment = Vector.LessThanOrEqual(S, Limit) & Vector.LessThan(I, MaxIterations); if (Increment == Zero) { break; } else { Vector Tre = Zre; Vector Tim = Zim; Zre = Cre + (Tre * Tre - Tim * Tim); Zim = Cim + 2f * Tre * Tim; } } return I; } Vector SquareAbs(Vector Vre, Vector Vim) { return (Vre * Vre) + (Vim * Vim); } } private FixedBuffer _MandelbrotUnmanagedv1(ref FixedBuffer output) { FixedBuffer Vectors = new FixedBuffer(10); Span Vector2Span = Vectors.AcquireWriteSpan().NonPortableCast(); //Lets us read individual vectors Vectors[0] = -2f; Vectors[1] = -1f; Vectors[2] = 1f; Vectors[3] = 1f; Vectors[4] = Mandelbrot_Width; Vectors[5] = Mandelbrot_Height; ref Vector2 C0 = ref Vector2Span[0]; ref Vector2 C1 = ref Vector2Span[1]; ref Vector2 B = ref Vector2Span[2]; ref Vector2 P = ref Vector2Span[3]; Vector2 D = (C1 - C0) / B; int index; for (int j = 0; j < Mandelbrot_Height; j++) { for (int i = 0; i < Mandelbrot_Width; i++) { Vectors[6] = i; Vectors[7] = j; index = unchecked(j * Mandelbrot_Width + i); Vector2 V = C0 + (P * D); output[index] = GetByte(ref V, 256); } } Vectors.Release(); return output; byte GetByte(ref Vector2 c, int max_iterations) { Vector2 z = c; //make a copy int i; for (i = 0; i < max_iterations; i++) { if (z.LengthSquared() > 4f) { return (byte)i; } Vector2 w = z * z; z = c + new Vector2(w.X - w.Y, 2f * z.X * z.Y); } return (byte)(i - 1); } } #endregion #endregion } } ================================================ FILE: jemalloc.Tests/jemalloc.Tests.csproj ================================================ netcoreapp2.0 false AnyCPU;x64 ..\x64\Debug\ x64 ..\x64\Debug\ x64 true ..\x64\Release\ ================================================ FILE: jemalloc.Tests/jemallocTest.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using Xunit; namespace jemalloc.Tests { public abstract class jemallocTest { public static Random Rng = new Random(); public Process CurrentProcess { get; protected set; } = Process.GetCurrentProcess(); public jemallocTest() { Jem.Init("dirty_decay_ms:1,muzzy_decay_ms:1,tcache:false,narenas:3"); init_privateMemorySize = CurrentProcess.PrivateMemorySize64; init_peakPagedMem = CurrentProcess.PeakPagedMemorySize64; init_peakWorkingSet = CurrentProcess.PeakWorkingSet64; init_peakVirtualMem = CurrentProcess.PeakVirtualMemorySize64; init_allocated = Jem.AllocatedBytes; } #region Fields long init_privateMemorySize = 0; long init_peakPagedMem = 0; long init_peakWorkingSet = 0; long init_peakVirtualMem = 0; ulong init_allocated; #endregion } } ================================================ FILE: jembench.cmd ================================================ @echo off dotnet .\x64\Release\netcoreapp2.0\jemalloc.Cli.dll %* :end