Browse Source

Merge pull request #2051 from br3aker/dp/jpeg-benchmark

Improved jpeg encoding benchmarks
pull/2058/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
823e785d4e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 161
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
  2. 112
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs
  3. 89
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs
  4. 34
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegMultiple.cs

161
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs

@ -1,161 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Drawing.Imaging;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
public class EncodeJpeg
{
[Params(75, 90, 100)]
public int Quality;
private const string TestImage = TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr;
// System.Drawing
private SDImage bmpDrawing;
private Stream bmpStream;
private ImageCodecInfo jpegCodec;
private EncoderParameters encoderParameters;
// ImageSharp
private Image<Rgba32> bmpCore;
private Image<L8> bmpLuminance;
private JpegEncoder encoder400;
private JpegEncoder encoder420;
private JpegEncoder encoder444;
private JpegEncoder encoderRgb;
private MemoryStream destinationStream;
[GlobalSetup]
public void ReadImages()
{
if (this.bmpStream == null)
{
this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.bmpCore = Image.Load<Rgba32>(this.bmpStream);
this.bmpCore.Metadata.ExifProfile = null;
this.bmpLuminance = this.bmpCore.CloneAs<L8>();
this.encoder400 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.Luminance };
this.encoder420 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 };
this.encoder444 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio444 };
this.encoderRgb = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.Rgb };
this.bmpStream.Position = 0;
this.bmpDrawing = SDImage.FromStream(this.bmpStream);
this.jpegCodec = GetEncoder(ImageFormat.Jpeg);
this.encoderParameters = new EncoderParameters(1);
// Quality cast to long is necessary
#pragma warning disable IDE0004 // Remove Unnecessary Cast
this.encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)this.Quality);
#pragma warning restore IDE0004 // Remove Unnecessary Cast
this.destinationStream = new MemoryStream();
}
}
[GlobalCleanup]
public void Cleanup()
{
this.bmpStream.Dispose();
this.bmpStream = null;
this.destinationStream.Dispose();
this.destinationStream = null;
this.bmpCore.Dispose();
this.bmpDrawing.Dispose();
this.encoderParameters.Dispose();
}
[Benchmark(Baseline = true, Description = "System.Drawing Jpeg 4:2:0")]
public void JpegSystemDrawing()
{
this.bmpDrawing.Save(this.destinationStream, this.jpegCodec, this.encoderParameters);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
[Benchmark(Description = "ImageSharp (greyscale) Jpeg 4:0:0")]
public void JpegCore400()
{
this.bmpLuminance.SaveAsJpeg(this.destinationStream, this.encoder400);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
[Benchmark(Description = "ImageSharp Jpeg 4:2:0")]
public void JpegCore420()
{
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder420);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
[Benchmark(Description = "ImageSharp Jpeg 4:4:4")]
public void JpegCore444()
{
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder444);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
[Benchmark(Description = "ImageSharp Jpeg rgb")]
public void JpegRgb()
{
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoderRgb);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
// https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.encoderparameter?redirectedfrom=MSDN&view=net-5.0
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
}
/*
BenchmarkDotNet=v0.13.0, OS=linuxmint 20.3
AMD Ryzen 7 5800X, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.200
[Host] : .NET Core 3.1.22 (CoreCLR 4.700.21.56803, CoreFX 4.700.21.57101), X64 RyuJIT
DefaultJob : .NET Core 3.1.22 (CoreCLR 4.700.21.56803, CoreFX 4.700.21.57101), X64 RyuJIT
| Method | Quality | Mean | Error | StdDev | Ratio | RatioSD |
|------------------------------------ |-------- |----------:|----------:|----------:|------:|--------:|
| 'System.Drawing Jpeg 4:2:0' | 75 | 9.157 ms | 0.0138 ms | 0.0123 ms | 1.00 | 0.00 |
| 'ImageSharp (greyscale) Jpeg 4:0:0' | 75 | 12.142 ms | 0.1321 ms | 0.1236 ms | 1.33 | 0.01 |
| 'ImageSharp Jpeg 4:2:0' | 75 | 19.655 ms | 0.1057 ms | 0.0883 ms | 2.15 | 0.01 |
| 'ImageSharp Jpeg 4:4:4' | 75 | 19.157 ms | 0.2852 ms | 0.2668 ms | 2.09 | 0.03 |
| 'ImageSharp Jpeg rgb' | 75 | 26.404 ms | 0.3803 ms | 0.3557 ms | 2.89 | 0.04 |
| | | | | | | |
| 'System.Drawing Jpeg 4:2:0' | 90 | 10.828 ms | 0.0727 ms | 0.0680 ms | 1.00 | 0.00 |
| 'ImageSharp (greyscale) Jpeg 4:0:0' | 90 | 14.918 ms | 0.1089 ms | 0.1019 ms | 1.38 | 0.01 |
| 'ImageSharp Jpeg 4:2:0' | 90 | 23.718 ms | 0.0301 ms | 0.0267 ms | 2.19 | 0.02 |
| 'ImageSharp Jpeg 4:4:4' | 90 | 23.857 ms | 0.2387 ms | 0.2233 ms | 2.20 | 0.03 |
| 'ImageSharp Jpeg rgb' | 90 | 34.700 ms | 0.2207 ms | 0.2064 ms | 3.20 | 0.03 |
| | | | | | | |
| 'System.Drawing Jpeg 4:2:0' | 100 | 13.478 ms | 0.0054 ms | 0.0048 ms | 1.00 | 0.00 |
| 'ImageSharp (greyscale) Jpeg 4:0:0' | 100 | 19.446 ms | 0.0803 ms | 0.0751 ms | 1.44 | 0.01 |
| 'ImageSharp Jpeg 4:2:0' | 100 | 30.339 ms | 0.4578 ms | 0.4282 ms | 2.25 | 0.03 |
| 'ImageSharp Jpeg 4:4:4' | 100 | 39.056 ms | 0.1779 ms | 0.1664 ms | 2.90 | 0.01 |
| 'ImageSharp Jpeg rgb' | 100 | 51.828 ms | 0.3336 ms | 0.3121 ms | 3.85 | 0.02 |
*/

112
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs

@ -0,0 +1,112 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Drawing.Imaging;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using SkiaSharp;
using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
/// <summary>
/// Benchmark for performance comparison between other codecs.
/// </summary>
/// <remarks>
/// This benchmarks tests baseline 4:2:0 chroma sampling path.
/// </remarks>
public class EncodeJpegComparison
{
// Big enough, 4:4:4 chroma sampling
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
// Change/add parameters for extra benchmarks
[Params(75, 90, 100)]
public int Quality;
private MemoryStream destinationStream;
// ImageSharp
private Image<Rgba32> imageImageSharp;
private JpegEncoder encoderImageSharp;
// SkiaSharp
private SKBitmap imageSkiaSharp;
[GlobalSetup(Target = nameof(BenchmarkImageSharp))]
public void SetupImageSharp()
{
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.imageImageSharp = Image.Load<Rgba32>(imageBinaryStream);
this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 };
this.destinationStream = new MemoryStream();
}
[GlobalCleanup(Target = nameof(BenchmarkImageSharp))]
public void CleanupImageSharp()
{
this.imageImageSharp.Dispose();
this.imageImageSharp = null;
this.destinationStream.Dispose();
this.destinationStream = null;
}
[Benchmark(Description = "ImageSharp")]
public void BenchmarkImageSharp()
{
this.imageImageSharp.SaveAsJpeg(this.destinationStream, this.encoderImageSharp);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
[GlobalSetup(Target = nameof(BenchmarkSkiaSharp))]
public void SetupSkiaSharp()
{
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.imageSkiaSharp = SKBitmap.Decode(imageBinaryStream);
this.destinationStream = new MemoryStream();
}
[GlobalCleanup(Target = nameof(BenchmarkSkiaSharp))]
public void CleanupSkiaSharp()
{
this.imageSkiaSharp.Dispose();
this.imageSkiaSharp = null;
this.destinationStream.Dispose();
this.destinationStream = null;
}
[Benchmark(Description = "SkiaSharp")]
public void BenchmarkSkiaSharp()
{
this.imageSkiaSharp.Encode(SKEncodedImageFormat.Jpeg, this.Quality).SaveTo(this.destinationStream);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
}
}
/*
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.100-preview.3.21202.5
[Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
| Method | Quality | Mean | Error | StdDev |
|----------- |-------- |----------:|----------:|----------:|
| ImageSharp | 75 | 6.820 ms | 0.0374 ms | 0.0312 ms |
| SkiaSharp | 75 | 16.417 ms | 0.3238 ms | 0.4747 ms |
| ImageSharp | 90 | 7.849 ms | 0.1565 ms | 0.3126 ms |
| SkiaSharp | 90 | 16.893 ms | 0.2200 ms | 0.2058 ms |
| ImageSharp | 100 | 11.016 ms | 0.2087 ms | 0.1850 ms |
| SkiaSharp | 100 | 20.410 ms | 0.2583 ms | 0.2290 ms |
*/

89
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs

@ -0,0 +1,89 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
/// <summary>
/// Benchmark for all available encoding features of the Jpeg file type.
/// </summary>
/// <remarks>
/// This benchmark does NOT compare ImageSharp to any other jpeg codecs.
/// </remarks>
public class EncodeJpegFeatures
{
// Big enough, 4:4:4 chroma sampling
// No metadata
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
public static IEnumerable<JpegColorType> ColorSpaceValues =>
new[] { JpegColorType.Luminance, JpegColorType.Rgb, JpegColorType.YCbCrRatio420, JpegColorType.YCbCrRatio444 };
[Params(75, 90, 100)]
public int Quality;
[ParamsSource(nameof(ColorSpaceValues), Priority = -100)]
public JpegColorType TargetColorSpace;
private Image<Rgb24> bmpCore;
private JpegEncoder encoder;
private MemoryStream destinationStream;
[GlobalSetup]
public void Setup()
{
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.bmpCore = Image.Load<Rgb24>(imageBinaryStream);
this.encoder = new JpegEncoder { Quality = this.Quality, ColorType = this.TargetColorSpace };
this.destinationStream = new MemoryStream();
}
[GlobalCleanup]
public void Cleanup()
{
this.bmpCore.Dispose();
this.bmpCore = null;
this.destinationStream.Dispose();
this.destinationStream = null;
}
[Benchmark]
public void Benchmark()
{
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder);
this.destinationStream.Seek(0, SeekOrigin.Begin);
}
}
}
/*
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.100-preview.3.21202.5
[Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
| Method | TargetColorSpace | Quality | Mean | Error | StdDev |
|---------- |----------------- |-------- |----------:|----------:|----------:|
| Benchmark | Luminance | 75 | 7.055 ms | 0.1411 ms | 0.3297 ms |
| Benchmark | Rgb | 75 | 12.139 ms | 0.0645 ms | 0.0538 ms |
| Benchmark | YCbCrRatio420 | 75 | 6.463 ms | 0.0282 ms | 0.0235 ms |
| Benchmark | YCbCrRatio444 | 75 | 8.616 ms | 0.0422 ms | 0.0374 ms |
| Benchmark | Luminance | 90 | 7.011 ms | 0.0361 ms | 0.0301 ms |
| Benchmark | Rgb | 90 | 13.119 ms | 0.0947 ms | 0.0886 ms |
| Benchmark | YCbCrRatio420 | 90 | 6.786 ms | 0.0328 ms | 0.0274 ms |
| Benchmark | YCbCrRatio444 | 90 | 8.672 ms | 0.0772 ms | 0.0722 ms |
| Benchmark | Luminance | 100 | 9.554 ms | 0.1211 ms | 0.1012 ms |
| Benchmark | Rgb | 100 | 19.475 ms | 0.1080 ms | 0.0958 ms |
| Benchmark | YCbCrRatio420 | 100 | 10.146 ms | 0.0585 ms | 0.0519 ms |
| Benchmark | YCbCrRatio444 | 100 | 15.317 ms | 0.0709 ms | 0.0592 ms |
*/

34
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegMultiple.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.Drawing.Imaging;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortMultiFramework))]
public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" };
protected override IEnumerable<string> SearchPatterns => new[] { "*.bmp", "*.jpg" };
[Benchmark(Description = "EncodeJpegMultiple - ImageSharp")]
public void EncodeJpegImageSharp()
=> this.ForEachImageSharpImage((img, ms) =>
{
img.Save(ms, new JpegEncoder());
return null;
});
[Benchmark(Baseline = true, Description = "EncodeJpegMultiple - System.Drawing")]
public void EncodeJpegSystemDrawing()
=> this.ForEachSystemDrawingImage((img, ms) =>
{
img.Save(ms, ImageFormat.Jpeg);
return null;
});
}
}
Loading…
Cancel
Save