Browse Source

allowing bucket sizes to be passed to ArrayPoolMemoryManager

af/merge-core
Anton Firszov 8 years ago
parent
commit
16d5f36e72
  1. 46
      src/ImageSharp/Memory/ArrayPoolMemoryManager.cs
  2. 16
      tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs

46
src/ImageSharp/Memory/ArrayPoolMemoryManager.cs

@ -1,15 +1,11 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Memory namespace SixLabors.ImageSharp.Memory
{ {
using Guard = SixLabors.Guard;
/// <summary> /// <summary>
/// Implements <see cref="MemoryManager"/> by allocating memory from <see cref="ArrayPool{T}"/>. /// Implements <see cref="MemoryManager"/> by allocating memory from <see cref="ArrayPool{T}"/>.
/// </summary> /// </summary>
@ -27,14 +23,18 @@ namespace SixLabors.ImageSharp.Memory
private const int DefaultLargeBufferThresholdInBytes = 8 * 1024 * 1024; private const int DefaultLargeBufferThresholdInBytes = 8 * 1024 * 1024;
/// <summary> /// <summary>
/// The <see cref="ArrayPool{T}"/> for huge buffers, which is not kept clean. /// The <see cref="ArrayPool{T}"/> for small-to-medium buffers which is not kept clean.
/// </summary> /// </summary>
private ArrayPool<byte> largeArrayPool; private ArrayPool<byte> normalArrayPool;
/// <summary> /// <summary>
/// The <see cref="ArrayPool{T}"/> for small-to-medium buffers which is not kept clean. /// The <see cref="ArrayPool{T}"/> for huge buffers, which is not kept clean.
/// </summary> /// </summary>
private ArrayPool<byte> normalArrayPool; private ArrayPool<byte> largeArrayPool;
private readonly int maxArraysPerBucketNormalPool;
private readonly int maxArraysPerBucketLargePool;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArrayPoolMemoryManager"/> class. /// Initializes a new instance of the <see cref="ArrayPoolMemoryManager"/> class.
@ -57,14 +57,28 @@ namespace SixLabors.ImageSharp.Memory
/// Initializes a new instance of the <see cref="ArrayPoolMemoryManager"/> class. /// Initializes a new instance of the <see cref="ArrayPoolMemoryManager"/> class.
/// </summary> /// </summary>
/// <param name="maxPoolSizeInBytes">The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated.</param> /// <param name="maxPoolSizeInBytes">The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated.</param>
/// <param name="largeBufferThresholdInBytes">The threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety.</param> /// <param name="poolSelectorThresholdInBytes">Arrays over this threshold will be pooled in <see cref="largeArrayPool"/> which has less buckets for memory safety.</param>
public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int largeBufferThresholdInBytes) public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes)
: this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, 8, 24)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ArrayPoolMemoryManager"/> 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 ArrayPoolMemoryManager(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes, int maxArraysPerBucketLargePool, int maxArraysPerBucketNormalPool)
{ {
Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes));
Guard.MustBeLessThanOrEqualTo(largeBufferThresholdInBytes, maxPoolSizeInBytes, nameof(largeBufferThresholdInBytes)); Guard.MustBeLessThanOrEqualTo(poolSelectorThresholdInBytes, maxPoolSizeInBytes, nameof(poolSelectorThresholdInBytes));
this.MaxPoolSizeInBytes = maxPoolSizeInBytes; this.MaxPoolSizeInBytes = maxPoolSizeInBytes;
this.LargeBufferThresholdInBytes = largeBufferThresholdInBytes; this.PoolSelectorThresholdInBytes = poolSelectorThresholdInBytes;
this.maxArraysPerBucketLargePool = maxArraysPerBucketLargePool;
this.maxArraysPerBucketNormalPool = maxArraysPerBucketNormalPool;
this.InitArrayPools(); this.InitArrayPools();
} }
@ -77,7 +91,7 @@ namespace SixLabors.ImageSharp.Memory
/// <summary> /// <summary>
/// Gets the threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety. /// Gets the threshold to pool arrays in <see cref="largeArrayPool"/> which has less buckets for memory safety.
/// </summary> /// </summary>
public int LargeBufferThresholdInBytes { get; } public int PoolSelectorThresholdInBytes { get; }
/// <inheritdoc /> /// <inheritdoc />
public override void ReleaseRetainedResources() public override void ReleaseRetainedResources()
@ -124,13 +138,13 @@ namespace SixLabors.ImageSharp.Memory
private ArrayPool<byte> GetArrayPool(int bufferSizeInBytes) private ArrayPool<byte> GetArrayPool(int bufferSizeInBytes)
{ {
return bufferSizeInBytes <= this.LargeBufferThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool;
} }
private void InitArrayPools() private void InitArrayPools()
{ {
this.largeArrayPool = ArrayPool<byte>.Create(this.MaxPoolSizeInBytes, 8); this.largeArrayPool = ArrayPool<byte>.Create(this.MaxPoolSizeInBytes, this.maxArraysPerBucketLargePool);
this.normalArrayPool = ArrayPool<byte>.Create(this.LargeBufferThresholdInBytes, 24); this.normalArrayPool = ArrayPool<byte>.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool);
} }
} }
} }

