From 82e22c30b4f01c90f0f6c4b36b6b64902abfb981 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 13 Jul 2021 16:11:05 +0300 Subject: [PATCH] Restored decoder parse stream only benchmark --- .../Formats/Jpeg/JpegDecoderCore.cs | 8 +- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 135 ++++++++++-------- .../Formats/Jpg/SpectralJpegTests.cs | 2 +- .../Jpg/SpectralToPixelConversionTests.cs | 2 +- 4 files changed, 82 insertions(+), 65 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index c4f8a1281f..922e9797cb 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -248,8 +248,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The input stream. /// Scan decoder used exclusively to decode SOS marker. - /// The token to monitor cancellation. - internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDecoder, CancellationToken ct) + /// The token to monitor cancellation. + internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDecoder, CancellationToken cancellationToken) { bool metadataOnly = scanDecoder == null; @@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg while (fileMarker.Marker != JpegConstants.Markers.EOI || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid)) { - ct.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (!fileMarker.Invalid) { @@ -301,7 +301,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg case JpegConstants.Markers.SOS: if (!metadataOnly) { - this.ProcessStartOfScanMarker(stream, ct); + this.ProcessStartOfScanMarker(stream, cancellationToken); break; } else diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 6796faa6d0..8659aee634 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -1,59 +1,76 @@ -//// Copyright (c) Six Labors. -//// Licensed under the Apache License, Version 2.0. - -//using System.IO; -//using BenchmarkDotNet.Attributes; -//using SixLabors.ImageSharp.Formats.Jpeg; -//using SixLabors.ImageSharp.IO; -//using SixLabors.ImageSharp.Tests; -//using SDSize = System.Drawing.Size; - -//namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg -//{ -// [Config(typeof(Config.ShortMultiFramework))] -// public class DecodeJpegParseStreamOnly -// { -// [Params(TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr)] -// public string TestImage { get; set; } - -// private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - -// private byte[] jpegBytes; - -// [GlobalSetup] -// public void Setup() -// => this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - -// [Benchmark(Baseline = true, Description = "System.Drawing FULL")] -// public SDSize JpegSystemDrawing() -// { -// using var memoryStream = new MemoryStream(this.jpegBytes); -// using var image = System.Drawing.Image.FromStream(memoryStream); -// return image.Size; -// } - -// [Benchmark(Description = "JpegDecoderCore.ParseStream")] -// public void ParseStream() -// { -// using var memoryStream = new MemoryStream(this.jpegBytes); -// using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream); - -// var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true }); -// decoder.ParseStream(bufferedStream); -// decoder.Dispose(); -// } -// } - -// /* -// | Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | -// |---------------------------- |----------- |-------------- |--------------------- |---------:|----------:|----------:|------:|--------:|------:|------:|----------:| -// | 'System.Drawing FULL' | Job-HITJFX | .NET 4.7.2 | Jpg/b(...)e.jpg [21] | 5.828 ms | 0.9885 ms | 0.0542 ms | 1.00 | 46.8750 | - | - | 211566 B | -// | JpegDecoderCore.ParseStream | Job-HITJFX | .NET 4.7.2 | Jpg/b(...)e.jpg [21] | 5.833 ms | 0.2923 ms | 0.0160 ms | 1.00 | - | - | - | 12416 B | -// | | | | | | | | | | | | | -// | 'System.Drawing FULL' | Job-WPSKZD | .NET Core 2.1 | Jpg/b(...)e.jpg [21] | 6.018 ms | 2.1374 ms | 0.1172 ms | 1.00 | 46.8750 | - | - | 210768 B | -// | JpegDecoderCore.ParseStream | Job-WPSKZD | .NET Core 2.1 | Jpg/b(...)e.jpg [21] | 4.382 ms | 0.9009 ms | 0.0494 ms | 0.73 | - | - | - | 12360 B | -// | | | | | | | | | | | | | -// | 'System.Drawing FULL' | Job-ZLSNRP | .NET Core 3.1 | Jpg/b(...)e.jpg [21] | 5.714 ms | 0.4078 ms | 0.0224 ms | 1.00 | - | - | - | 176 B | -// | JpegDecoderCore.ParseStream | Job-ZLSNRP | .NET Core 3.1 | Jpg/b(...)e.jpg [21] | 4.239 ms | 1.0943 ms | 0.0600 ms | 0.74 | - | - | - | 12406 B | -// */ -//} +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Tests; +using SDSize = System.Drawing.Size; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +{ + //[Config(typeof(Config.ShortMultiFramework))] + public class DecodeJpegParseStreamOnly + { + [Params(TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr)] + public string TestImage { get; set; } + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + private byte[] jpegBytes; + + [GlobalSetup] + public void Setup() + => this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); + + //[Benchmark(Baseline = true, Description = "System.Drawing FULL")] + //public SDSize JpegSystemDrawing() + //{ + // using var memoryStream = new MemoryStream(this.jpegBytes); + // using var image = System.Drawing.Image.FromStream(memoryStream); + // return image.Size; + //} + + [Benchmark(Description = "JpegDecoderCore.ParseStream")] + public void ParseStream() + { + using var memoryStream = new MemoryStream(this.jpegBytes); + using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream); + + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true }); + var scanDecoder = new HuffmanScanDecoder(bufferedStream, new NoopSpectralConverter(), cancellationToken: default); + decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default); + decoder.Dispose(); + } + + // We want to test only stream parsing and scan decoding, we don't need to convert spectral data to actual pixels + // Nor we need to allocate final pixel buffer + // Note: this still introduces virtual method call overhead for baseline interleaved images + // There's no way to eliminate it as spectral conversion is built into the scan decoding loop for memory footprint reduction + private class NoopSpectralConverter : SpectralConverter + { + public override void ConvertStrideBaseline() + { + } + + public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) + { + } + } + } + + /* + | Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | + |---------------------------- |----------- |-------------- |--------------------- |---------:|----------:|----------:|------:|--------:|------:|------:|----------:| + | 'System.Drawing FULL' | Job-HITJFX | .NET 4.7.2 | Jpg/b(...)e.jpg [21] | 5.828 ms | 0.9885 ms | 0.0542 ms | 1.00 | 46.8750 | - | - | 211566 B | + | JpegDecoderCore.ParseStream | Job-HITJFX | .NET 4.7.2 | Jpg/b(...)e.jpg [21] | 5.833 ms | 0.2923 ms | 0.0160 ms | 1.00 | - | - | - | 12416 B | + | | | | | | | | | | | | | + | 'System.Drawing FULL' | Job-WPSKZD | .NET Core 2.1 | Jpg/b(...)e.jpg [21] | 6.018 ms | 2.1374 ms | 0.1172 ms | 1.00 | 46.8750 | - | - | 210768 B | + | JpegDecoderCore.ParseStream | Job-WPSKZD | .NET Core 2.1 | Jpg/b(...)e.jpg [21] | 4.382 ms | 0.9009 ms | 0.0494 ms | 0.73 | - | - | - | 12360 B | + | | | | | | | | | | | | | + | 'System.Drawing FULL' | Job-ZLSNRP | .NET Core 3.1 | Jpg/b(...)e.jpg [21] | 5.714 ms | 0.4078 ms | 0.0224 ms | 1.00 | - | - | - | 176 B | + | JpegDecoderCore.ParseStream | Job-ZLSNRP | .NET Core 3.1 | Jpg/b(...)e.jpg [21] | 4.239 ms | 1.0943 ms | 0.0600 ms | 0.74 | - | - | - | 12406 B | + */ +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 09548e2761..0d4881adab 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var scanDecoder = new HuffmanScanDecoder(bufferedStream, debugConverter, cancellationToken: default); // This would parse entire image - decoder.ParseStream(bufferedStream, scanDecoder, ct: default); + decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default); // Actual verification this.VerifySpectralCorrectnessImpl(libJpegData, debugConverter.SpectralData); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs index b0e5a3db6b..353ae39f0f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using var converter = new SpectralConverter(Configuration.Default, cancellationToken: default); var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); var scanDecoder = new HuffmanScanDecoder(bufferedStream, converter, cancellationToken: default); - decoder.ParseStream(bufferedStream, scanDecoder, ct: default); + decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default); // Test metadata provider.Utility.TestGroupName = nameof(JpegDecoderTests);