From 661e32f143d3e05bc0e7550155e7d60d213cea57 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 24 Dec 2016 07:01:25 +0100 Subject: [PATCH] pooling PixelArea.Bytes --- src/ImageSharp/Image/PixelArea{TColor}.cs | 115 +++++++++++------- .../ImageSharp.Tests/Formats/Jpg/JpegTests.cs | 11 +- 2 files changed, 79 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 673fe55008..f8e46a6cc9 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -2,10 +2,10 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageSharp { using System; + using System.Buffers; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; @@ -39,6 +39,11 @@ namespace ImageSharp /// private bool isDisposed; + /// + /// True if was allocated by the constructor (rented from ) + /// + private bool isBufferOwner; + /// /// Initializes a new instance of the class. /// @@ -96,7 +101,7 @@ namespace ImageSharp /// The width. /// The component order. public PixelArea(int width, ComponentOrder componentOrder) - : this(width, 1, componentOrder, 0) + : this(width, 1, componentOrder, 0) { } @@ -107,7 +112,7 @@ namespace ImageSharp /// The component order. /// The number of bytes to pad each row. public PixelArea(int width, ComponentOrder componentOrder, int padding) - : this(width, 1, componentOrder, padding) + : this(width, 1, componentOrder, padding) { } @@ -124,7 +129,8 @@ namespace ImageSharp this.Height = height; this.ComponentOrder = componentOrder; this.RowByteCount = (width * GetComponentCount(componentOrder)) + padding; - this.Bytes = new byte[this.RowByteCount * height]; + this.Bytes = BytesPool.Rent(this.RowByteCount * height); + this.isBufferOwner = true; this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); // TODO: Why is Resharper warning us about an impure method call? @@ -137,7 +143,7 @@ namespace ImageSharp /// ~PixelArea() { - this.Dispose(); + this.Dispose(false); } /// @@ -145,20 +151,30 @@ namespace ImageSharp /// public byte[] Bytes { get; } + /// + /// Gets the component order. + /// + public ComponentOrder ComponentOrder { get; } + /// /// Gets the pointer to the pixel buffer. /// public IntPtr DataPointer => this.dataPointer; + /// + /// Gets the height. + /// + public int Height { get; } + /// /// Gets the data pointer. /// public byte* PixelBase { get; private set; } /// - /// Gets the component order. + /// Gets number of bytes in a row. /// - public ComponentOrder ComponentOrder { get; } + public int RowByteCount { get; } /// /// Gets the width. @@ -166,14 +182,17 @@ namespace ImageSharp public int Width { get; } /// - /// Gets the height. + /// Gets the pool used to rent , when it's not coming from an external source /// - public int Height { get; } + private static ArrayPool BytesPool => ArrayPool.Shared; /// - /// Gets number of bytes in a row. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// - public int RowByteCount { get; } + public void Dispose() + { + this.Dispose(true); + } /// /// Reads the stream to the area. @@ -193,39 +212,6 @@ namespace ImageSharp stream.Write(this.Bytes, 0, this.Bytes.Length); } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - if (this.isDisposed) - { - return; - } - - if (this.PixelBase == null) - { - return; - } - - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - } - - this.dataPointer = IntPtr.Zero; - this.PixelBase = null; - - this.isDisposed = true; - - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - /// /// Resets the bytes of the array to it's initial value. /// @@ -265,8 +251,45 @@ namespace ImageSharp int requiredLength = (width * GetComponentCount(componentOrder)) * height; if (bytes.Length != requiredLength) { - throw new ArgumentOutOfRangeException(nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); + throw new ArgumentOutOfRangeException( + nameof(bytes), + $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); } } + + private void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (this.PixelBase == null) + { + return; + } + + if (this.pixelsHandle.IsAllocated) + { + this.pixelsHandle.Free(); + } + + if (disposing && this.isBufferOwner) + { + BytesPool.Return(this.Bytes); + } + + this.dataPointer = IntPtr.Zero; + this.PixelBase = null; + + this.isDisposed = true; + + // This object will be cleaned up by the Dispose method. + // Therefore, you should call GC.SuppressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs index 13d31ce518..35b1ff0d93 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs @@ -19,6 +19,7 @@ namespace ImageSharp.Tests { this.Output = output; } + public static IEnumerable AllJpegFiles => TestImages.Jpeg.All; [Theory] @@ -57,7 +58,15 @@ namespace ImageSharp.Tests } } - private const int BenchmarkExecTimes = 2; + private const int BenchmarkExecTimes = 30; + + public static readonly string[] BenchmarkFiles = + { + TestImages.Bmp.Car, TestImages.Bmp.NegHeight, + TestImages.Bmp.F, TestImages.Png.Splash, + TestImages.Jpeg.Jpeg420, TestImages.Jpeg.Calliphora, + TestImages.Jpeg.Cmyk + }; [Theory( //Skip = "Benchmark, enable manually!"