diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs deleted file mode 100644 index 5200a2793c..0000000000 --- a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Memory.Internals; - -namespace SixLabors.ImageSharp.Memory -{ - /// - /// Contains . - /// - public partial class ArrayPoolMemoryAllocator - { - /// - /// The buffer implementation of . - /// - private class Buffer : ManagedBufferBase - where T : struct - { - /// - /// The length of the buffer. - /// - private readonly int length; - - /// - /// A weak reference to the source pool. - /// - /// - /// By using a weak reference here, we are making sure that array pools and their retained arrays are always GC-ed - /// after a call to , regardless of having buffer instances still being in use. - /// - private WeakReference> sourcePoolReference; - - public Buffer(byte[] data, int length, ArrayPool sourcePool) - { - this.Data = data; - this.length = length; - this.sourcePoolReference = new WeakReference>(sourcePool); - } - - /// - /// Gets the buffer as a byte array. - /// - protected byte[] Data { get; private set; } - - /// - public override Span GetSpan() - { - if (this.Data is null) - { - ThrowObjectDisposedException(); - } -#if SUPPORTS_CREATESPAN - ref byte r0 = ref MemoryMarshal.GetReference(this.Data); - return MemoryMarshal.CreateSpan(ref Unsafe.As(ref r0), this.length); -#else - return MemoryMarshal.Cast(this.Data.AsSpan()).Slice(0, this.length); -#endif - - } - - /// - protected override void Dispose(bool disposing) - { - if (!disposing || this.Data is null || this.sourcePoolReference is null) - { - return; - } - - if (this.sourcePoolReference.TryGetTarget(out ArrayPool pool)) - { - pool.Return(this.Data); - } - - this.sourcePoolReference = null; - this.Data = null; - } - - protected override object GetPinnableObject() => this.Data; - - [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowObjectDisposedException() - { - throw new ObjectDisposedException("ArrayPoolMemoryAllocator.Buffer"); - } - } - } -} diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs deleted file mode 100644 index 8aa1b90634..0000000000 --- a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Memory -{ - /// - /// Contains common factory methods and configuration constants. - /// - public partial class ArrayPoolMemoryAllocator - { - /// - /// The default value for: maximum size of pooled arrays in bytes. - /// Currently set to 24MB, which is equivalent to 8 megapixels of raw RGBA32 data. - /// - internal const int DefaultMaxPooledBufferSizeInBytes = 24 * 1024 * 1024; - - /// - /// The value for: The threshold to pool arrays in which has less buckets for memory safety. - /// - private const int DefaultBufferSelectorThresholdInBytes = 8 * 1024 * 1024; - - /// - /// The default bucket count for . - /// - private const int DefaultLargePoolBucketCount = 6; - - /// - /// The default bucket count for . - /// - private const int DefaultNormalPoolBucketCount = 16; - - // TODO: This value should be determined by benchmarking - private const int DefaultBufferCapacityInBytes = int.MaxValue / 4; - - /// - /// This is the default. Should be good for most use cases. - /// - /// The memory manager. - public static ArrayPoolMemoryAllocator CreateDefault() - { - return new ArrayPoolMemoryAllocator( - DefaultMaxPooledBufferSizeInBytes, - DefaultBufferSelectorThresholdInBytes, - DefaultLargePoolBucketCount, - DefaultNormalPoolBucketCount, - DefaultBufferCapacityInBytes); - } - - /// - /// For environments with very limited memory capabilities, only small buffers like image rows are pooled. - /// - /// The memory manager. - public static ArrayPoolMemoryAllocator CreateWithMinimalPooling() - { - return new ArrayPoolMemoryAllocator(64 * 1024, 32 * 1024, 8, 24); - } - - /// - /// For environments with limited memory capabilities, only small array requests are pooled, which can result in reduced throughput. - /// - /// The memory manager. - public static ArrayPoolMemoryAllocator CreateWithModeratePooling() - { - return new ArrayPoolMemoryAllocator(1024 * 1024, 32 * 1024, 16, 24); - } - - /// - /// For environments where memory capabilities are not an issue, the maximum amount of array requests are pooled which results in optimal throughput. - /// - /// The memory manager. - public static ArrayPoolMemoryAllocator CreateWithAggressivePooling() - { - return new ArrayPoolMemoryAllocator(128 * 1024 * 1024, 32 * 1024 * 1024, 16, 32); - } - } -} diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs deleted file mode 100644 index eb34b813fe..0000000000 --- a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Memory -{ - /// - /// Implements by allocating memory from . - /// - [Obsolete("ArrayPoolMemoryAllocator is obsolete. Use MemoryAllocator.CreateDefault() instead.")] - public sealed partial class ArrayPoolMemoryAllocator : MemoryAllocator - { - private readonly int maxArraysPerBucketNormalPool; - - private readonly int maxArraysPerBucketLargePool; - - /// - /// The for small-to-medium buffers which is not kept clean. - /// - private ArrayPool normalArrayPool; - - /// - /// The for huge buffers, which is not kept clean. - /// - private ArrayPool largeArrayPool; - - /// - /// Initializes a new instance of the class. - /// - public ArrayPoolMemoryAllocator() - : this(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. - public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes) - : this(maxPoolSizeInBytes, GetLargeBufferThresholdInBytes(maxPoolSizeInBytes)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. - /// Arrays over this threshold will be pooled in which has less buckets for memory safety. - public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes) - : this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, DefaultLargePoolBucketCount, DefaultNormalPoolBucketCount) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. - /// The threshold to pool arrays in which has less buckets for memory safety. - /// Max arrays per bucket for the large array pool. - /// Max arrays per bucket for the normal array pool. - public ArrayPoolMemoryAllocator( - int maxPoolSizeInBytes, - int poolSelectorThresholdInBytes, - int maxArraysPerBucketLargePool, - int maxArraysPerBucketNormalPool) - : this( - maxPoolSizeInBytes, - poolSelectorThresholdInBytes, - maxArraysPerBucketLargePool, - maxArraysPerBucketNormalPool, - DefaultBufferCapacityInBytes) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. - /// The threshold to pool arrays in which has less buckets for memory safety. - /// Max arrays per bucket for the large array pool. - /// Max arrays per bucket for the normal array pool. - /// The length of the largest contiguous buffer that can be handled by this allocator instance. - public ArrayPoolMemoryAllocator( - int maxPoolSizeInBytes, - int poolSelectorThresholdInBytes, - int maxArraysPerBucketLargePool, - int maxArraysPerBucketNormalPool, - int bufferCapacityInBytes) - { - Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); - Guard.MustBeLessThanOrEqualTo(poolSelectorThresholdInBytes, maxPoolSizeInBytes, nameof(poolSelectorThresholdInBytes)); - - this.MaxPoolSizeInBytes = maxPoolSizeInBytes; - this.PoolSelectorThresholdInBytes = poolSelectorThresholdInBytes; - this.BufferCapacityInBytes = bufferCapacityInBytes; - this.maxArraysPerBucketLargePool = maxArraysPerBucketLargePool; - this.maxArraysPerBucketNormalPool = maxArraysPerBucketNormalPool; - - this.InitArrayPools(); - } - - /// - /// Gets the maximum size of pooled arrays in bytes. - /// - public int MaxPoolSizeInBytes { get; } - - /// - /// Gets the threshold to pool arrays in which has less buckets for memory safety. - /// - public int PoolSelectorThresholdInBytes { get; } - - /// - /// Gets the length of the largest contiguous buffer that can be handled by this allocator instance. - /// - public int BufferCapacityInBytes { get; internal set; } // Setter is internal for easy configuration in tests - - /// - public override void ReleaseRetainedResources() - { - this.InitArrayPools(); - } - - /// - protected internal override int GetBufferCapacityInBytes() => this.BufferCapacityInBytes; - - /// - public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) - { - Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); - int itemSizeBytes = Unsafe.SizeOf(); - int bufferSizeInBytes = length * itemSizeBytes; - - ArrayPool pool = this.GetArrayPool(bufferSizeInBytes); - byte[] byteArray = pool.Rent(bufferSizeInBytes); - - var buffer = new Buffer(byteArray, length, pool); - if (options.Has(AllocationOptions.Clean)) - { - buffer.GetSpan().Clear(); - } - - return buffer; - } - - private static int GetLargeBufferThresholdInBytes(int maxPoolSizeInBytes) => maxPoolSizeInBytes / 4; - - [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowInvalidAllocationException(int length, int max) => - throw new InvalidMemoryOperationException( - $"Requested allocation: '{length}' elements of '{typeof(T).Name}' is over the capacity in bytes '{max}' of the MemoryAllocator."); - - private ArrayPool GetArrayPool(int bufferSizeInBytes) - { - return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; - } - - private void InitArrayPools() - { - this.largeArrayPool = ArrayPool.Create(this.MaxPoolSizeInBytes, this.maxArraysPerBucketLargePool); - this.normalArrayPool = ArrayPool.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool); - } - } -} diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs index 9d6804e64b..b998863e87 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs @@ -17,11 +17,6 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave // private const JpegKind Filter = JpegKind.Progressive; private const JpegKind Filter = JpegKind.Any; -#pragma warning disable CS0618 // 'ArrayPoolMemoryAllocator' is obsolete - private ArrayPoolMemoryAllocator arrayPoolMemoryAllocator; -#pragma warning restore CS0618 - private MemoryAllocator defaultMemoryAllocator; - [GlobalSetup] public void Setup() { @@ -32,11 +27,6 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave }; Console.WriteLine($"ImageCount: {this.runner.ImageCount} Filter: {Filter}"); this.runner.Init(); - this.defaultMemoryAllocator = Configuration.Default.MemoryAllocator; - -#pragma warning disable CS0618 // 'ArrayPoolMemoryAllocator' is obsolete - this.arrayPoolMemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault(); -#pragma warning restore CS0618 } private void ForEachImage(Action action, int maxDegreeOfParallelism) @@ -59,17 +49,8 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave [Benchmark(Baseline = true)] [ArgumentsSource(nameof(ParallelismValues))] - public void ImageSharp_DefaultMemoryAllocator(int maxDegreeOfParallelism) - { - Configuration.Default.MemoryAllocator = this.defaultMemoryAllocator; - this.ForEachImage(this.runner.ImageSharpResize, maxDegreeOfParallelism); - } - - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void ImageSharp_ArrayPoolMemoryAllocator(int maxDegreeOfParallelism) + public void ImageSharp(int maxDegreeOfParallelism) { - Configuration.Default.MemoryAllocator = this.arrayPoolMemoryAllocator; this.ForEachImage(this.runner.ImageSharpResize, maxDegreeOfParallelism); } diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs index 873845393d..acec87ca58 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs @@ -192,20 +192,11 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox } } - private enum AllocatorKind - { - Classic, - Unmanaged - } - private class CommandLineOptions { [Option('i', "imagesharp", Required = false, Default = false, HelpText = "Test ImageSharp without benchmark switching")] public bool ImageSharp { get; set; } - [Option('a', "allocator", Required = false, Default = AllocatorKind.Unmanaged, HelpText = "Select allocator: Classic (ArrayPoolMemoryAllocator) or Unmanaged")] - public AllocatorKind Allocator { get; set; } - [Option('m', "max-contiguous", Required = false, Default = 4, HelpText = "Maximum size of contiguous pool buffers in MegaBytes")] public int MaxContiguousPoolBufferMegaBytes { get; set; } = 4; @@ -253,40 +244,29 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox } public override string ToString() => - $"p({this.MaxDegreeOfParallelism})_i({this.ImageSharp})_a({this.Allocator})_m({this.MaxContiguousPoolBufferMegaBytes})_s({this.MaxPoolSizeMegaBytes})_u({this.MaxCapacityOfNonPoolBuffersMegaBytes})_r({this.RepeatCount})_g({this.FinalGcCount})_e({this.ReleaseRetainedResourcesAtEnd})"; + $"p({this.MaxDegreeOfParallelism})_i({this.ImageSharp})_m({this.MaxContiguousPoolBufferMegaBytes})_s({this.MaxPoolSizeMegaBytes})_u({this.MaxCapacityOfNonPoolBuffersMegaBytes})_r({this.RepeatCount})_g({this.FinalGcCount})_e({this.ReleaseRetainedResourcesAtEnd})"; public MemoryAllocator CreateMemoryAllocator() { - switch (this.Allocator) + if (this.TrimTimeSeconds.HasValue) { - case AllocatorKind.Classic: -#pragma warning disable CS0618 // 'ArrayPoolMemoryAllocator' is obsolete - return ArrayPoolMemoryAllocator.CreateDefault(); -#pragma warning restore CS0618 - case AllocatorKind.Unmanaged: - if (this.TrimTimeSeconds.HasValue) + return new UniformUnmanagedMemoryPoolMemoryAllocator( + 1024 * 1024, + (int)B(this.MaxContiguousPoolBufferMegaBytes), + B(this.MaxPoolSizeMegaBytes), + (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes), + new UniformUnmanagedMemoryPool.TrimSettings { - return new UniformUnmanagedMemoryPoolMemoryAllocator( - 1024 * 1024, - (int)B(this.MaxContiguousPoolBufferMegaBytes), - B(this.MaxPoolSizeMegaBytes), - (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes), - new UniformUnmanagedMemoryPool.TrimSettings - { - TrimPeriodMilliseconds = this.TrimTimeSeconds.Value * 1000 - }); - } - else - { - return new UniformUnmanagedMemoryPoolMemoryAllocator( - 1024 * 1024, - (int)B(this.MaxContiguousPoolBufferMegaBytes), - B(this.MaxPoolSizeMegaBytes), - (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes)); - } - - default: - throw new ArgumentOutOfRangeException(); + TrimPeriodMilliseconds = this.TrimTimeSeconds.Value * 1000 + }); + } + else + { + return new UniformUnmanagedMemoryPoolMemoryAllocator( + 1024 * 1024, + (int)B(this.MaxContiguousPoolBufferMegaBytes), + B(this.MaxPoolSizeMegaBytes), + (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes)); } }