From 01ab0d16ecf7fa2b14da2c2cf740bf1bc50743f1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 28 Apr 2018 12:48:36 +1000 Subject: [PATCH] Pool buffer --- .../Components/DoubleBufferedStreamReader.cs | 18 ++++- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 4 +- .../Codecs/Jpeg/DoubleBufferedStreams.cs | 73 +++++++++++++++++++ .../General/DoubleBufferedStreams.cs | 53 -------------- 4 files changed, 90 insertions(+), 58 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs delete mode 100644 tests/ImageSharp.Benchmarks/General/DoubleBufferedStreams.cs diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs index 0818d7309..d8a43428d 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Memory; // TODO: This could be useful elsewhere. namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components @@ -11,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// A stream reader that add a secondary level buffer in addition to native stream buffered reading /// to reduce the overhead of small incremental reads. /// - internal class DoubleBufferedStreamReader + internal class DoubleBufferedStreamReader : IDisposable { /// /// The length, in bytes, of the chunk @@ -20,6 +21,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components private readonly Stream stream; + private readonly IManagedByteBuffer buffer; + private readonly byte[] chunk; private int bytesRead; @@ -29,14 +32,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The input stream. - public DoubleBufferedStreamReader(Stream stream) + public DoubleBufferedStreamReader(MemoryManager memoryManager, Stream stream) { this.stream = stream; this.Length = stream.Length; - // TODO: Consider pooling this. - this.chunk = new byte[ChunkLength]; + this.buffer = memoryManager.AllocateCleanManagedByteBuffer(ChunkLength); + this.chunk = this.buffer.Array; } /// @@ -147,5 +151,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components return Math.Max(n, 0); } + + /// + public void Dispose() + { + this.buffer?.Dispose(); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 2322758ee..c20f283d7 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort public void ParseStream(Stream stream, bool metadataOnly = false) { this.MetaData = new ImageMetaData(); - this.InputStream = new DoubleBufferedStreamReader(stream); + this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryManager, stream); // Check for the Start Of Image marker. this.InputStream.Read(this.markerBuffer, 0, 2); @@ -339,9 +339,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// public void Dispose() { + this.InputStream?.Dispose(); this.Frame?.Dispose(); // Set large fields to null. + this.InputStream = null; this.Frame = null; this.dcHuffmanTables = null; this.acHuffmanTables = null; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs new file mode 100644 index 000000000..d178a4970 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +{ + [Config(typeof(Config.ShortClr))] + public class DoubleBufferedStreams + { + private byte[] buffer = CreateTestBytes(); + + private MemoryStream stream1; + private MemoryStream stream2; + DoubleBufferedStreamReader reader; + + [GlobalSetup] + public void CreateStreams() + { + this.stream1 = new MemoryStream(this.buffer); + this.stream2 = new MemoryStream(this.buffer); + this.reader = new DoubleBufferedStreamReader(Configuration.Default.MemoryManager, this.stream2); + } + + [GlobalCleanup] + public void DestroyStreams() + { + this.stream1?.Dispose(); + this.stream2?.Dispose(); + this.reader?.Dispose(); + } + + [Benchmark(Baseline = true)] + public int StandardStream() + { + int r = 0; + Stream stream = this.stream1; + + for (int i = 0; i < stream.Length; i++) + { + r += stream.ReadByte(); + } + + return r; + } + + [Benchmark] + public int DoubleBufferedStream() + { + int r = 0; + DoubleBufferedStreamReader reader = this.reader; + + for (int i = 0; i < reader.Length; i++) + { + r += reader.ReadByte(); + } + + return r; + } + + private static byte[] CreateTestBytes() + { + byte[] buffer = new byte[DoubleBufferedStreamReader.ChunkLength * 3]; + var random = new Random(); + random.NextBytes(buffer); + + return buffer; + } + } +} diff --git a/tests/ImageSharp.Benchmarks/General/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/General/DoubleBufferedStreams.cs deleted file mode 100644 index 665d0cbad..000000000 --- a/tests/ImageSharp.Benchmarks/General/DoubleBufferedStreams.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.IO; -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; - -namespace SixLabors.ImageSharp.Benchmarks.General -{ - [Config(typeof(Config.ShortClr))] - public class DoubleBufferedStreams - { - private byte[] buffer = CreateTestBytes(); - - [Benchmark] - public int StandardStream() - { - int r = 0; - using (var stream = new MemoryStream(this.buffer)) - { - for (int i = 0; i < stream.Length; i++) - { - r += stream.ReadByte(); - } - } - - return r; - } - - [Benchmark] - public int ChunkedStream() - { - int r = 0; - using (var stream = new MemoryStream(this.buffer)) - { - var reader = new DoubleBufferedStreamReader(stream); - for (int i = 0; i < reader.Length; i++) - { - r += reader.ReadByte(); - } - } - - return r; - } - - private static byte[] CreateTestBytes() - { - byte[] buffer = new byte[DoubleBufferedStreamReader.ChunkLength * 3]; - var random = new Random(); - random.NextBytes(buffer); - - return buffer; - } - } -}