16
tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs

@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Tests.Memory
{ {
private const int MaxPooledBufferSizeInBytes = 2048; private const int MaxPooledBufferSizeInBytes = 2048;
private const int LargeBufferThresholdInBytes = MaxPooledBufferSizeInBytes / 2; private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2;
private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, LargeBufferThresholdInBytes); private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes);
/// <summary> /// <summary>
/// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location
@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
public class BufferTests : BufferTestSuite public class BufferTests : BufferTestSuite
{ {
public BufferTests() public BufferTests()
: base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, LargeBufferThresholdInBytes)) : base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes))
{ {
} }
} }
@ -53,19 +53,19 @@ namespace SixLabors.ImageSharp.Tests.Memory
{ {
var mgr = new ArrayPoolMemoryManager(1111, 666); var mgr = new ArrayPoolMemoryManager(1111, 666);
Assert.Equal(1111, mgr.MaxPoolSizeInBytes); Assert.Equal(1111, mgr.MaxPoolSizeInBytes);
Assert.Equal(666, mgr.LargeBufferThresholdInBytes); Assert.Equal(666, mgr.PoolSelectorThresholdInBytes);
} }
[Fact] [Fact]
public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdIsAutoCalculated() public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdValueIsAutoCalculated()
{ {
var mgr = new ArrayPoolMemoryManager(5000); var mgr = new ArrayPoolMemoryManager(5000);
Assert.Equal(5000, mgr.MaxPoolSizeInBytes); Assert.Equal(5000, mgr.MaxPoolSizeInBytes);
Assert.True(mgr.LargeBufferThresholdInBytes < mgr.MaxPoolSizeInBytes); Assert.True(mgr.PoolSelectorThresholdInBytes < mgr.MaxPoolSizeInBytes);
} }
[Fact] [Fact]
public void When_LargeBufferThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_Throws() public void When_PoolSelectorThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_ExceptionIsThrown()
{ {
Assert.ThrowsAny<Exception>(() => { new ArrayPoolMemoryManager(100, 200); }); Assert.ThrowsAny<Exception>(() => { new ArrayPoolMemoryManager(100, 200); });
} }
@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
[Fact] [Fact]
public void AllocationOverLargeArrayThreshold_UsesDifferentPool() public void AllocationOverLargeArrayThreshold_UsesDifferentPool()
{ {
int arrayLengthThreshold = LargeBufferThresholdInBytes / sizeof(int); int arrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int);
IBuffer<int> small = this.MemoryManager.Allocate<int>(arrayLengthThreshold - 1); IBuffer<int> small = this.MemoryManager.Allocate<int>(arrayLengthThreshold - 1);
ref int ptr2Small = ref small.DangerousGetPinnableReference(); ref int ptr2Small = ref small.DangerousGetPinnableReference();

Loading…
Cancel
Save