diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index 52c0a51b9..999a44ff3 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using ImageMagick; using PhotoSauce.MagicScaler; using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests; using SkiaSharp; @@ -53,6 +54,9 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave public int ThumbnailSize { get; set; } = 150; + // Inject leaking memory allocation requests to ImageSharp processing code to stress-test finalizer behavior. + public bool EmulateLeakedAllocations { get; set; } + private static readonly string[] ProgressiveFiles = { "ancyloscelis-apiformis-m-paraguay-face_2014-08-08-095255-zs-pmax_15046500892_o.jpg", @@ -180,6 +184,12 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave using var image = ImageSharpImage.Load(input); this.IncreaseTotalMegapixels(image.Width, image.Height); + if (this.EmulateLeakedAllocations) + { + _ = Configuration.Default.MemoryAllocator.Allocate(image.Width * image.Height); + _ = Configuration.Default.MemoryAllocator.Allocate(1 << 21); + } + image.Mutate(i => i.Resize(new ResizeOptions { Size = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize), @@ -191,6 +201,11 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave // Save the results image.Save(output, this.imageSharpJpegEncoder); + + if (this.EmulateLeakedAllocations) + { + _ = Configuration.Default.MemoryAllocator.Allocate2D(image.Width, image.Height); + } } public void MagickResize(string input) diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs index 734bbf826..e8b3a744c 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs @@ -58,6 +58,8 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox MemoryAllocator.Default = Configuration.Default.MemoryAllocator = options.CreateMemoryAllocator(); } + lrs.Benchmarks.EmulateLeakedAllocations = options.LeakAllocations; + timer = Stopwatch.StartNew(); try { @@ -235,6 +237,9 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox [Option('t', "trim-period", Required = false, Default = null, HelpText = "Trim period for the pool in seconds")] public int? TrimTimeSeconds { get; set; } + [Option('l', "leak-allocations", Required = false, Default = false, HelpText = "Inject leaking memory allocation requests to stress-test finalizer behavior.")] + public bool LeakAllocations { get; set; } + public static CommandLineOptions Parse(string[] args) { CommandLineOptions result = null; @@ -252,7 +257,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox } public override string ToString() => - $"p({this.MaxDegreeOfParallelism})_i({this.ImageSharp})_d({this.KeepDefaultAllocator})_m({this.MaxContiguousPoolBufferMegaBytes})_s({this.MaxPoolSizeMegaBytes})_u({this.MaxCapacityOfNonPoolBuffersMegaBytes})_r({this.RepeatCount})_g({this.FinalGcCount})_e({this.ReleaseRetainedResourcesAtEnd})"; + $"p({this.MaxDegreeOfParallelism})_i({this.ImageSharp})_d({this.KeepDefaultAllocator})_m({this.MaxContiguousPoolBufferMegaBytes})_s({this.MaxPoolSizeMegaBytes})_u({this.MaxCapacityOfNonPoolBuffersMegaBytes})_r({this.RepeatCount})_g({this.FinalGcCount})_e({this.ReleaseRetainedResourcesAtEnd}_l({this.LeakAllocations}))"; public MemoryAllocator CreateMemoryAllocator() { @@ -285,7 +290,22 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox private void SystemDrawingBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SystemDrawingResize); - private void ImageSharpBenchmarkParallel() => this.ForEachImage(this.Benchmarks.ImageSharpResize); + private void ImageSharpBenchmarkParallel() + { + int cnt = 0; + this.ForEachImage(f => + { + this.Benchmarks.ImageSharpResize(f); + if (cnt % 4 == 0 && this.Benchmarks.EmulateLeakedAllocations) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } + + cnt++; + }); + } private void MagickBenchmarkParallel() => this.ForEachImage(this.Benchmarks.MagickResize);