mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 19 additions and 391 deletions
@ -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 |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains <see cref="Buffer{T}"/>.
|
|
||||
/// </summary>
|
|
||||
public partial class ArrayPoolMemoryAllocator |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The buffer implementation of <see cref="ArrayPoolMemoryAllocator"/>.
|
|
||||
/// </summary>
|
|
||||
private class Buffer<T> : ManagedBufferBase<T> |
|
||||
where T : struct |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The length of the buffer.
|
|
||||
/// </summary>
|
|
||||
private readonly int length; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A weak reference to the source pool.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// 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 <see cref="ArrayPoolMemoryAllocator.ReleaseRetainedResources"/>, regardless of having buffer instances still being in use.
|
|
||||
/// </remarks>
|
|
||||
private WeakReference<ArrayPool<byte>> sourcePoolReference; |
|
||||
|
|
||||
public Buffer(byte[] data, int length, ArrayPool<byte> sourcePool) |
|
||||
{ |
|
||||
this.Data = data; |
|
||||
this.length = length; |
|
||||
this.sourcePoolReference = new WeakReference<ArrayPool<byte>>(sourcePool); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the buffer as a byte array.
|
|
||||
/// </summary>
|
|
||||
protected byte[] Data { get; private set; } |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public override Span<T> GetSpan() |
|
||||
{ |
|
||||
if (this.Data is null) |
|
||||
{ |
|
||||
ThrowObjectDisposedException(); |
|
||||
} |
|
||||
#if SUPPORTS_CREATESPAN
|
|
||||
ref byte r0 = ref MemoryMarshal.GetReference<byte>(this.Data); |
|
||||
return MemoryMarshal.CreateSpan(ref Unsafe.As<byte, T>(ref r0), this.length); |
|
||||
#else
|
|
||||
return MemoryMarshal.Cast<byte, T>(this.Data.AsSpan()).Slice(0, this.length); |
|
||||
#endif
|
|
||||
|
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
protected override void Dispose(bool disposing) |
|
||||
{ |
|
||||
if (!disposing || this.Data is null || this.sourcePoolReference is null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (this.sourcePoolReference.TryGetTarget(out ArrayPool<byte> 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<T>"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,76 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Memory |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains common factory methods and configuration constants.
|
|
||||
/// </summary>
|
|
||||
public partial class ArrayPoolMemoryAllocator |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 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.
|
|
||||
/// </summary>
|
|
||||
internal const int DefaultMaxPooledBufferSizeInBytes = 24 * 1024 * 1024; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The value for: The threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety.
|
|
||||
/// </summary>
|
|
||||
private const int DefaultBufferSelectorThresholdInBytes = 8 * 1024 * 1024; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The default bucket count for <see cref="largeArrayPool"/>.
|
|
||||
/// </summary>
|
|
||||
private const int DefaultLargePoolBucketCount = 6; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The default bucket count for <see cref="normalArrayPool"/>.
|
|
||||
/// </summary>
|
|
||||
private const int DefaultNormalPoolBucketCount = 16; |
|
||||
|
|
||||
// TODO: This value should be determined by benchmarking
|
|
||||
private const int DefaultBufferCapacityInBytes = int.MaxValue / 4; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This is the default. Should be good for most use cases.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The memory manager.</returns>
|
|
||||
public static ArrayPoolMemoryAllocator CreateDefault() |
|
||||
{ |
|
||||
return new ArrayPoolMemoryAllocator( |
|
||||
DefaultMaxPooledBufferSizeInBytes, |
|
||||
DefaultBufferSelectorThresholdInBytes, |
|
||||
DefaultLargePoolBucketCount, |
|
||||
DefaultNormalPoolBucketCount, |
|
||||
DefaultBufferCapacityInBytes); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// For environments with very limited memory capabilities, only small buffers like image rows are pooled.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The memory manager.</returns>
|
|
||||
public static ArrayPoolMemoryAllocator CreateWithMinimalPooling() |
|
||||
{ |
|
||||
return new ArrayPoolMemoryAllocator(64 * 1024, 32 * 1024, 8, 24); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// For environments with limited memory capabilities, only small array requests are pooled, which can result in reduced throughput.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The memory manager.</returns>
|
|
||||
public static ArrayPoolMemoryAllocator CreateWithModeratePooling() |
|
||||
{ |
|
||||
return new ArrayPoolMemoryAllocator(1024 * 1024, 32 * 1024, 16, 24); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// For environments where memory capabilities are not an issue, the maximum amount of array requests are pooled which results in optimal throughput.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The memory manager.</returns>
|
|
||||
public static ArrayPoolMemoryAllocator CreateWithAggressivePooling() |
|
||||
{ |
|
||||
return new ArrayPoolMemoryAllocator(128 * 1024 * 1024, 32 * 1024 * 1024, 16, 32); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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 |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Implements <see cref="MemoryAllocator"/> by allocating memory from <see cref="ArrayPool{T}"/>.
|
|
||||
/// </summary>
|
|
||||
[Obsolete("ArrayPoolMemoryAllocator is obsolete. Use MemoryAllocator.CreateDefault() instead.")] |
|
||||
public sealed partial class ArrayPoolMemoryAllocator : MemoryAllocator |
|
||||
{ |
|
||||
private readonly int maxArraysPerBucketNormalPool; |
|
||||
|
|
||||
private readonly int maxArraysPerBucketLargePool; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The <see cref="ArrayPool{T}"/> for small-to-medium buffers which is not kept clean.
|
|
||||
/// </summary>
|
|
||||
private ArrayPool<byte> normalArrayPool; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The <see cref="ArrayPool{T}"/> for huge buffers, which is not kept clean.
|
|
||||
/// </summary>
|
|
||||
private ArrayPool<byte> largeArrayPool; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ArrayPoolMemoryAllocator"/> class.
|
|
||||
/// </summary>
|
|
||||
public ArrayPoolMemoryAllocator() |
|
||||
: this(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ArrayPoolMemoryAllocator"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="maxPoolSizeInBytes">The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated.</param>
|
|
||||
public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes) |
|
||||
: this(maxPoolSizeInBytes, GetLargeBufferThresholdInBytes(maxPoolSizeInBytes)) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ArrayPoolMemoryAllocator"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="maxPoolSizeInBytes">The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated.</param>
|
|
||||
/// <param name="poolSelectorThresholdInBytes">Arrays over this threshold will be pooled in <see cref="largeArrayPool"/> which has less buckets for memory safety.</param>
|
|
||||
public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes) |
|
||||
: this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, DefaultLargePoolBucketCount, DefaultNormalPoolBucketCount) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ArrayPoolMemoryAllocator"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="maxPoolSizeInBytes">The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated.</param>
|
|
||||
/// <param name="poolSelectorThresholdInBytes">The threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety.</param>
|
|
||||
/// <param name="maxArraysPerBucketLargePool">Max arrays per bucket for the large array pool.</param>
|
|
||||
/// <param name="maxArraysPerBucketNormalPool">Max arrays per bucket for the normal array pool.</param>
|
|
||||
public ArrayPoolMemoryAllocator( |
|
||||
int maxPoolSizeInBytes, |
|
||||
int poolSelectorThresholdInBytes, |
|
||||
int maxArraysPerBucketLargePool, |
|
||||
int maxArraysPerBucketNormalPool) |
|
||||
: this( |
|
||||
maxPoolSizeInBytes, |
|
||||
poolSelectorThresholdInBytes, |
|
||||
maxArraysPerBucketLargePool, |
|
||||
maxArraysPerBucketNormalPool, |
|
||||
DefaultBufferCapacityInBytes) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ArrayPoolMemoryAllocator"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="maxPoolSizeInBytes">The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated.</param>
|
|
||||
/// <param name="poolSelectorThresholdInBytes">The threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety.</param>
|
|
||||
/// <param name="maxArraysPerBucketLargePool">Max arrays per bucket for the large array pool.</param>
|
|
||||
/// <param name="maxArraysPerBucketNormalPool">Max arrays per bucket for the normal array pool.</param>
|
|
||||
/// <param name="bufferCapacityInBytes">The length of the largest contiguous buffer that can be handled by this allocator instance.</param>
|
|
||||
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(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum size of pooled arrays in bytes.
|
|
||||
/// </summary>
|
|
||||
public int MaxPoolSizeInBytes { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety.
|
|
||||
/// </summary>
|
|
||||
public int PoolSelectorThresholdInBytes { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the length of the largest contiguous buffer that can be handled by this allocator instance.
|
|
||||
/// </summary>
|
|
||||
public int BufferCapacityInBytes { get; internal set; } // Setter is internal for easy configuration in tests
|
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public override void ReleaseRetainedResources() |
|
||||
{ |
|
||||
this.InitArrayPools(); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
protected internal override int GetBufferCapacityInBytes() => this.BufferCapacityInBytes; |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None) |
|
||||
{ |
|
||||
Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); |
|
||||
int itemSizeBytes = Unsafe.SizeOf<T>(); |
|
||||
int bufferSizeInBytes = length * itemSizeBytes; |
|
||||
|
|
||||
ArrayPool<byte> pool = this.GetArrayPool(bufferSizeInBytes); |
|
||||
byte[] byteArray = pool.Rent(bufferSizeInBytes); |
|
||||
|
|
||||
var buffer = new Buffer<T>(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<T>(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<byte> GetArrayPool(int bufferSizeInBytes) |
|
||||
{ |
|
||||
return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; |
|
||||
} |
|
||||
|
|
||||
private void InitArrayPools() |
|
||||
{ |
|
||||
this.largeArrayPool = ArrayPool<byte>.Create(this.MaxPoolSizeInBytes, this.maxArraysPerBucketLargePool); |
|
||||
this.normalArrayPool = ArrayPool<byte>.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue