From ca17d33ea3d1b6cbd3491d28440b2e09215ab728 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 7 Aug 2021 23:17:40 +0200 Subject: [PATCH] use WeakReference with timer, configure trim period for sandbox --- .../Internals/UniformUnmanagedMemoryPool.cs | 15 ++++++++++--- ...iformUnmanagedMemoryPoolMemoryAllocator.cs | 22 ++++++++++++++++--- .../LoadResizeSaveParallelMemoryStress.cs | 10 ++++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs index c1a2d90bd5..c3e41cf7e3 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs @@ -34,9 +34,10 @@ namespace SixLabors.ImageSharp.Memory.Internals // Invoke the timer callback more frequently, than trimSettings.TrimPeriodMilliseconds, // and also invoke it on Gen 2 GC. // We are checking in the callback if enough time passed since the last trimming. If not, we do nothing. + var weakPoolRef = new WeakReference(this); this.trimTimer = new Timer( - s => ((UniformUnmanagedMemoryPool)s)?.Trim(), - this, + s => TimerCallback((WeakReference)s), + weakPoolRef, this.trimSettings.TrimPeriodMilliseconds / 4, this.trimSettings.TrimPeriodMilliseconds / 4); @@ -217,6 +218,14 @@ namespace SixLabors.ImageSharp.Memory.Internals private static void ThrowReturnedMoreArraysThanRented() => throw new InvalidMemoryOperationException("Returned more arrays then rented"); + private static void TimerCallback(WeakReference weakPoolRef) + { + if (weakPoolRef.TryGetTarget(out UniformUnmanagedMemoryPool pool)) + { + pool.Trim(); + } + } + private bool Trim() { UnmanagedMemoryHandle[] buffersLocal = this.buffers; @@ -307,7 +316,7 @@ namespace SixLabors.ImageSharp.Memory.Internals public class TrimSettings { // Trim half of the retained pool buffers every minute. - public int TrimPeriodMilliseconds { get; set; } = 60_000; + public int TrimPeriodMilliseconds { get; set; } = 20_000; public float Rate { get; set; } = 0.5f; diff --git a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs index 51c20519d6..6ea69c8926 100644 --- a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs @@ -18,6 +18,7 @@ namespace SixLabors.ImageSharp.Memory private readonly int sharedArrayPoolThresholdInBytes; private readonly int poolBufferSizeInBytes; private readonly int poolCapacity; + private readonly UniformUnmanagedMemoryPool.TrimSettings trimSettings; private UniformUnmanagedMemoryPool pool; private readonly UnmanagedMemoryAllocator nonPoolAllocator; @@ -42,17 +43,32 @@ namespace SixLabors.ImageSharp.Memory { } - // Internal constructor allowing to change the shared array pool threshold for testing purposes. internal UniformUnmanagedMemoryPoolMemoryAllocator( int sharedArrayPoolThresholdInBytes, int poolBufferSizeInBytes, long maxPoolSizeInBytes, int unmanagedBufferSizeInBytes) + : this( + sharedArrayPoolThresholdInBytes, + poolBufferSizeInBytes, + maxPoolSizeInBytes, + unmanagedBufferSizeInBytes, + UniformUnmanagedMemoryPool.TrimSettings.Default) + { + } + + internal UniformUnmanagedMemoryPoolMemoryAllocator( + int sharedArrayPoolThresholdInBytes, + int poolBufferSizeInBytes, + long maxPoolSizeInBytes, + int unmanagedBufferSizeInBytes, + UniformUnmanagedMemoryPool.TrimSettings trimSettings) { this.sharedArrayPoolThresholdInBytes = sharedArrayPoolThresholdInBytes; this.poolBufferSizeInBytes = poolBufferSizeInBytes; this.poolCapacity = (int)(maxPoolSizeInBytes / poolBufferSizeInBytes); - this.pool = new UniformUnmanagedMemoryPool(this.poolBufferSizeInBytes, this.poolCapacity); + this.trimSettings = trimSettings; + this.pool = new UniformUnmanagedMemoryPool(this.poolBufferSizeInBytes, this.poolCapacity, this.trimSettings); this.nonPoolAllocator = new UnmanagedMemoryAllocator(unmanagedBufferSizeInBytes); } @@ -125,7 +141,7 @@ namespace SixLabors.ImageSharp.Memory { UniformUnmanagedMemoryPool oldPool = Interlocked.Exchange( ref this.pool, - new UniformUnmanagedMemoryPool(this.poolBufferSizeInBytes, this.poolCapacity)); + new UniformUnmanagedMemoryPool(this.poolBufferSizeInBytes, this.poolCapacity, this.trimSettings)); oldPool.Release(); } diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs index 3782e5584e..53960b9548 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs @@ -11,6 +11,7 @@ using System.Threading; using CommandLine; using SixLabors.ImageSharp.Benchmarks.LoadResizeSave; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Memory.Internals; namespace SixLabors.ImageSharp.Tests.ProfilingSandbox { @@ -231,6 +232,9 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox [Option('f', "file", Required = false, Default = null)] public string FileOutput { get; set; } + [Option('t', "trim-time", Required = false, Default = 60)] + public int TrimTimeSeconds { get; set; } + public static CommandLineOptions Parse(string[] args) { CommandLineOptions result = null; @@ -257,7 +261,11 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox 1024 * 1024, (int)B(this.MaxContiguousPoolBufferMegaBytes), B(this.MaxPoolSizeMegaBytes), - (int)B(this.MaxCapacityOfUnmanagedBuffersMegaBytes)); + (int)B(this.MaxCapacityOfUnmanagedBuffersMegaBytes), + new UniformUnmanagedMemoryPool.TrimSettings + { + TrimPeriodMilliseconds = this.TrimTimeSeconds * 100 + }); default: throw new ArgumentOutOfRangeException(); }