From 5528a2923ccc63d776c91994b0b17a2c3ad5be94 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 15 Sep 2022 14:22:02 +1000 Subject: [PATCH] Update missed test and benchmarks project --- .../Codecs/Bmp/DecodeBmp.cs | 58 +- .../Codecs/Bmp/EncodeBmp.cs | 72 +- .../Codecs/Bmp/EncodeBmpMultiple.cs | 40 +- .../Codecs/Gif/DecodeGif.cs | 58 +- .../Codecs/Gif/EncodeGif.cs | 88 +- .../Codecs/Gif/EncodeGifMultiple.cs | 52 +- .../BlockOperations/Block8x8F_AddInPlace.cs | 21 +- .../BlockOperations/Block8x8F_CopyTo1x1.cs | 676 +++++++------ .../BlockOperations/Block8x8F_CopyTo2x2.cs | 773 ++++++++------- .../BlockOperations/Block8x8F_DivideRound.cs | 231 +++-- .../Block8x8F_LoadFromInt16.cs | 58 +- .../Block8x8F_MultiplyInPlaceBlock.cs | 43 +- .../Block8x8F_MultiplyInPlaceScalar.cs | 21 +- .../BlockOperations/Block8x8F_Quantize.cs | 49 +- .../Jpeg/BlockOperations/Block8x8F_Round.cs | 892 +++++++++--------- .../BlockOperations/Block8x8F_Transpose.cs | 47 +- .../ColorConversion/CmykColorConversion.cs | 61 +- .../ColorConversionBenchmark.cs | 75 +- .../GrayscaleColorConversion.cs | 37 +- .../ColorConversion/RgbColorConversion.cs | 61 +- .../ColorConversion/YCbCrColorConversion.cs | 61 +- .../ColorConversion/YccKColorConverter.cs | 61 +- .../Codecs/Jpeg/DecodeJpeg.cs | 94 +- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 92 +- .../Codecs/Jpeg/DecodeJpeg_Aggregate.cs | 52 +- .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 102 +- .../Codecs/Jpeg/EncodeJpegComparison.cs | 162 ++-- .../Codecs/Jpeg/EncodeJpegFeatures.cs | 109 ++- .../Codecs/Jpeg/IdentifyJpeg.cs | 42 +- .../Codecs/MultiImageBenchmarkBase.cs | 343 ++++--- .../Codecs/Png/DecodeFilteredPng.cs | 94 +- .../Codecs/Png/DecodePng.cs | 58 +- .../Codecs/Png/EncodeIndexedPng.cs | 136 ++- .../Codecs/Png/EncodePng.cs | 82 +- .../Codecs/Tga/DecodeTga.cs | 109 ++- .../Codecs/Tga/EncodeTga.cs | 72 +- .../Codecs/Tiff/DecodeTiff.cs | 108 ++- .../Codecs/Tiff/EncodeTiff.cs | 179 ++-- .../Codecs/Webp/DecodeWebp.cs | 178 ++-- .../Codecs/Webp/EncodeWebp.cs | 228 +++-- .../Color/Bulk/FromRgba32Bytes.cs | 122 ++- .../Color/Bulk/FromVector4.cs | 236 +++-- .../Color/Bulk/FromVector4_Rgb24.cs | 9 +- .../Color/Bulk/Pad3Shuffle4Channel.cs | 148 ++- .../Color/Bulk/PremultiplyVector4.cs | 90 +- .../Color/Bulk/Rgb24Bytes.cs | 85 +- .../Color/Bulk/Shuffle3Channel.cs | 104 +- .../Color/Bulk/Shuffle4Slice3Channel.cs | 164 ++-- .../Color/Bulk/ShuffleByte4Channel.cs | 110 ++- .../Color/Bulk/ShuffleFloat4Channel.cs | 110 ++- .../Color/Bulk/ToRgba32Bytes.cs | 126 ++- .../Color/Bulk/ToVector4.cs | 76 +- .../Color/Bulk/ToVector4_Bgra32.cs | 63 +- .../Color/Bulk/ToVector4_Rgb24.cs | 21 +- .../Color/Bulk/ToVector4_Rgba32.cs | 254 +++-- .../Color/Bulk/UnPremultiplyVector4.cs | 90 +- .../Color/ColorEquality.cs | 27 +- .../Color/ColorspaceCieXyzToCieLabConvert.cs | 33 +- .../ColorspaceCieXyzToHunterLabConvert.cs | 33 +- .../Color/ColorspaceCieXyzToLmsConvert.cs | 33 +- .../Color/ColorspaceCieXyzToRgbConvert.cs | 33 +- .../Color/RgbWorkingSpaceAdapt.cs | 33 +- .../ImageSharp.Benchmarks/Color/YcbCrToRgb.cs | 81 +- .../Config.HwIntrinsics.cs | 123 ++- tests/ImageSharp.Benchmarks/Config.cs | 55 +- .../General/Adler32Benchmark.cs | 114 ++- .../ImageSharp.Benchmarks/General/Array2D.cs | 178 ++-- .../General/ArrayReverse.cs | 82 +- .../General/BasicMath/Abs.cs | 60 +- .../General/BasicMath/ClampFloat.cs | 88 +- .../General/BasicMath/ClampInt32IntoByte.cs | 132 ++- .../General/BasicMath/ClampSpan.cs | 50 +- .../General/BasicMath/ClampVector4.cs | 77 +- .../BasicMath/ModuloPowerOfTwoConstant.cs | 29 +- .../BasicMath/ModuloPowerOfTwoVariable.cs | 45 +- .../General/BasicMath/Pow.cs | 58 +- .../General/BasicMath/Round.cs | 28 +- .../General/Buffer2D_DangerousGetRowSpan.cs | 69 +- .../General/CopyBuffers.cs | 430 +++++---- .../General/Crc32Benchmark.cs | 114 ++- .../General/GetSetPixel.cs | 31 +- .../General/IO/BufferedReadStreamWrapper.cs | 407 ++++---- .../General/IO/BufferedStreams.cs | 425 +++++---- .../General/PixelConversion/ITestPixel.cs | 27 +- .../PixelConversion_ConvertFromRgba32.cs | 276 +++--- .../PixelConversion_ConvertFromVector4.cs | 199 ++-- .../PixelConversion_ConvertToRgba32.cs | 159 ++-- ...vertToRgba32_AsPartOfCompositeOperation.cs | 155 ++- .../PixelConversion_ConvertToVector4.cs | 111 ++- ...ertToVector4_AsPartOfCompositeOperation.cs | 127 ++- .../PixelConversion_PackFromRgbPlanes.cs | 454 +++++---- .../PixelConversion_Rgba32_To_Argb32.cs | 276 +++--- .../PixelConversion_Rgba32_To_Bgra32.cs | 626 ++++++------ .../General/PixelConversion/TestArgb.cs | 147 ++- .../General/PixelConversion/TestRgba.cs | 111 ++- .../General/StructCasting.cs | 37 +- .../General/Vector4Constants.cs | 102 +- .../General/Vectorization/BitwiseOrUint32.cs | 69 +- .../General/Vectorization/DivFloat.cs | 69 +- .../General/Vectorization/DivUInt32.cs | 71 +- .../General/Vectorization/Divide.cs | 85 +- .../General/Vectorization/MulFloat.cs | 89 +- .../General/Vectorization/MulUInt32.cs | 69 +- .../General/Vectorization/Multiply.cs | 61 +- .../General/Vectorization/Premultiply.cs | 95 +- .../Vectorization/ReinterpretUInt32AsFloat.cs | 77 +- .../Vectorization/SIMDBenchmarkBase.cs | 83 +- .../General/Vectorization/UInt32ToSingle.cs | 143 ++- .../General/Vectorization/VectorFetching.cs | 157 ++- .../Vectorization/WidenBytesToUInt32.cs | 81 +- .../LoadResizeSaveStressBenchmarks.cs | 122 ++- .../LoadResizeSaveStressRunner.cs | 541 ++++++----- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 134 ++- .../Processing/BokehBlur.cs | 17 +- .../ImageSharp.Benchmarks/Processing/Crop.cs | 43 +- .../Processing/DetectEdges.cs | 64 +- .../Processing/Diffuse.cs | 33 +- .../Processing/GaussianBlur.cs | 17 +- .../Processing/HistogramEqualization.cs | 68 +- .../Processing/Resize.cs | 391 ++++---- .../Processing/Rotate.cs | 19 +- .../ImageSharp.Benchmarks/Processing/Skew.cs | 19 +- tests/ImageSharp.Benchmarks/Program.cs | 25 +- .../LoadResizeSaveParallelMemoryStress.cs | 506 +++++----- .../Program.cs | 105 +-- 125 files changed, 8147 insertions(+), 8364 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs index e11825122d..5bd806c487 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs @@ -1,49 +1,47 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; using SDSize = System.Drawing.Size; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeBmp { - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeBmp - { - private byte[] bmpBytes; + private byte[] bmpBytes; - private string TestImageFullPath - => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath + => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.bmpBytes == null) { - if (this.bmpBytes == null) - { - this.bmpBytes = File.ReadAllBytes(this.TestImageFullPath); - } + this.bmpBytes = File.ReadAllBytes(this.TestImageFullPath); } + } - [Params(TestImages.Bmp.Car)] - public string TestImage { get; set; } + [Params(TestImages.Bmp.Car)] + public string TestImage { get; set; } - [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] - public SDSize BmpSystemDrawing() - { - using var memoryStream = new MemoryStream(this.bmpBytes); - using var image = SDImage.FromStream(memoryStream); - return image.Size; - } + [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] + public SDSize BmpSystemDrawing() + { + using var memoryStream = new MemoryStream(this.bmpBytes); + using var image = SDImage.FromStream(memoryStream); + return image.Size; + } - [Benchmark(Description = "ImageSharp Bmp")] - public Size BmpImageSharp() - { - using var memoryStream = new MemoryStream(this.bmpBytes); - using var image = Image.Load(memoryStream); - return new Size(image.Width, image.Height); - } + [Benchmark(Description = "ImageSharp Bmp")] + public Size BmpImageSharp() + { + using var memoryStream = new MemoryStream(this.bmpBytes); + using var image = Image.Load(memoryStream); + return new Size(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs index 3f64b2cff9..ab6cdf28e0 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs @@ -2,54 +2,52 @@ // Licensed under the Six Labors Split License. using System.Drawing.Imaging; -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeBmp { - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeBmp - { - private Stream bmpStream; - private SDImage bmpDrawing; - private Image bmpCore; + private Stream bmpStream; + private SDImage bmpDrawing; + private Image bmpCore; - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.bmpStream == null) { - if (this.bmpStream == null) - { - this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car)); - this.bmpCore = Image.Load(this.bmpStream); - this.bmpStream.Position = 0; - this.bmpDrawing = SDImage.FromStream(this.bmpStream); - } + this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car)); + this.bmpCore = Image.Load(this.bmpStream); + this.bmpStream.Position = 0; + this.bmpDrawing = SDImage.FromStream(this.bmpStream); } + } - [GlobalCleanup] - public void Cleanup() - { - this.bmpStream.Dispose(); - this.bmpStream = null; - this.bmpCore.Dispose(); - this.bmpDrawing.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpStream = null; + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } - [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] - public void BmpSystemDrawing() - { - using var memoryStream = new MemoryStream(); - this.bmpDrawing.Save(memoryStream, ImageFormat.Bmp); - } + [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] + public void BmpSystemDrawing() + { + using var memoryStream = new MemoryStream(); + this.bmpDrawing.Save(memoryStream, ImageFormat.Bmp); + } - [Benchmark(Description = "ImageSharp Bmp")] - public void BmpImageSharp() - { - using var memoryStream = new MemoryStream(); - this.bmpCore.SaveAsBmp(memoryStream); - } + [Benchmark(Description = "ImageSharp Bmp")] + public void BmpImageSharp() + { + using var memoryStream = new MemoryStream(); + this.bmpCore.SaveAsBmp(memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs index a23cf878c7..50afea5a6d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs @@ -1,32 +1,30 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Bmp; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded - { - protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; + protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; - [Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] - public void EncodeBmpImageSharp() - => this.ForEachImageSharpImage((img, ms) => - { - img.Save(ms, new BmpEncoder()); - return null; - }); + [Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] + public void EncodeBmpImageSharp() + => this.ForEachImageSharpImage((img, ms) => + { + img.Save(ms, new BmpEncoder()); + return null; + }); - [Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")] - public void EncodeBmpSystemDrawing() - => this.ForEachSystemDrawingImage((img, ms) => - { - img.Save(ms, ImageFormat.Bmp); - return null; - }); - } + [Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")] + public void EncodeBmpSystemDrawing() + => this.ForEachSystemDrawingImage((img, ms) => + { + img.Save(ms, ImageFormat.Bmp); + return null; + }); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs index 53718a4212..21b193ddaf 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs @@ -1,49 +1,47 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; using SDSize = System.Drawing.Size; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeGif { - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeGif - { - private byte[] gifBytes; + private byte[] gifBytes; - private string TestImageFullPath - => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath + => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.gifBytes == null) { - if (this.gifBytes == null) - { - this.gifBytes = File.ReadAllBytes(this.TestImageFullPath); - } + this.gifBytes = File.ReadAllBytes(this.TestImageFullPath); } + } - [Params(TestImages.Gif.Rings)] - public string TestImage { get; set; } + [Params(TestImages.Gif.Rings)] + public string TestImage { get; set; } - [Benchmark(Baseline = true, Description = "System.Drawing Gif")] - public SDSize GifSystemDrawing() - { - using var memoryStream = new MemoryStream(this.gifBytes); - using var image = SDImage.FromStream(memoryStream); - return image.Size; - } + [Benchmark(Baseline = true, Description = "System.Drawing Gif")] + public SDSize GifSystemDrawing() + { + using var memoryStream = new MemoryStream(this.gifBytes); + using var image = SDImage.FromStream(memoryStream); + return image.Size; + } - [Benchmark(Description = "ImageSharp Gif")] - public Size GifImageSharp() - { - using var memoryStream = new MemoryStream(this.gifBytes); - using var image = Image.Load(memoryStream); - return new Size(image.Width, image.Height); - } + [Benchmark(Description = "ImageSharp Gif")] + public Size GifImageSharp() + { + using var memoryStream = new MemoryStream(this.gifBytes); + using var image = Image.Load(memoryStream); + return new Size(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs index 79d491b0ae..048c2aadda 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Drawing.Imaging; -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; @@ -11,58 +10,57 @@ using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeGif { - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeGif - { - // System.Drawing needs this. - private Stream bmpStream; - private SDImage bmpDrawing; - private Image bmpCore; + // System.Drawing needs this. + private Stream bmpStream; + private SDImage bmpDrawing; + private Image bmpCore; - // Try to get as close to System.Drawing's output as possible - private readonly GifEncoder encoder = new GifEncoder - { - Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) - }; + // Try to get as close to System.Drawing's output as possible + private readonly GifEncoder encoder = new GifEncoder + { + Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) + }; - [Params(TestImages.Bmp.Car, TestImages.Png.Rgb48Bpp)] - public string TestImage { get; set; } + [Params(TestImages.Bmp.Car, TestImages.Png.Rgb48Bpp)] + public string TestImage { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.bmpStream == null) { - if (this.bmpStream == null) - { - this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage)); - this.bmpCore = Image.Load(this.bmpStream); - this.bmpStream.Position = 0; - this.bmpDrawing = SDImage.FromStream(this.bmpStream); - } + this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage)); + this.bmpCore = Image.Load(this.bmpStream); + this.bmpStream.Position = 0; + this.bmpDrawing = SDImage.FromStream(this.bmpStream); } + } - [GlobalCleanup] - public void Cleanup() - { - this.bmpStream.Dispose(); - this.bmpStream = null; - this.bmpCore.Dispose(); - this.bmpDrawing.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpStream = null; + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } - [Benchmark(Baseline = true, Description = "System.Drawing Gif")] - public void GifSystemDrawing() - { - using var memoryStream = new MemoryStream(); - this.bmpDrawing.Save(memoryStream, ImageFormat.Gif); - } + [Benchmark(Baseline = true, Description = "System.Drawing Gif")] + public void GifSystemDrawing() + { + using var memoryStream = new MemoryStream(); + this.bmpDrawing.Save(memoryStream, ImageFormat.Gif); + } - [Benchmark(Description = "ImageSharp Gif")] - public void GifImageSharp() - { - using var memoryStream = new MemoryStream(); - this.bmpCore.SaveAsGif(memoryStream, this.encoder); - } + [Benchmark(Description = "ImageSharp Gif")] + public void GifImageSharp() + { + using var memoryStream = new MemoryStream(); + this.bmpCore.SaveAsGif(memoryStream, this.encoder); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs index 301bf9c8f0..c523b5c204 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs @@ -1,43 +1,41 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded - { - [Params(InputImageCategory.AllImages)] - public override InputImageCategory InputCategory { get; set; } + [Params(InputImageCategory.AllImages)] + public override InputImageCategory InputCategory { get; set; } - protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Gif/" }; + protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Gif/" }; - [Benchmark(Description = "EncodeGifMultiple - ImageSharp")] - public void EncodeGifImageSharp() - => this.ForEachImageSharpImage((img, ms) => + [Benchmark(Description = "EncodeGifMultiple - ImageSharp")] + public void EncodeGifImageSharp() + => this.ForEachImageSharpImage((img, ms) => + { + // Try to get as close to System.Drawing's output as possible + var options = new GifEncoder { - // Try to get as close to System.Drawing's output as possible - var options = new GifEncoder - { - Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) - }; + Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) + }; - img.Save(ms, options); - return null; - }); + img.Save(ms, options); + return null; + }); - [Benchmark(Baseline = true, Description = "EncodeGifMultiple - System.Drawing")] - public void EncodeGifSystemDrawing() - => this.ForEachSystemDrawingImage((img, ms) => - { - img.Save(ms, ImageFormat.Gif); - return null; - }); - } + [Benchmark(Baseline = true, Description = "EncodeGifMultiple - System.Drawing")] + public void EncodeGifSystemDrawing() + => this.ForEachSystemDrawingImage((img, ms) => + { + img.Save(ms, ImageFormat.Gif); + return null; + }); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_AddInPlace.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_AddInPlace.cs index af60ee8bc6..24e033cab1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_AddInPlace.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_AddInPlace.cs @@ -4,18 +4,17 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Block8x8F_AddInPlace { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Block8x8F_AddInPlace + [Benchmark] + public float AddInplace() { - [Benchmark] - public float AddInplace() - { - float f = 42F; - Block8x8F b = default; - b.AddInPlace(f); - return f; - } + float f = 42F; + Block8x8F b = default; + b.AddInPlace(f); + return f; } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs index ee196cbea2..f36f92e907 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -11,375 +10,374 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +public unsafe class Block8x8F_CopyTo1x1 { - public unsafe class Block8x8F_CopyTo1x1 - { - private Block8x8F block; - private readonly Block8x8F[] blockArray = new Block8x8F[1]; + private Block8x8F block; + private readonly Block8x8F[] blockArray = new Block8x8F[1]; - private static readonly int Width = 100; + private static readonly int Width = 100; - private float[] buffer = new float[Width * 500]; - private readonly float[] unpinnedBuffer = new float[Width * 500]; - private GCHandle bufferHandle; - private GCHandle blockHandle; - private float* bufferPtr; - private float* blockPtr; + private float[] buffer = new float[Width * 500]; + private readonly float[] unpinnedBuffer = new float[Width * 500]; + private GCHandle bufferHandle; + private GCHandle blockHandle; + private float* bufferPtr; + private float* blockPtr; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + if (!SimdUtils.HasVector8) { - if (!SimdUtils.HasVector8) - { - throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support."); - } + throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support."); + } - this.bufferHandle = GCHandle.Alloc(this.buffer, GCHandleType.Pinned); - this.bufferPtr = (float*)this.bufferHandle.AddrOfPinnedObject(); + this.bufferHandle = GCHandle.Alloc(this.buffer, GCHandleType.Pinned); + this.bufferPtr = (float*)this.bufferHandle.AddrOfPinnedObject(); - // Pin self so we can take address of to the block: - this.blockHandle = GCHandle.Alloc(this.blockArray, GCHandleType.Pinned); - this.blockPtr = (float*)Unsafe.AsPointer(ref this.block); - } + // Pin self so we can take address of to the block: + this.blockHandle = GCHandle.Alloc(this.blockArray, GCHandleType.Pinned); + this.blockPtr = (float*)Unsafe.AsPointer(ref this.block); + } - [GlobalCleanup] - public void Cleanup() - { - this.bufferPtr = null; - this.blockPtr = null; - this.bufferHandle.Free(); - this.blockHandle.Free(); - this.buffer = null; - } + [GlobalCleanup] + public void Cleanup() + { + this.bufferPtr = null; + this.blockPtr = null; + this.bufferHandle.Free(); + this.blockHandle.Free(); + this.buffer = null; + } - [Benchmark(Baseline = true)] - public void Original() - { - ref byte selfBase = ref Unsafe.As(ref this.block); - ref byte destBase = ref Unsafe.AsRef(this.bufferPtr); - int destStride = Width * sizeof(float); - - CopyRowImpl(ref selfBase, ref destBase, destStride, 0); - CopyRowImpl(ref selfBase, ref destBase, destStride, 1); - CopyRowImpl(ref selfBase, ref destBase, destStride, 2); - CopyRowImpl(ref selfBase, ref destBase, destStride, 3); - CopyRowImpl(ref selfBase, ref destBase, destStride, 4); - CopyRowImpl(ref selfBase, ref destBase, destStride, 5); - CopyRowImpl(ref selfBase, ref destBase, destStride, 6); - CopyRowImpl(ref selfBase, ref destBase, destStride, 7); - } + [Benchmark(Baseline = true)] + public void Original() + { + ref byte selfBase = ref Unsafe.As(ref this.block); + ref byte destBase = ref Unsafe.AsRef(this.bufferPtr); + int destStride = Width * sizeof(float); + + CopyRowImpl(ref selfBase, ref destBase, destStride, 0); + CopyRowImpl(ref selfBase, ref destBase, destStride, 1); + CopyRowImpl(ref selfBase, ref destBase, destStride, 2); + CopyRowImpl(ref selfBase, ref destBase, destStride, 3); + CopyRowImpl(ref selfBase, ref destBase, destStride, 4); + CopyRowImpl(ref selfBase, ref destBase, destStride, 5); + CopyRowImpl(ref selfBase, ref destBase, destStride, 6); + CopyRowImpl(ref selfBase, ref destBase, destStride, 7); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) - { - ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); - ref byte d = ref Unsafe.Add(ref destBase, row * destStride); - Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) + { + ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); + ref byte d = ref Unsafe.Add(ref destBase, row * destStride); + Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); + } - // [Benchmark] - public void UseVector8() - { - ref Block8x8F s = ref this.block; - ref float origin = ref Unsafe.AsRef(this.bufferPtr); - int stride = Width; - - ref Vector d0 = ref Unsafe.As>(ref origin); - ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); - ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2)); - ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3)); - ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4)); - ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5)); - ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6)); - ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7)); - - Vector row0 = Unsafe.As>(ref s.V0L); - Vector row1 = Unsafe.As>(ref s.V1L); - Vector row2 = Unsafe.As>(ref s.V2L); - Vector row3 = Unsafe.As>(ref s.V3L); - Vector row4 = Unsafe.As>(ref s.V4L); - Vector row5 = Unsafe.As>(ref s.V5L); - Vector row6 = Unsafe.As>(ref s.V6L); - Vector row7 = Unsafe.As>(ref s.V7L); - - d0 = row0; - d1 = row1; - d2 = row2; - d3 = row3; - d4 = row4; - d5 = row5; - d6 = row6; - d7 = row7; - } + // [Benchmark] + public void UseVector8() + { + ref Block8x8F s = ref this.block; + ref float origin = ref Unsafe.AsRef(this.bufferPtr); + int stride = Width; + + ref Vector d0 = ref Unsafe.As>(ref origin); + ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); + ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2)); + ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3)); + ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4)); + ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5)); + ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6)); + ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7)); + + Vector row0 = Unsafe.As>(ref s.V0L); + Vector row1 = Unsafe.As>(ref s.V1L); + Vector row2 = Unsafe.As>(ref s.V2L); + Vector row3 = Unsafe.As>(ref s.V3L); + Vector row4 = Unsafe.As>(ref s.V4L); + Vector row5 = Unsafe.As>(ref s.V5L); + Vector row6 = Unsafe.As>(ref s.V6L); + Vector row7 = Unsafe.As>(ref s.V7L); + + d0 = row0; + d1 = row1; + d2 = row2; + d3 = row3; + d4 = row4; + d5 = row5; + d6 = row6; + d7 = row7; + } - // [Benchmark] - public void UseVector8_V2() - { - ref Block8x8F s = ref this.block; - ref float origin = ref Unsafe.AsRef(this.bufferPtr); - int stride = Width; - - ref Vector d0 = ref Unsafe.As>(ref origin); - ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); - ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2)); - ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3)); - ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4)); - ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5)); - ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6)); - ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7)); - - d0 = Unsafe.As>(ref s.V0L); - d1 = Unsafe.As>(ref s.V1L); - d2 = Unsafe.As>(ref s.V2L); - d3 = Unsafe.As>(ref s.V3L); - d4 = Unsafe.As>(ref s.V4L); - d5 = Unsafe.As>(ref s.V5L); - d6 = Unsafe.As>(ref s.V6L); - d7 = Unsafe.As>(ref s.V7L); - } + // [Benchmark] + public void UseVector8_V2() + { + ref Block8x8F s = ref this.block; + ref float origin = ref Unsafe.AsRef(this.bufferPtr); + int stride = Width; + + ref Vector d0 = ref Unsafe.As>(ref origin); + ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); + ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2)); + ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3)); + ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4)); + ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5)); + ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6)); + ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7)); + + d0 = Unsafe.As>(ref s.V0L); + d1 = Unsafe.As>(ref s.V1L); + d2 = Unsafe.As>(ref s.V2L); + d3 = Unsafe.As>(ref s.V3L); + d4 = Unsafe.As>(ref s.V4L); + d5 = Unsafe.As>(ref s.V5L); + d6 = Unsafe.As>(ref s.V6L); + d7 = Unsafe.As>(ref s.V7L); + } - [Benchmark] - public void UseVector8_V3() - { - int stride = Width * sizeof(float); - ref float d = ref this.unpinnedBuffer[0]; - ref Vector s = ref Unsafe.As>(ref this.block); - - Vector v0 = s; - Vector v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1); - Vector v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2); - Vector v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3); - - Unsafe.As>(ref d) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 3))) = v3; - - v0 = Unsafe.AddByteOffset(ref s, (IntPtr)4); - v1 = Unsafe.AddByteOffset(ref s, (IntPtr)5); - v2 = Unsafe.AddByteOffset(ref s, (IntPtr)6); - v3 = Unsafe.AddByteOffset(ref s, (IntPtr)7); - - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3; - } + [Benchmark] + public void UseVector8_V3() + { + int stride = Width * sizeof(float); + ref float d = ref this.unpinnedBuffer[0]; + ref Vector s = ref Unsafe.As>(ref this.block); + + Vector v0 = s; + Vector v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1); + Vector v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2); + Vector v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3); + + Unsafe.As>(ref d) = v0; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 3))) = v3; + + v0 = Unsafe.AddByteOffset(ref s, (IntPtr)4); + v1 = Unsafe.AddByteOffset(ref s, (IntPtr)5); + v2 = Unsafe.AddByteOffset(ref s, (IntPtr)6); + v3 = Unsafe.AddByteOffset(ref s, (IntPtr)7); + + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3; + } - [Benchmark] - public void UseVector256_Avx2_Variant1() - { - int stride = Width; - float* d = this.bufferPtr; - float* s = this.blockPtr; - Vector256 v; + [Benchmark] + public void UseVector256_Avx2_Variant1() + { + int stride = Width; + float* d = this.bufferPtr; + float* s = this.blockPtr; + Vector256 v; - v = Avx.LoadVector256(s); - Avx.Store(d, v); + v = Avx.LoadVector256(s); + Avx.Store(d, v); - v = Avx.LoadVector256(s + 8); - Avx.Store(d + stride, v); + v = Avx.LoadVector256(s + 8); + Avx.Store(d + stride, v); - v = Avx.LoadVector256(s + (8 * 2)); - Avx.Store(d + (stride * 2), v); + v = Avx.LoadVector256(s + (8 * 2)); + Avx.Store(d + (stride * 2), v); - v = Avx.LoadVector256(s + (8 * 3)); - Avx.Store(d + (stride * 3), v); + v = Avx.LoadVector256(s + (8 * 3)); + Avx.Store(d + (stride * 3), v); - v = Avx.LoadVector256(s + (8 * 4)); - Avx.Store(d + (stride * 4), v); + v = Avx.LoadVector256(s + (8 * 4)); + Avx.Store(d + (stride * 4), v); - v = Avx.LoadVector256(s + (8 * 5)); - Avx.Store(d + (stride * 5), v); + v = Avx.LoadVector256(s + (8 * 5)); + Avx.Store(d + (stride * 5), v); - v = Avx.LoadVector256(s + (8 * 6)); - Avx.Store(d + (stride * 6), v); + v = Avx.LoadVector256(s + (8 * 6)); + Avx.Store(d + (stride * 6), v); - v = Avx.LoadVector256(s + (8 * 7)); - Avx.Store(d + (stride * 7), v); - } + v = Avx.LoadVector256(s + (8 * 7)); + Avx.Store(d + (stride * 7), v); + } - [Benchmark] - public void UseVector256_Avx2_Variant2() - { - int stride = Width; - float* d = this.bufferPtr; - float* s = this.blockPtr; - - Vector256 v0 = Avx.LoadVector256(s); - Vector256 v1 = Avx.LoadVector256(s + 8); - Vector256 v2 = Avx.LoadVector256(s + (8 * 2)); - Vector256 v3 = Avx.LoadVector256(s + (8 * 3)); - Vector256 v4 = Avx.LoadVector256(s + (8 * 4)); - Vector256 v5 = Avx.LoadVector256(s + (8 * 5)); - Vector256 v6 = Avx.LoadVector256(s + (8 * 6)); - Vector256 v7 = Avx.LoadVector256(s + (8 * 7)); - - Avx.Store(d, v0); - Avx.Store(d + stride, v1); - Avx.Store(d + (stride * 2), v2); - Avx.Store(d + (stride * 3), v3); - Avx.Store(d + (stride * 4), v4); - Avx.Store(d + (stride * 5), v5); - Avx.Store(d + (stride * 6), v6); - Avx.Store(d + (stride * 7), v7); - } + [Benchmark] + public void UseVector256_Avx2_Variant2() + { + int stride = Width; + float* d = this.bufferPtr; + float* s = this.blockPtr; + + Vector256 v0 = Avx.LoadVector256(s); + Vector256 v1 = Avx.LoadVector256(s + 8); + Vector256 v2 = Avx.LoadVector256(s + (8 * 2)); + Vector256 v3 = Avx.LoadVector256(s + (8 * 3)); + Vector256 v4 = Avx.LoadVector256(s + (8 * 4)); + Vector256 v5 = Avx.LoadVector256(s + (8 * 5)); + Vector256 v6 = Avx.LoadVector256(s + (8 * 6)); + Vector256 v7 = Avx.LoadVector256(s + (8 * 7)); + + Avx.Store(d, v0); + Avx.Store(d + stride, v1); + Avx.Store(d + (stride * 2), v2); + Avx.Store(d + (stride * 3), v3); + Avx.Store(d + (stride * 4), v4); + Avx.Store(d + (stride * 5), v5); + Avx.Store(d + (stride * 6), v6); + Avx.Store(d + (stride * 7), v7); + } - [Benchmark] - public void UseVector256_Avx2_Variant3() - { - int stride = Width; - float* d = this.bufferPtr; - float* s = this.blockPtr; - - Vector256 v0 = Avx.LoadVector256(s); - Vector256 v1 = Avx.LoadVector256(s + 8); - Vector256 v2 = Avx.LoadVector256(s + (8 * 2)); - Vector256 v3 = Avx.LoadVector256(s + (8 * 3)); - Avx.Store(d, v0); - Avx.Store(d + stride, v1); - Avx.Store(d + (stride * 2), v2); - Avx.Store(d + (stride * 3), v3); - - v0 = Avx.LoadVector256(s + (8 * 4)); - v1 = Avx.LoadVector256(s + (8 * 5)); - v2 = Avx.LoadVector256(s + (8 * 6)); - v3 = Avx.LoadVector256(s + (8 * 7)); - Avx.Store(d + (stride * 4), v0); - Avx.Store(d + (stride * 5), v1); - Avx.Store(d + (stride * 6), v2); - Avx.Store(d + (stride * 7), v3); - } + [Benchmark] + public void UseVector256_Avx2_Variant3() + { + int stride = Width; + float* d = this.bufferPtr; + float* s = this.blockPtr; + + Vector256 v0 = Avx.LoadVector256(s); + Vector256 v1 = Avx.LoadVector256(s + 8); + Vector256 v2 = Avx.LoadVector256(s + (8 * 2)); + Vector256 v3 = Avx.LoadVector256(s + (8 * 3)); + Avx.Store(d, v0); + Avx.Store(d + stride, v1); + Avx.Store(d + (stride * 2), v2); + Avx.Store(d + (stride * 3), v3); + + v0 = Avx.LoadVector256(s + (8 * 4)); + v1 = Avx.LoadVector256(s + (8 * 5)); + v2 = Avx.LoadVector256(s + (8 * 6)); + v3 = Avx.LoadVector256(s + (8 * 7)); + Avx.Store(d + (stride * 4), v0); + Avx.Store(d + (stride * 5), v1); + Avx.Store(d + (stride * 6), v2); + Avx.Store(d + (stride * 7), v3); + } - [Benchmark] - public void UseVector256_Avx2_Variant3_RefCast() - { - int stride = Width; - ref float d = ref this.unpinnedBuffer[0]; - ref Vector256 s = ref Unsafe.As>(ref this.block); - - Vector256 v0 = s; - Vector256 v1 = Unsafe.Add(ref s, 1); - Vector256 v2 = Unsafe.Add(ref s, 2); - Vector256 v3 = Unsafe.Add(ref s, 3); - - Unsafe.As>(ref d) = v0; - Unsafe.As>(ref Unsafe.Add(ref d, stride)) = v1; - Unsafe.As>(ref Unsafe.Add(ref d, stride * 2)) = v2; - Unsafe.As>(ref Unsafe.Add(ref d, stride * 3)) = v3; - - v0 = Unsafe.Add(ref s, 4); - v1 = Unsafe.Add(ref s, 5); - v2 = Unsafe.Add(ref s, 6); - v3 = Unsafe.Add(ref s, 7); - - Unsafe.As>(ref Unsafe.Add(ref d, stride * 4)) = v0; - Unsafe.As>(ref Unsafe.Add(ref d, stride * 5)) = v1; - Unsafe.As>(ref Unsafe.Add(ref d, stride * 6)) = v2; - Unsafe.As>(ref Unsafe.Add(ref d, stride * 7)) = v3; - } + [Benchmark] + public void UseVector256_Avx2_Variant3_RefCast() + { + int stride = Width; + ref float d = ref this.unpinnedBuffer[0]; + ref Vector256 s = ref Unsafe.As>(ref this.block); + + Vector256 v0 = s; + Vector256 v1 = Unsafe.Add(ref s, 1); + Vector256 v2 = Unsafe.Add(ref s, 2); + Vector256 v3 = Unsafe.Add(ref s, 3); + + Unsafe.As>(ref d) = v0; + Unsafe.As>(ref Unsafe.Add(ref d, stride)) = v1; + Unsafe.As>(ref Unsafe.Add(ref d, stride * 2)) = v2; + Unsafe.As>(ref Unsafe.Add(ref d, stride * 3)) = v3; + + v0 = Unsafe.Add(ref s, 4); + v1 = Unsafe.Add(ref s, 5); + v2 = Unsafe.Add(ref s, 6); + v3 = Unsafe.Add(ref s, 7); + + Unsafe.As>(ref Unsafe.Add(ref d, stride * 4)) = v0; + Unsafe.As>(ref Unsafe.Add(ref d, stride * 5)) = v1; + Unsafe.As>(ref Unsafe.Add(ref d, stride * 6)) = v2; + Unsafe.As>(ref Unsafe.Add(ref d, stride * 7)) = v3; + } - [Benchmark] - public void UseVector256_Avx2_Variant3_RefCast_Mod() - { - int stride = Width * sizeof(float); - ref float d = ref this.unpinnedBuffer[0]; - ref Vector256 s = ref Unsafe.As>(ref this.block); - - Vector256 v0 = s; - Vector256 v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1); - Vector256 v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2); - Vector256 v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3); - - Unsafe.As>(ref d) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 3))) = v3; - - v0 = Unsafe.AddByteOffset(ref s, (IntPtr)4); - v1 = Unsafe.AddByteOffset(ref s, (IntPtr)5); - v2 = Unsafe.AddByteOffset(ref s, (IntPtr)6); - v3 = Unsafe.AddByteOffset(ref s, (IntPtr)7); - - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3; - } + [Benchmark] + public void UseVector256_Avx2_Variant3_RefCast_Mod() + { + int stride = Width * sizeof(float); + ref float d = ref this.unpinnedBuffer[0]; + ref Vector256 s = ref Unsafe.As>(ref this.block); + + Vector256 v0 = s; + Vector256 v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1); + Vector256 v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2); + Vector256 v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3); + + Unsafe.As>(ref d) = v0; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 3))) = v3; + + v0 = Unsafe.AddByteOffset(ref s, (IntPtr)4); + v1 = Unsafe.AddByteOffset(ref s, (IntPtr)5); + v2 = Unsafe.AddByteOffset(ref s, (IntPtr)6); + v3 = Unsafe.AddByteOffset(ref s, (IntPtr)7); + + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3; + } - // [Benchmark] - public void UseVector256_Avx2_Variant3_WithLocalPinning() + // [Benchmark] + public void UseVector256_Avx2_Variant3_WithLocalPinning() + { + int stride = Width; + fixed (float* d = this.unpinnedBuffer) { - int stride = Width; - fixed (float* d = this.unpinnedBuffer) + fixed (Block8x8F* ss = &this.block) { - fixed (Block8x8F* ss = &this.block) - { - float* s = (float*)ss; - Vector256 v0 = Avx.LoadVector256(s); - Vector256 v1 = Avx.LoadVector256(s + 8); - Vector256 v2 = Avx.LoadVector256(s + (8 * 2)); - Vector256 v3 = Avx.LoadVector256(s + (8 * 3)); - Avx.Store(d, v0); - Avx.Store(d + stride, v1); - Avx.Store(d + (stride * 2), v2); - Avx.Store(d + (stride * 3), v3); - - v0 = Avx.LoadVector256(s + (8 * 4)); - v1 = Avx.LoadVector256(s + (8 * 5)); - v2 = Avx.LoadVector256(s + (8 * 6)); - v3 = Avx.LoadVector256(s + (8 * 7)); - Avx.Store(d + (stride * 4), v0); - Avx.Store(d + (stride * 5), v1); - Avx.Store(d + (stride * 6), v2); - Avx.Store(d + (stride * 7), v3); - } + float* s = (float*)ss; + Vector256 v0 = Avx.LoadVector256(s); + Vector256 v1 = Avx.LoadVector256(s + 8); + Vector256 v2 = Avx.LoadVector256(s + (8 * 2)); + Vector256 v3 = Avx.LoadVector256(s + (8 * 3)); + Avx.Store(d, v0); + Avx.Store(d + stride, v1); + Avx.Store(d + (stride * 2), v2); + Avx.Store(d + (stride * 3), v3); + + v0 = Avx.LoadVector256(s + (8 * 4)); + v1 = Avx.LoadVector256(s + (8 * 5)); + v2 = Avx.LoadVector256(s + (8 * 6)); + v3 = Avx.LoadVector256(s + (8 * 7)); + Avx.Store(d + (stride * 4), v0); + Avx.Store(d + (stride * 5), v1); + Avx.Store(d + (stride * 6), v2); + Avx.Store(d + (stride * 7), v3); } } + } - // [Benchmark] - public void UseVector256_Avx2_Variant3_sbyte() - { - int stride = Width * 4; - sbyte* d = (sbyte*)this.bufferPtr; - sbyte* s = (sbyte*)this.blockPtr; - - Vector256 v0 = Avx.LoadVector256(s); - Vector256 v1 = Avx.LoadVector256(s + 32); - Vector256 v2 = Avx.LoadVector256(s + (32 * 2)); - Vector256 v3 = Avx.LoadVector256(s + (32 * 3)); - Avx.Store(d, v0); - Avx.Store(d + stride, v1); - Avx.Store(d + (stride * 2), v2); - Avx.Store(d + (stride * 3), v3); - - v0 = Avx.LoadVector256(s + (32 * 4)); - v1 = Avx.LoadVector256(s + (32 * 5)); - v2 = Avx.LoadVector256(s + (32 * 6)); - v3 = Avx.LoadVector256(s + (32 * 7)); - Avx.Store(d + (stride * 4), v0); - Avx.Store(d + (stride * 5), v1); - Avx.Store(d + (stride * 6), v2); - Avx.Store(d + (stride * 7), v3); - } - - // *** RESULTS 02/2020 *** - // BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.200-preview-014971 - // [Host] : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT - // DefaultJob : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT - // - // - // | Method | Mean | Error | StdDev | Ratio | RatioSD | - // |--------------------------------------- |---------:|----------:|----------:|------:|--------:| - // | Original | 4.012 ns | 0.0567 ns | 0.0531 ns | 1.00 | 0.00 | - // | UseVector8_V3 | 4.013 ns | 0.0947 ns | 0.0840 ns | 1.00 | 0.03 | - // | UseVector256_Avx2_Variant1 | 2.546 ns | 0.0376 ns | 0.0314 ns | 0.63 | 0.01 | - // | UseVector256_Avx2_Variant2 | 2.643 ns | 0.0162 ns | 0.0151 ns | 0.66 | 0.01 | - // | UseVector256_Avx2_Variant3 | 2.520 ns | 0.0760 ns | 0.0813 ns | 0.63 | 0.02 | - // | UseVector256_Avx2_Variant3_RefCast | 2.300 ns | 0.0877 ns | 0.0938 ns | 0.58 | 0.03 | - // | UseVector256_Avx2_Variant3_RefCast_Mod | 2.139 ns | 0.0698 ns | 0.0686 ns | 0.53 | 0.02 | + // [Benchmark] + public void UseVector256_Avx2_Variant3_sbyte() + { + int stride = Width * 4; + sbyte* d = (sbyte*)this.bufferPtr; + sbyte* s = (sbyte*)this.blockPtr; + + Vector256 v0 = Avx.LoadVector256(s); + Vector256 v1 = Avx.LoadVector256(s + 32); + Vector256 v2 = Avx.LoadVector256(s + (32 * 2)); + Vector256 v3 = Avx.LoadVector256(s + (32 * 3)); + Avx.Store(d, v0); + Avx.Store(d + stride, v1); + Avx.Store(d + (stride * 2), v2); + Avx.Store(d + (stride * 3), v3); + + v0 = Avx.LoadVector256(s + (32 * 4)); + v1 = Avx.LoadVector256(s + (32 * 5)); + v2 = Avx.LoadVector256(s + (32 * 6)); + v3 = Avx.LoadVector256(s + (32 * 7)); + Avx.Store(d + (stride * 4), v0); + Avx.Store(d + (stride * 5), v1); + Avx.Store(d + (stride * 6), v2); + Avx.Store(d + (stride * 7), v3); } + + // *** RESULTS 02/2020 *** + // BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 + // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores + // .NET Core SDK=3.1.200-preview-014971 + // [Host] : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT + // DefaultJob : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT + // + // + // | Method | Mean | Error | StdDev | Ratio | RatioSD | + // |--------------------------------------- |---------:|----------:|----------:|------:|--------:| + // | Original | 4.012 ns | 0.0567 ns | 0.0531 ns | 1.00 | 0.00 | + // | UseVector8_V3 | 4.013 ns | 0.0947 ns | 0.0840 ns | 1.00 | 0.03 | + // | UseVector256_Avx2_Variant1 | 2.546 ns | 0.0376 ns | 0.0314 ns | 0.63 | 0.01 | + // | UseVector256_Avx2_Variant2 | 2.643 ns | 0.0162 ns | 0.0151 ns | 0.66 | 0.01 | + // | UseVector256_Avx2_Variant3 | 2.520 ns | 0.0760 ns | 0.0813 ns | 0.63 | 0.02 | + // | UseVector256_Avx2_Variant3_RefCast | 2.300 ns | 0.0877 ns | 0.0938 ns | 0.58 | 0.03 | + // | UseVector256_Avx2_Variant3_RefCast_Mod | 2.139 ns | 0.0698 ns | 0.0686 ns | 0.53 | 0.02 | } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs index 473cea8d01..88c0098230 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -9,402 +9,401 @@ using SixLabors.ImageSharp.Memory; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +public class Block8x8F_CopyTo2x2 { - public class Block8x8F_CopyTo2x2 + private Block8x8F block; + + private Buffer2D buffer; + + private Buffer2DRegion destRegion; + + [GlobalSetup] + public void Setup() { - private Block8x8F block; + this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500); + this.destRegion = this.buffer.GetRegion(200, 100, 128, 128); + } - private Buffer2D buffer; + [Benchmark(Baseline = true)] + public void Original() + { + ref float destBase = ref this.destRegion.GetReferenceToOrigin(); + int destStride = this.destRegion.Stride; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 7, destStride); + } - private Buffer2DRegion destRegion; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; + Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + } - [GlobalSetup] - public void Setup() - { - this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500); - this.destRegion = this.buffer.GetRegion(200, 100, 128, 128); - } + [Benchmark] + public void Original_V2() + { + ref float destBase = ref this.destRegion.GetReferenceToOrigin(); + int destStride = this.destRegion.Stride; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride); + } - [Benchmark(Baseline = true)] - public void Original() - { - ref float destBase = ref this.destRegion.GetReferenceToOrigin(); - int destStride = this.destRegion.Stride; - - ref Block8x8F src = ref this.block; - - WidenCopyImpl2x2(ref src, ref destBase, 0, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 1, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 2, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 3, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 4, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 5, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 6, destStride); - WidenCopyImpl2x2(ref src, ref destBase, 7, destStride); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride) - { - ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); - ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); - ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); - - Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; - Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; - Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; - Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; - Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; - Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; - Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; - Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; - } - - [Benchmark] - public void Original_V2() - { - ref float destBase = ref this.destRegion.GetReferenceToOrigin(); - int destStride = this.destRegion.Stride; - - ref Block8x8F src = ref this.block; - - WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride); - WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride) - { - ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); - ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); - ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride); - - Unsafe.Add(ref dest0, 0) = selfLeft.X; - Unsafe.Add(ref dest0, 1) = selfLeft.X; - Unsafe.Add(ref dest0, 2) = selfLeft.Y; - Unsafe.Add(ref dest0, 3) = selfLeft.Y; - Unsafe.Add(ref dest0, 4) = selfLeft.Z; - Unsafe.Add(ref dest0, 5) = selfLeft.Z; - Unsafe.Add(ref dest0, 6) = selfLeft.W; - Unsafe.Add(ref dest0, 7) = selfLeft.W; - - ref float dest1 = ref Unsafe.Add(ref dest0, 8); - - Unsafe.Add(ref dest1, 0) = selfRight.X; - Unsafe.Add(ref dest1, 1) = selfRight.X; - Unsafe.Add(ref dest1, 2) = selfRight.Y; - Unsafe.Add(ref dest1, 3) = selfRight.Y; - Unsafe.Add(ref dest1, 4) = selfRight.Z; - Unsafe.Add(ref dest1, 5) = selfRight.Z; - Unsafe.Add(ref dest1, 6) = selfRight.W; - Unsafe.Add(ref dest1, 7) = selfRight.W; - - ref float dest2 = ref Unsafe.Add(ref dest0, destStride); - - Unsafe.Add(ref dest2, 0) = selfLeft.X; - Unsafe.Add(ref dest2, 1) = selfLeft.X; - Unsafe.Add(ref dest2, 2) = selfLeft.Y; - Unsafe.Add(ref dest2, 3) = selfLeft.Y; - Unsafe.Add(ref dest2, 4) = selfLeft.Z; - Unsafe.Add(ref dest2, 5) = selfLeft.Z; - Unsafe.Add(ref dest2, 6) = selfLeft.W; - Unsafe.Add(ref dest2, 7) = selfLeft.W; - - ref float dest3 = ref Unsafe.Add(ref dest2, 8); - - Unsafe.Add(ref dest3, 0) = selfRight.X; - Unsafe.Add(ref dest3, 1) = selfRight.X; - Unsafe.Add(ref dest3, 2) = selfRight.Y; - Unsafe.Add(ref dest3, 3) = selfRight.Y; - Unsafe.Add(ref dest3, 4) = selfRight.Z; - Unsafe.Add(ref dest3, 5) = selfRight.Z; - Unsafe.Add(ref dest3, 6) = selfRight.W; - Unsafe.Add(ref dest3, 7) = selfRight.W; - } - - [Benchmark] - public void UseVector2() - { - ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); - int destStride = this.destRegion.Stride / 2; - - ref Block8x8F src = ref this.block; - - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride); - WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) - { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); - ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); - ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); - ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); - - var xLeft = new Vector2(sLeft.X); - var yLeft = new Vector2(sLeft.Y); - var zLeft = new Vector2(sLeft.Z); - var wLeft = new Vector2(sLeft.W); - - var xRight = new Vector2(sRight.X); - var yRight = new Vector2(sRight.Y); - var zRight = new Vector2(sRight.Z); - var wRight = new Vector2(sRight.W); - - dTopLeft = xLeft; - Unsafe.Add(ref dTopLeft, 1) = yLeft; - Unsafe.Add(ref dTopLeft, 2) = zLeft; - Unsafe.Add(ref dTopLeft, 3) = wLeft; - - dTopRight = xRight; - Unsafe.Add(ref dTopRight, 1) = yRight; - Unsafe.Add(ref dTopRight, 2) = zRight; - Unsafe.Add(ref dTopRight, 3) = wRight; - - dBottomLeft = xLeft; - Unsafe.Add(ref dBottomLeft, 1) = yLeft; - Unsafe.Add(ref dBottomLeft, 2) = zLeft; - Unsafe.Add(ref dBottomLeft, 3) = wLeft; - - dBottomRight = xRight; - Unsafe.Add(ref dBottomRight, 1) = yRight; - Unsafe.Add(ref dBottomRight, 2) = zRight; - Unsafe.Add(ref dBottomRight, 3) = wRight; - } - - [Benchmark] - public void UseVector4() - { - ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); - int destStride = this.destRegion.Stride / 2; - - ref Block8x8F src = ref this.block; - - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride); - WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) - { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); - ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); - ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); - ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); - - var xLeft = new Vector4(sLeft.X); - var yLeft = new Vector4(sLeft.Y); - var zLeft = new Vector4(sLeft.Z); - var wLeft = new Vector4(sLeft.W); - - var xRight = new Vector4(sRight.X); - var yRight = new Vector4(sRight.Y); - var zRight = new Vector4(sRight.Z); - var wRight = new Vector4(sRight.W); - - Unsafe.As(ref dTopLeft) = xLeft; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; - - Unsafe.As(ref dTopRight) = xRight; - Unsafe.As(ref Unsafe.Add(ref dTopRight, 1)) = yRight; - Unsafe.As(ref Unsafe.Add(ref dTopRight, 2)) = zRight; - Unsafe.As(ref Unsafe.Add(ref dTopRight, 3)) = wRight; - - Unsafe.As(ref dBottomLeft) = xLeft; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; - - Unsafe.As(ref dBottomRight) = xRight; - Unsafe.As(ref Unsafe.Add(ref dBottomRight, 1)) = yRight; - Unsafe.As(ref Unsafe.Add(ref dBottomRight, 2)) = zRight; - Unsafe.As(ref Unsafe.Add(ref dBottomRight, 3)) = wRight; - } - - [Benchmark] - public void UseVector4_SafeRightCorner() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref dest0, 0) = selfLeft.X; + Unsafe.Add(ref dest0, 1) = selfLeft.X; + Unsafe.Add(ref dest0, 2) = selfLeft.Y; + Unsafe.Add(ref dest0, 3) = selfLeft.Y; + Unsafe.Add(ref dest0, 4) = selfLeft.Z; + Unsafe.Add(ref dest0, 5) = selfLeft.Z; + Unsafe.Add(ref dest0, 6) = selfLeft.W; + Unsafe.Add(ref dest0, 7) = selfLeft.W; + + ref float dest1 = ref Unsafe.Add(ref dest0, 8); + + Unsafe.Add(ref dest1, 0) = selfRight.X; + Unsafe.Add(ref dest1, 1) = selfRight.X; + Unsafe.Add(ref dest1, 2) = selfRight.Y; + Unsafe.Add(ref dest1, 3) = selfRight.Y; + Unsafe.Add(ref dest1, 4) = selfRight.Z; + Unsafe.Add(ref dest1, 5) = selfRight.Z; + Unsafe.Add(ref dest1, 6) = selfRight.W; + Unsafe.Add(ref dest1, 7) = selfRight.W; + + ref float dest2 = ref Unsafe.Add(ref dest0, destStride); + + Unsafe.Add(ref dest2, 0) = selfLeft.X; + Unsafe.Add(ref dest2, 1) = selfLeft.X; + Unsafe.Add(ref dest2, 2) = selfLeft.Y; + Unsafe.Add(ref dest2, 3) = selfLeft.Y; + Unsafe.Add(ref dest2, 4) = selfLeft.Z; + Unsafe.Add(ref dest2, 5) = selfLeft.Z; + Unsafe.Add(ref dest2, 6) = selfLeft.W; + Unsafe.Add(ref dest2, 7) = selfLeft.W; + + ref float dest3 = ref Unsafe.Add(ref dest2, 8); + + Unsafe.Add(ref dest3, 0) = selfRight.X; + Unsafe.Add(ref dest3, 1) = selfRight.X; + Unsafe.Add(ref dest3, 2) = selfRight.Y; + Unsafe.Add(ref dest3, 3) = selfRight.Y; + Unsafe.Add(ref dest3, 4) = selfRight.Z; + Unsafe.Add(ref dest3, 5) = selfRight.Z; + Unsafe.Add(ref dest3, 6) = selfRight.W; + Unsafe.Add(ref dest3, 7) = selfRight.W; + } + + [Benchmark] + public void UseVector2() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); + int destStride = this.destRegion.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); + + var xLeft = new Vector2(sLeft.X); + var yLeft = new Vector2(sLeft.Y); + var zLeft = new Vector2(sLeft.Z); + var wLeft = new Vector2(sLeft.W); + + var xRight = new Vector2(sRight.X); + var yRight = new Vector2(sRight.Y); + var zRight = new Vector2(sRight.Z); + var wRight = new Vector2(sRight.W); + + dTopLeft = xLeft; + Unsafe.Add(ref dTopLeft, 1) = yLeft; + Unsafe.Add(ref dTopLeft, 2) = zLeft; + Unsafe.Add(ref dTopLeft, 3) = wLeft; + + dTopRight = xRight; + Unsafe.Add(ref dTopRight, 1) = yRight; + Unsafe.Add(ref dTopRight, 2) = zRight; + Unsafe.Add(ref dTopRight, 3) = wRight; + + dBottomLeft = xLeft; + Unsafe.Add(ref dBottomLeft, 1) = yLeft; + Unsafe.Add(ref dBottomLeft, 2) = zLeft; + Unsafe.Add(ref dBottomLeft, 3) = wLeft; + + dBottomRight = xRight; + Unsafe.Add(ref dBottomRight, 1) = yRight; + Unsafe.Add(ref dBottomRight, 2) = zRight; + Unsafe.Add(ref dBottomRight, 3) = wRight; + } + + [Benchmark] + public void UseVector4() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); + int destStride = this.destRegion.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); + + var xLeft = new Vector4(sLeft.X); + var yLeft = new Vector4(sLeft.Y); + var zLeft = new Vector4(sLeft.Z); + var wLeft = new Vector4(sLeft.W); + + var xRight = new Vector4(sRight.X); + var yRight = new Vector4(sRight.Y); + var zRight = new Vector4(sRight.Z); + var wRight = new Vector4(sRight.W); + + Unsafe.As(ref dTopLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; + + Unsafe.As(ref dTopRight) = xRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 1)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 2)) = zRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 3)) = wRight; + + Unsafe.As(ref dBottomLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; + + Unsafe.As(ref dBottomRight) = xRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 1)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 2)) = zRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 3)) = wRight; + } + + [Benchmark] + public void UseVector4_SafeRightCorner() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); + int destStride = this.destRegion.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + + var xLeft = new Vector4(sLeft.X); + var yLeft = new Vector4(sLeft.Y); + var zLeft = new Vector4(sLeft.Z); + var wLeft = new Vector4(sLeft.W); + + var xRight = new Vector4(sRight.X); + var yRight = new Vector4(sRight.Y); + var zRight = new Vector4(sRight.Z); + var wRight = new Vector2(sRight.W); + + Unsafe.As(ref dTopLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; + + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 4)) = xRight; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 5)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 6)) = zRight; + Unsafe.Add(ref dTopLeft, 7) = wRight; + + Unsafe.As(ref dBottomLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; + + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight; + Unsafe.Add(ref dBottomLeft, 7) = wRight; + } + + [Benchmark] + public void UseVector4_V2() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); + int destStride = this.destRegion.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + int offset = 2 * row * destStride; + ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); + ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); + + var xyLeft = new Vector4(sLeft.X) { - ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); - int destStride = this.destRegion.Stride / 2; - - ref Block8x8F src = ref this.block; - - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride); - WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + Z = sLeft.Y, + W = sLeft.Y + }; + + var zwLeft = new Vector4(sLeft.Z) { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); - ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); - - var xLeft = new Vector4(sLeft.X); - var yLeft = new Vector4(sLeft.Y); - var zLeft = new Vector4(sLeft.Z); - var wLeft = new Vector4(sLeft.W); - - var xRight = new Vector4(sRight.X); - var yRight = new Vector4(sRight.Y); - var zRight = new Vector4(sRight.Z); - var wRight = new Vector2(sRight.W); - - Unsafe.As(ref dTopLeft) = xLeft; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; - - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 4)) = xRight; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 5)) = yRight; - Unsafe.As(ref Unsafe.Add(ref dTopLeft, 6)) = zRight; - Unsafe.Add(ref dTopLeft, 7) = wRight; - - Unsafe.As(ref dBottomLeft) = xLeft; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; - - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight; - Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight; - Unsafe.Add(ref dBottomLeft, 7) = wRight; - } - - [Benchmark] - public void UseVector4_V2() + Z = sLeft.W, + W = sLeft.W + }; + + var xyRight = new Vector4(sRight.X) { - ref Vector2 destBase = ref Unsafe.As(ref this.destRegion.GetReferenceToOrigin()); - int destStride = this.destRegion.Stride / 2; - - ref Block8x8F src = ref this.block; - - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride); - WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + Z = sRight.Y, + W = sRight.Y + }; + + var zwRight = new Vector4(sRight.Z) { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); - ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - - int offset = 2 * row * destStride; - ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); - ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); - - var xyLeft = new Vector4(sLeft.X) - { - Z = sLeft.Y, - W = sLeft.Y - }; - - var zwLeft = new Vector4(sLeft.Z) - { - Z = sLeft.W, - W = sLeft.W - }; - - var xyRight = new Vector4(sRight.X) - { - Z = sRight.Y, - W = sRight.Y - }; - - var zwRight = new Vector4(sRight.Z) - { - Z = sRight.W, - W = sRight.W - }; - - dTopLeft = xyLeft; - Unsafe.Add(ref dTopLeft, 1) = zwLeft; - Unsafe.Add(ref dTopLeft, 2) = xyRight; - Unsafe.Add(ref dTopLeft, 3) = zwRight; - - dBottomLeft = xyLeft; - Unsafe.Add(ref dBottomLeft, 1) = zwLeft; - Unsafe.Add(ref dBottomLeft, 2) = xyRight; - Unsafe.Add(ref dBottomLeft, 3) = zwRight; - } - - // RESULTS: - // Method | Mean | Error | StdDev | Scaled | ScaledSD | - // --------------------------- |---------:|----------:|----------:|-------:|---------:| - // Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 | - // Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 | - // UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 | - // UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 | - // UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 | - // UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 | + Z = sRight.W, + W = sRight.W + }; + + dTopLeft = xyLeft; + Unsafe.Add(ref dTopLeft, 1) = zwLeft; + Unsafe.Add(ref dTopLeft, 2) = xyRight; + Unsafe.Add(ref dTopLeft, 3) = zwRight; + + dBottomLeft = xyLeft; + Unsafe.Add(ref dBottomLeft, 1) = zwLeft; + Unsafe.Add(ref dBottomLeft, 2) = xyRight; + Unsafe.Add(ref dBottomLeft, 3) = zwRight; } + + // RESULTS: + // Method | Mean | Error | StdDev | Scaled | ScaledSD | + // --------------------------- |---------:|----------:|----------:|-------:|---------:| + // Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 | + // Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 | + // UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 | + // UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 | + // UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 | + // UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 | } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs index 531eca1f7d..efc347586c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs @@ -8,153 +8,152 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +/// +/// The goal of this benchmark is to measure the following Jpeg-related scenario: +/// - Take 2 blocks of float-s +/// - Divide each float pair, round the result +/// - Iterate through all rounded values as int-s +/// +public unsafe class Block8x8F_DivideRound { - /// - /// The goal of this benchmark is to measure the following Jpeg-related scenario: - /// - Take 2 blocks of float-s - /// - Divide each float pair, round the result - /// - Iterate through all rounded values as int-s - /// - public unsafe class Block8x8F_DivideRound - { - private const int ExecutionCount = 5; // Added this to reduce the effect of copying the blocks - private static readonly Vector4 MinusOne = new Vector4(-1); - private static readonly Vector4 Half = new Vector4(0.5f); + private const int ExecutionCount = 5; // Added this to reduce the effect of copying the blocks + private static readonly Vector4 MinusOne = new Vector4(-1); + private static readonly Vector4 Half = new Vector4(0.5f); - private Block8x8F inputDividend; - private Block8x8F inputDivisor; + private Block8x8F inputDividend; + private Block8x8F inputDivisor; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + for (int i = 0; i < Block8x8F.Size; i++) { - for (int i = 0; i < Block8x8F.Size; i++) - { - this.inputDividend[i] = i * 44.8f; - this.inputDivisor[i] = 100 - i; - } + this.inputDividend[i] = i * 44.8f; + this.inputDivisor[i] = 100 - i; } + } - [Benchmark(Baseline = true)] - public int ByRationalIntegers() - { - int sum = 0; + [Benchmark(Baseline = true)] + public int ByRationalIntegers() + { + int sum = 0; - Block8x8F b1 = this.inputDividend; - Block8x8F b2 = this.inputDivisor; - float* pDividend = (float*)&b1; - float* pDivisor = (float*)&b2; + Block8x8F b1 = this.inputDividend; + Block8x8F b2 = this.inputDivisor; + float* pDividend = (float*)&b1; + float* pDivisor = (float*)&b2; - int* result = stackalloc int[Block8x8F.Size]; + int* result = stackalloc int[Block8x8F.Size]; - for (int cnt = 0; cnt < ExecutionCount; cnt++) + for (int cnt = 0; cnt < ExecutionCount; cnt++) + { + sum = 0; + for (int i = 0; i < Block8x8F.Size; i++) { - sum = 0; - for (int i = 0; i < Block8x8F.Size; i++) - { - int a = (int)pDividend[i]; - int b = (int)pDivisor; - result[i] = RationalRound(a, b); - } - - for (int i = 0; i < Block8x8F.Size; i++) - { - sum += result[i]; - } + int a = (int)pDividend[i]; + int b = (int)pDivisor; + result[i] = RationalRound(a, b); } - return sum; + for (int i = 0; i < Block8x8F.Size; i++) + { + sum += result[i]; + } } - [Benchmark] - public int BySystemMathRound() - { - int sum = 0; + return sum; + } + + [Benchmark] + public int BySystemMathRound() + { + int sum = 0; - Block8x8F b1 = this.inputDividend; - Block8x8F b2 = this.inputDivisor; - float* pDividend = (float*)&b1; - float* pDivisor = (float*)&b2; + Block8x8F b1 = this.inputDividend; + Block8x8F b2 = this.inputDivisor; + float* pDividend = (float*)&b1; + float* pDivisor = (float*)&b2; - for (int cnt = 0; cnt < ExecutionCount; cnt++) + for (int cnt = 0; cnt < ExecutionCount; cnt++) + { + sum = 0; + for (int i = 0; i < Block8x8F.Size; i++) { - sum = 0; - for (int i = 0; i < Block8x8F.Size; i++) - { - double value = pDividend[i] / pDivisor[i]; - pDividend[i] = (float)System.Math.Round(value); - } - - for (int i = 0; i < Block8x8F.Size; i++) - { - sum += (int)pDividend[i]; - } + double value = pDividend[i] / pDivisor[i]; + pDividend[i] = (float)System.Math.Round(value); } - return sum; + for (int i = 0; i < Block8x8F.Size; i++) + { + sum += (int)pDividend[i]; + } } - [Benchmark] - public int BySimdMagic() - { - int sum = 0; + return sum; + } + + [Benchmark] + public int BySimdMagic() + { + int sum = 0; - Block8x8F bDividend = this.inputDividend; - Block8x8F bDivisor = this.inputDivisor; - float* pDividend = (float*)&bDividend; + Block8x8F bDividend = this.inputDividend; + Block8x8F bDivisor = this.inputDivisor; + float* pDividend = (float*)&bDividend; - for (int cnt = 0; cnt < ExecutionCount; cnt++) + for (int cnt = 0; cnt < ExecutionCount; cnt++) + { + sum = 0; + DivideRoundAll(ref bDividend, ref bDivisor); + for (int i = 0; i < Block8x8F.Size; i++) { - sum = 0; - DivideRoundAll(ref bDividend, ref bDivisor); - for (int i = 0; i < Block8x8F.Size; i++) - { - sum += (int)pDividend[i]; - } + sum += (int)pDividend[i]; } - - return sum; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) - { - a.V0L = DivideRound(a.V0L, b.V0L); - a.V0R = DivideRound(a.V0R, b.V0R); - a.V1L = DivideRound(a.V1L, b.V1L); - a.V1R = DivideRound(a.V1R, b.V1R); - a.V2L = DivideRound(a.V2L, b.V2L); - a.V2R = DivideRound(a.V2R, b.V2R); - a.V3L = DivideRound(a.V3L, b.V3L); - a.V3R = DivideRound(a.V3R, b.V3R); - a.V4L = DivideRound(a.V4L, b.V4L); - a.V4R = DivideRound(a.V4R, b.V4R); - a.V5L = DivideRound(a.V5L, b.V5L); - a.V5R = DivideRound(a.V5R, b.V5R); - a.V6L = DivideRound(a.V6L, b.V6L); - a.V6R = DivideRound(a.V6R, b.V6R); - a.V7L = DivideRound(a.V7L, b.V7L); - a.V7R = DivideRound(a.V7R, b.V7R); - } + return sum; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) - { - var sign = Vector4.Min(dividend, Vector4.One); - sign = Vector4.Max(sign, MinusOne); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) + { + a.V0L = DivideRound(a.V0L, b.V0L); + a.V0R = DivideRound(a.V0R, b.V0R); + a.V1L = DivideRound(a.V1L, b.V1L); + a.V1R = DivideRound(a.V1R, b.V1R); + a.V2L = DivideRound(a.V2L, b.V2L); + a.V2R = DivideRound(a.V2R, b.V2R); + a.V3L = DivideRound(a.V3L, b.V3L); + a.V3R = DivideRound(a.V3R, b.V3R); + a.V4L = DivideRound(a.V4L, b.V4L); + a.V4R = DivideRound(a.V4R, b.V4R); + a.V5L = DivideRound(a.V5L, b.V5L); + a.V5R = DivideRound(a.V5R, b.V5R); + a.V6L = DivideRound(a.V6L, b.V6L); + a.V6R = DivideRound(a.V6R, b.V6R); + a.V7L = DivideRound(a.V7L, b.V7L); + a.V7R = DivideRound(a.V7R, b.V7R); + } - return (dividend / divisor) + (sign * Half); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) + { + var sign = Vector4.Min(dividend, Vector4.One); + sign = Vector4.Max(sign, MinusOne); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RationalRound(int dividend, int divisor) - { - if (dividend >= 0) - { - return (dividend + (divisor >> 1)) / divisor; - } + return (dividend / divisor) + (sign * Half); + } - return -((-dividend + (divisor >> 1)) / divisor); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RationalRound(int dividend, int divisor) + { + if (dividend >= 0) + { + return (dividend + (divisor >> 1)) / divisor; } + + return -((-dividend + (divisor >> 1)) / divisor); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs index 18a76c652d..91255c9466 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs @@ -1,51 +1,49 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +public class Block8x8F_LoadFromInt16 { - public class Block8x8F_LoadFromInt16 - { - private Block8x8 source; + private Block8x8 source; - private Block8x8F dest = default; + private Block8x8F dest = default; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + if (Vector.Count != 8) { - if (Vector.Count != 8) - { - throw new NotSupportedException("Vector.Count != 8"); - } - - for (short i = 0; i < Block8x8F.Size; i++) - { - this.source[i] = i; - } + throw new NotSupportedException("Vector.Count != 8"); } - [Benchmark(Baseline = true)] - public void Scalar() + for (short i = 0; i < Block8x8F.Size; i++) { - this.dest.LoadFromInt16Scalar(ref this.source); + this.source[i] = i; } + } - [Benchmark] - public void ExtendedAvx2() - { - this.dest.LoadFromInt16ExtendedAvx2(ref this.source); - } + [Benchmark(Baseline = true)] + public void Scalar() + { + this.dest.LoadFromInt16Scalar(ref this.source); + } - // RESULT: - // Method | Mean | Error | StdDev | Scaled | - // ------------- |---------:|----------:|----------:|-------:| - // Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 | - // ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 | + [Benchmark] + public void ExtendedAvx2() + { + this.dest.LoadFromInt16ExtendedAvx2(ref this.source); } + + // RESULT: + // Method | Mean | Error | StdDev | Scaled | + // ------------- |---------:|----------:|----------:|-------:| + // Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 | + // ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 | } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs index 998656147f..a5abeb3b66 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs @@ -4,34 +4,33 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Block8x8F_MultiplyInPlaceBlock { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Block8x8F_MultiplyInPlaceBlock - { - private static readonly Block8x8F Source = Create8x8FloatData(); + private static readonly Block8x8F Source = Create8x8FloatData(); - [Benchmark] - public void MultiplyInPlaceBlock() - { - Block8x8F dest = default; - Source.MultiplyInPlace(ref dest); - } + [Benchmark] + public void MultiplyInPlaceBlock() + { + Block8x8F dest = default; + Source.MultiplyInPlace(ref dest); + } - private static Block8x8F Create8x8FloatData() + private static Block8x8F Create8x8FloatData() + { + var result = new float[64]; + for (int i = 0; i < 8; i++) { - var result = new float[64]; - for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) { - for (int j = 0; j < 8; j++) - { - result[(i * 8) + j] = (i * 10) + j; - } + result[(i * 8) + j] = (i * 10) + j; } - - var source = default(Block8x8F); - source.LoadFrom(result); - return source; } + + var source = default(Block8x8F); + source.LoadFrom(result); + return source; } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceScalar.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceScalar.cs index 92226e5452..8ffb06e38a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceScalar.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceScalar.cs @@ -4,18 +4,17 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Block8x8F_MultiplyInPlaceScalar { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Block8x8F_MultiplyInPlaceScalar + [Benchmark] + public float MultiplyInPlaceScalar() { - [Benchmark] - public float MultiplyInPlaceScalar() - { - float f = 42F; - Block8x8F b = default; - b.MultiplyInPlace(f); - return f; - } + float f = 42F; + Block8x8F b = default; + b.MultiplyInPlace(f); + return f; } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs index 3c8c19f7a4..b1718759ea 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs @@ -4,32 +4,31 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Block8x8F_Quantize { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Block8x8F_Quantize + private Block8x8F block = CreateFromScalar(1); + private Block8x8F quant = CreateFromScalar(1); + private Block8x8 result = default; + + [Benchmark] + public short Quantize() { - private Block8x8F block = CreateFromScalar(1); - private Block8x8F quant = CreateFromScalar(1); - private Block8x8 result = default; + Block8x8F.Quantize(ref this.block, ref this.result, ref this.quant); + return this.result[0]; + } - [Benchmark] - public short Quantize() + private static Block8x8F CreateFromScalar(float scalar) + { + Block8x8F block = default; + for (int i = 0; i < 64; i++) { - Block8x8F.Quantize(ref this.block, ref this.result, ref this.quant); - return this.result[0]; + block[i] = scalar; } - private static Block8x8F CreateFromScalar(float scalar) - { - Block8x8F block = default; - for (int i = 0; i < 64; i++) - { - block[i] = scalar; - } - - return block; - } + return block; } } @@ -37,14 +36,14 @@ private static Block8x8F CreateFromScalar(float scalar) BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1165 (20H2/October2020Update) 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.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - 1. No HwIntrinsics : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - 2. SSE : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - 3. AVX : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +[Host] : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +1. No HwIntrinsics : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +2. SSE : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +3. AVX : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT | Method | Job | Mean | Error | StdDev | Ratio | |--------- |-----------------|---------:|---------:|---------:|------:| | Quantize | No HwIntrinsics | 73.34 ns | 1.081 ns | 1.011 ns | 1.00 | | Quantize | SSE | 24.11 ns | 0.298 ns | 0.279 ns | 0.33 | | Quantize | AVX | 15.90 ns | 0.074 ns | 0.065 ns | 0.22 | - */ +*/ diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 1ff81d677c..afe9d94ae8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,481 +11,480 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +public unsafe class Block8x8F_Round { - public unsafe class Block8x8F_Round - { - private Block8x8F block; + private Block8x8F block; - private readonly byte[] blockBuffer = new byte[512]; - private GCHandle blockHandle; - private float* alignedPtr; + private readonly byte[] blockBuffer = new byte[512]; + private GCHandle blockHandle; + private float* alignedPtr; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + if (Vector.Count != 8) { - if (Vector.Count != 8) - { - throw new NotSupportedException("Vector.Count != 8"); - } - - this.blockHandle = GCHandle.Alloc(this.blockBuffer, GCHandleType.Pinned); - ulong ptr = (ulong)this.blockHandle.AddrOfPinnedObject(); - ptr += 16; - ptr -= ptr % 16; - - if (ptr % 16 != 0) - { - throw new Exception("ptr is unaligned"); - } - - this.alignedPtr = (float*)ptr; + throw new NotSupportedException("Vector.Count != 8"); } - [GlobalCleanup] - public void Cleanup() + this.blockHandle = GCHandle.Alloc(this.blockBuffer, GCHandleType.Pinned); + ulong ptr = (ulong)this.blockHandle.AddrOfPinnedObject(); + ptr += 16; + ptr -= ptr % 16; + + if (ptr % 16 != 0) { - this.blockHandle.Free(); - this.alignedPtr = null; + throw new Exception("ptr is unaligned"); } - [Benchmark] - public void ScalarRound() - { - ref float b = ref Unsafe.As(ref this.block); + this.alignedPtr = (float*)ptr; + } - for (int i = 0; i < Block8x8F.Size; i++) - { - ref float v = ref Unsafe.Add(ref b, i); - v = (float)Math.Round(v); - } - } + [GlobalCleanup] + public void Cleanup() + { + this.blockHandle.Free(); + this.alignedPtr = null; + } - [Benchmark(Baseline = true)] - public void SimdUtils_FastRound_Vector8() - { - ref Block8x8F b = ref this.block; - - ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = SimdUtils.FastRound(row0); - ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = SimdUtils.FastRound(row1); - ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = SimdUtils.FastRound(row2); - ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = SimdUtils.FastRound(row3); - ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = SimdUtils.FastRound(row4); - ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = SimdUtils.FastRound(row5); - ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = SimdUtils.FastRound(row6); - ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = SimdUtils.FastRound(row7); - } + [Benchmark] + public void ScalarRound() + { + ref float b = ref Unsafe.As(ref this.block); - [Benchmark] - public void SimdUtils_FastRound_Vector8_ForceAligned() + for (int i = 0; i < Block8x8F.Size; i++) { - ref Block8x8F b = ref Unsafe.AsRef(this.alignedPtr); - - ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = SimdUtils.FastRound(row0); - ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = SimdUtils.FastRound(row1); - ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = SimdUtils.FastRound(row2); - ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = SimdUtils.FastRound(row3); - ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = SimdUtils.FastRound(row4); - ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = SimdUtils.FastRound(row5); - ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = SimdUtils.FastRound(row6); - ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = SimdUtils.FastRound(row7); + ref float v = ref Unsafe.Add(ref b, i); + v = (float)Math.Round(v); } + } - [Benchmark] - public void SimdUtils_FastRound_Vector8_Grouped() - { - ref Block8x8F b = ref this.block; - - ref Vector row0 = ref Unsafe.As>(ref b.V0L); - ref Vector row1 = ref Unsafe.As>(ref b.V1L); - ref Vector row2 = ref Unsafe.As>(ref b.V2L); - ref Vector row3 = ref Unsafe.As>(ref b.V3L); - - row0 = SimdUtils.FastRound(row0); - row1 = SimdUtils.FastRound(row1); - row2 = SimdUtils.FastRound(row2); - row3 = SimdUtils.FastRound(row3); - - row0 = ref Unsafe.As>(ref b.V4L); - row1 = ref Unsafe.As>(ref b.V5L); - row2 = ref Unsafe.As>(ref b.V6L); - row3 = ref Unsafe.As>(ref b.V7L); - - row0 = SimdUtils.FastRound(row0); - row1 = SimdUtils.FastRound(row1); - row2 = SimdUtils.FastRound(row2); - row3 = SimdUtils.FastRound(row3); - } + [Benchmark(Baseline = true)] + public void SimdUtils_FastRound_Vector8() + { + ref Block8x8F b = ref this.block; + + ref Vector row0 = ref Unsafe.As>(ref b.V0L); + row0 = SimdUtils.FastRound(row0); + ref Vector row1 = ref Unsafe.As>(ref b.V1L); + row1 = SimdUtils.FastRound(row1); + ref Vector row2 = ref Unsafe.As>(ref b.V2L); + row2 = SimdUtils.FastRound(row2); + ref Vector row3 = ref Unsafe.As>(ref b.V3L); + row3 = SimdUtils.FastRound(row3); + ref Vector row4 = ref Unsafe.As>(ref b.V4L); + row4 = SimdUtils.FastRound(row4); + ref Vector row5 = ref Unsafe.As>(ref b.V5L); + row5 = SimdUtils.FastRound(row5); + ref Vector row6 = ref Unsafe.As>(ref b.V6L); + row6 = SimdUtils.FastRound(row6); + ref Vector row7 = ref Unsafe.As>(ref b.V7L); + row7 = SimdUtils.FastRound(row7); + } - [Benchmark] - public void Sse41_V1() - { - ref Vector128 b0 = ref Unsafe.As>(ref this.block); + [Benchmark] + public void SimdUtils_FastRound_Vector8_ForceAligned() + { + ref Block8x8F b = ref Unsafe.AsRef(this.alignedPtr); + + ref Vector row0 = ref Unsafe.As>(ref b.V0L); + row0 = SimdUtils.FastRound(row0); + ref Vector row1 = ref Unsafe.As>(ref b.V1L); + row1 = SimdUtils.FastRound(row1); + ref Vector row2 = ref Unsafe.As>(ref b.V2L); + row2 = SimdUtils.FastRound(row2); + ref Vector row3 = ref Unsafe.As>(ref b.V3L); + row3 = SimdUtils.FastRound(row3); + ref Vector row4 = ref Unsafe.As>(ref b.V4L); + row4 = SimdUtils.FastRound(row4); + ref Vector row5 = ref Unsafe.As>(ref b.V5L); + row5 = SimdUtils.FastRound(row5); + ref Vector row6 = ref Unsafe.As>(ref b.V6L); + row6 = SimdUtils.FastRound(row6); + ref Vector row7 = ref Unsafe.As>(ref b.V7L); + row7 = SimdUtils.FastRound(row7); + } - ref Vector128 p = ref b0; - p = Sse41.RoundToNearestInteger(p); + [Benchmark] + public void SimdUtils_FastRound_Vector8_Grouped() + { + ref Block8x8F b = ref this.block; + + ref Vector row0 = ref Unsafe.As>(ref b.V0L); + ref Vector row1 = ref Unsafe.As>(ref b.V1L); + ref Vector row2 = ref Unsafe.As>(ref b.V2L); + ref Vector row3 = ref Unsafe.As>(ref b.V3L); + + row0 = SimdUtils.FastRound(row0); + row1 = SimdUtils.FastRound(row1); + row2 = SimdUtils.FastRound(row2); + row3 = SimdUtils.FastRound(row3); + + row0 = ref Unsafe.As>(ref b.V4L); + row1 = ref Unsafe.As>(ref b.V5L); + row2 = ref Unsafe.As>(ref b.V6L); + row3 = ref Unsafe.As>(ref b.V7L); + + row0 = SimdUtils.FastRound(row0); + row1 = SimdUtils.FastRound(row1); + row2 = SimdUtils.FastRound(row2); + row3 = SimdUtils.FastRound(row3); + } - p = ref Unsafe.Add(ref b0, 1); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 2); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 3); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 4); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 5); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 6); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 7); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 8); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 9); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 10); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 11); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 12); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 13); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 14); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.Add(ref b0, 15); - p = Sse41.RoundToNearestInteger(p); - } + [Benchmark] + public void Sse41_V1() + { + ref Vector128 b0 = ref Unsafe.As>(ref this.block); + + ref Vector128 p = ref b0; + p = Sse41.RoundToNearestInteger(p); + + p = ref Unsafe.Add(ref b0, 1); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 2); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 3); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 4); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 5); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 6); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 7); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 8); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 9); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 10); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 11); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 12); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 13); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 14); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.Add(ref b0, 15); + p = Sse41.RoundToNearestInteger(p); + } - [Benchmark] - public unsafe void Sse41_V2() - { - ref Vector128 p = ref Unsafe.As>(ref this.block); - p = Sse41.RoundToNearestInteger(p); - var offset = (IntPtr)sizeof(Vector128); - p = Sse41.RoundToNearestInteger(p); + [Benchmark] + public unsafe void Sse41_V2() + { + ref Vector128 p = ref Unsafe.As>(ref this.block); + p = Sse41.RoundToNearestInteger(p); + var offset = (IntPtr)sizeof(Vector128); + p = Sse41.RoundToNearestInteger(p); + + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + p = ref Unsafe.AddByteOffset(ref p, offset); + p = Sse41.RoundToNearestInteger(p); + } - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - } + [Benchmark] + public unsafe void Sse41_V3() + { + ref Vector128 p = ref Unsafe.As>(ref this.block); + p = Sse41.RoundToNearestInteger(p); + var offset = (IntPtr)sizeof(Vector128); - [Benchmark] - public unsafe void Sse41_V3() + for (int i = 0; i < 15; i++) { - ref Vector128 p = ref Unsafe.As>(ref this.block); + p = ref Unsafe.AddByteOffset(ref p, offset); p = Sse41.RoundToNearestInteger(p); - var offset = (IntPtr)sizeof(Vector128); - - for (int i = 0; i < 15; i++) - { - p = ref Unsafe.AddByteOffset(ref p, offset); - p = Sse41.RoundToNearestInteger(p); - } } + } - [Benchmark] - public unsafe void Sse41_V4() - { - ref Vector128 p = ref Unsafe.As>(ref this.block); - var offset = (IntPtr)sizeof(Vector128); - - ref Vector128 a = ref p; - ref Vector128 b = ref Unsafe.AddByteOffset(ref a, offset); - ref Vector128 c = ref Unsafe.AddByteOffset(ref b, offset); - ref Vector128 d = ref Unsafe.AddByteOffset(ref c, offset); - a = Sse41.RoundToNearestInteger(a); - b = Sse41.RoundToNearestInteger(b); - c = Sse41.RoundToNearestInteger(c); - d = Sse41.RoundToNearestInteger(d); - - a = ref Unsafe.AddByteOffset(ref d, offset); - b = ref Unsafe.AddByteOffset(ref a, offset); - c = ref Unsafe.AddByteOffset(ref b, offset); - d = ref Unsafe.AddByteOffset(ref c, offset); - a = Sse41.RoundToNearestInteger(a); - b = Sse41.RoundToNearestInteger(b); - c = Sse41.RoundToNearestInteger(c); - d = Sse41.RoundToNearestInteger(d); - - a = ref Unsafe.AddByteOffset(ref d, offset); - b = ref Unsafe.AddByteOffset(ref a, offset); - c = ref Unsafe.AddByteOffset(ref b, offset); - d = ref Unsafe.AddByteOffset(ref c, offset); - a = Sse41.RoundToNearestInteger(a); - b = Sse41.RoundToNearestInteger(b); - c = Sse41.RoundToNearestInteger(c); - d = Sse41.RoundToNearestInteger(d); - - a = ref Unsafe.AddByteOffset(ref d, offset); - b = ref Unsafe.AddByteOffset(ref a, offset); - c = ref Unsafe.AddByteOffset(ref b, offset); - d = ref Unsafe.AddByteOffset(ref c, offset); - a = Sse41.RoundToNearestInteger(a); - b = Sse41.RoundToNearestInteger(b); - c = Sse41.RoundToNearestInteger(c); - d = Sse41.RoundToNearestInteger(d); - } + [Benchmark] + public unsafe void Sse41_V4() + { + ref Vector128 p = ref Unsafe.As>(ref this.block); + var offset = (IntPtr)sizeof(Vector128); + + ref Vector128 a = ref p; + ref Vector128 b = ref Unsafe.AddByteOffset(ref a, offset); + ref Vector128 c = ref Unsafe.AddByteOffset(ref b, offset); + ref Vector128 d = ref Unsafe.AddByteOffset(ref c, offset); + a = Sse41.RoundToNearestInteger(a); + b = Sse41.RoundToNearestInteger(b); + c = Sse41.RoundToNearestInteger(c); + d = Sse41.RoundToNearestInteger(d); + + a = ref Unsafe.AddByteOffset(ref d, offset); + b = ref Unsafe.AddByteOffset(ref a, offset); + c = ref Unsafe.AddByteOffset(ref b, offset); + d = ref Unsafe.AddByteOffset(ref c, offset); + a = Sse41.RoundToNearestInteger(a); + b = Sse41.RoundToNearestInteger(b); + c = Sse41.RoundToNearestInteger(c); + d = Sse41.RoundToNearestInteger(d); + + a = ref Unsafe.AddByteOffset(ref d, offset); + b = ref Unsafe.AddByteOffset(ref a, offset); + c = ref Unsafe.AddByteOffset(ref b, offset); + d = ref Unsafe.AddByteOffset(ref c, offset); + a = Sse41.RoundToNearestInteger(a); + b = Sse41.RoundToNearestInteger(b); + c = Sse41.RoundToNearestInteger(c); + d = Sse41.RoundToNearestInteger(d); + + a = ref Unsafe.AddByteOffset(ref d, offset); + b = ref Unsafe.AddByteOffset(ref a, offset); + c = ref Unsafe.AddByteOffset(ref b, offset); + d = ref Unsafe.AddByteOffset(ref c, offset); + a = Sse41.RoundToNearestInteger(a); + b = Sse41.RoundToNearestInteger(b); + c = Sse41.RoundToNearestInteger(c); + d = Sse41.RoundToNearestInteger(d); + } - [Benchmark] - public unsafe void Sse41_V5_Unaligned() - { - float* p = this.alignedPtr + 1; - - Vector128 v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - p += 8; - - v = Sse.LoadVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.Store(p, v); - } + [Benchmark] + public unsafe void Sse41_V5_Unaligned() + { + float* p = this.alignedPtr + 1; + + Vector128 v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + p += 8; + + v = Sse.LoadVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.Store(p, v); + } - [Benchmark] - public unsafe void Sse41_V5_Aligned() - { - float* p = this.alignedPtr; - - Vector128 v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - - v = Sse.LoadAlignedVector128(p); - v = Sse41.RoundToNearestInteger(v); - Sse.StoreAligned(p, v); - p += 8; - } + [Benchmark] + public unsafe void Sse41_V5_Aligned() + { + float* p = this.alignedPtr; + + Vector128 v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + + v = Sse.LoadAlignedVector128(p); + v = Sse41.RoundToNearestInteger(v); + Sse.StoreAligned(p, v); + p += 8; + } - [Benchmark] - public void Sse41_V6_Aligned() - { - float* p = this.alignedPtr; + [Benchmark] + public void Sse41_V6_Aligned() + { + float* p = this.alignedPtr; - Round8SseVectors(p); - Round8SseVectors(p + 32); - } + Round8SseVectors(p); + Round8SseVectors(p + 32); + } - private static void Round8SseVectors(float* p0) - { - float* p1 = p0 + 4; - float* p2 = p1 + 4; - float* p3 = p2 + 4; - float* p4 = p3 + 4; - float* p5 = p4 + 4; - float* p6 = p5 + 4; - float* p7 = p6 + 4; - - Vector128 v0 = Sse.LoadAlignedVector128(p0); - Vector128 v1 = Sse.LoadAlignedVector128(p1); - Vector128 v2 = Sse.LoadAlignedVector128(p2); - Vector128 v3 = Sse.LoadAlignedVector128(p3); - Vector128 v4 = Sse.LoadAlignedVector128(p4); - Vector128 v5 = Sse.LoadAlignedVector128(p5); - Vector128 v6 = Sse.LoadAlignedVector128(p6); - Vector128 v7 = Sse.LoadAlignedVector128(p7); - - v0 = Sse41.RoundToNearestInteger(v0); - v1 = Sse41.RoundToNearestInteger(v1); - v2 = Sse41.RoundToNearestInteger(v2); - v3 = Sse41.RoundToNearestInteger(v3); - v4 = Sse41.RoundToNearestInteger(v4); - v5 = Sse41.RoundToNearestInteger(v5); - v6 = Sse41.RoundToNearestInteger(v6); - v7 = Sse41.RoundToNearestInteger(v7); - - Sse.StoreAligned(p0, v0); - Sse.StoreAligned(p1, v1); - Sse.StoreAligned(p2, v2); - Sse.StoreAligned(p3, v3); - Sse.StoreAligned(p4, v4); - Sse.StoreAligned(p5, v5); - Sse.StoreAligned(p6, v6); - Sse.StoreAligned(p7, v7); - } + private static void Round8SseVectors(float* p0) + { + float* p1 = p0 + 4; + float* p2 = p1 + 4; + float* p3 = p2 + 4; + float* p4 = p3 + 4; + float* p5 = p4 + 4; + float* p6 = p5 + 4; + float* p7 = p6 + 4; + + Vector128 v0 = Sse.LoadAlignedVector128(p0); + Vector128 v1 = Sse.LoadAlignedVector128(p1); + Vector128 v2 = Sse.LoadAlignedVector128(p2); + Vector128 v3 = Sse.LoadAlignedVector128(p3); + Vector128 v4 = Sse.LoadAlignedVector128(p4); + Vector128 v5 = Sse.LoadAlignedVector128(p5); + Vector128 v6 = Sse.LoadAlignedVector128(p6); + Vector128 v7 = Sse.LoadAlignedVector128(p7); + + v0 = Sse41.RoundToNearestInteger(v0); + v1 = Sse41.RoundToNearestInteger(v1); + v2 = Sse41.RoundToNearestInteger(v2); + v3 = Sse41.RoundToNearestInteger(v3); + v4 = Sse41.RoundToNearestInteger(v4); + v5 = Sse41.RoundToNearestInteger(v5); + v6 = Sse41.RoundToNearestInteger(v6); + v7 = Sse41.RoundToNearestInteger(v7); + + Sse.StoreAligned(p0, v0); + Sse.StoreAligned(p1, v1); + Sse.StoreAligned(p2, v2); + Sse.StoreAligned(p3, v3); + Sse.StoreAligned(p4, v4); + Sse.StoreAligned(p5, v5); + Sse.StoreAligned(p6, v6); + Sse.StoreAligned(p7, v7); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs index 60a793c5bb..07907f21d7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs @@ -4,33 +4,32 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Block8x8F_Transpose { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Block8x8F_Transpose - { - private Block8x8F source = Create8x8FloatData(); + private Block8x8F source = Create8x8FloatData(); - [Benchmark] - public float TransposeInplace() - { - this.source.TransposeInplace(); - return this.source[0]; - } + [Benchmark] + public float TransposeInplace() + { + this.source.TransposeInplace(); + return this.source[0]; + } - private static Block8x8F Create8x8FloatData() + private static Block8x8F Create8x8FloatData() + { + Block8x8F block = default; + for (int i = 0; i < 8; i++) { - Block8x8F block = default; - for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) { - for (int j = 0; j < 8; j++) - { - block[(i * 8) + j] = (i * 10) + j; - } + block[(i * 8) + j] = (i * 10) + j; } - - return block; } + + return block; } } @@ -38,10 +37,10 @@ private static Block8x8F Create8x8FloatData() BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1237 (20H2/October2020Update) 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.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - 1. No HwIntrinsics : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - 2. SSE : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - 3. AVX : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +[Host] : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +1. No HwIntrinsics : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +2. SSE : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +3. AVX : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT Runtime=.NET Core 3.1 diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index 59fe2ed431..6ad20ce679 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -4,38 +4,37 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class CmykColorConversion : ColorConversionBenchmark { - [Config(typeof(Config.ShortMultiFramework))] - public class CmykColorConversion : ColorConversionBenchmark + public CmykColorConversion() + : base(4) + { + } + + [Benchmark(Baseline = true)] + public void Scalar() { - public CmykColorConversion() - : base(4) - { - } - - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykAvx(8).ConvertToRgbInplace(values); - } + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVector8() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.CmykVector(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVectorAvx() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.CmykAvx(8).ConvertToRgbInplace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs index 130d884bd4..8964667b74 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs @@ -1,61 +1,58 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Numerics; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +public abstract class ColorConversionBenchmark { - public abstract class ColorConversionBenchmark - { - private readonly int componentCount; + private readonly int componentCount; - public const int Count = 128; + public const int Count = 128; - protected ColorConversionBenchmark(int componentCount) - => this.componentCount = componentCount; + protected ColorConversionBenchmark(int componentCount) + => this.componentCount = componentCount; - protected Buffer2D[] Input { get; private set; } + protected Buffer2D[] Input { get; private set; } - [GlobalSetup] - public void Setup() - { - this.Input = CreateRandomValues(this.componentCount, Count); - } + [GlobalSetup] + public void Setup() + { + this.Input = CreateRandomValues(this.componentCount, Count); + } - [GlobalCleanup] - public void Cleanup() + [GlobalCleanup] + public void Cleanup() + { + foreach (Buffer2D buffer in this.Input) { - foreach (Buffer2D buffer in this.Input) - { - buffer.Dispose(); - } + buffer.Dispose(); } + } - private static Buffer2D[] CreateRandomValues( - int componentCount, - int inputBufferLength, - float minVal = 0f, - float maxVal = 255f) + private static Buffer2D[] CreateRandomValues( + int componentCount, + int inputBufferLength, + float minVal = 0f, + float maxVal = 255f) + { + var rnd = new Random(42); + var buffers = new Buffer2D[componentCount]; + for (int i = 0; i < componentCount; i++) { - var rnd = new Random(42); - var buffers = new Buffer2D[componentCount]; - for (int i = 0; i < componentCount; i++) - { - var values = new float[inputBufferLength]; + var values = new float[inputBufferLength]; - for (int j = 0; j < inputBufferLength; j++) - { - values[j] = ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; - } - - // no need to dispose when buffer is not array owner - buffers[i] = Configuration.Default.MemoryAllocator.Allocate2D(values.Length, 1); + for (int j = 0; j < inputBufferLength; j++) + { + values[j] = ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } - return buffers; + // no need to dispose when buffer is not array owner + buffers[i] = Configuration.Default.MemoryAllocator.Allocate2D(values.Length, 1); } + + return buffers; } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index 5f70787943..47aac3464e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -4,30 +4,29 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class GrayscaleColorConversion : ColorConversionBenchmark { - [Config(typeof(Config.ShortMultiFramework))] - public class GrayscaleColorConversion : ColorConversionBenchmark + public GrayscaleColorConversion() + : base(1) { - public GrayscaleColorConversion() - : base(1) - { - } + } - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + [Benchmark(Baseline = true)] + public void Scalar() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInplace(values); - } + new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInplace(values); + } - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + [Benchmark] + public void SimdVectorAvx() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); - } + new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index 99b1f88107..18b3eac611 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -4,38 +4,37 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class RgbColorConversion : ColorConversionBenchmark { - [Config(typeof(Config.ShortMultiFramework))] - public class RgbColorConversion : ColorConversionBenchmark + public RgbColorConversion() + : base(3) + { + } + + [Benchmark(Baseline = true)] + public void Scalar() { - public RgbColorConversion() - : base(3) - { - } - - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values); - } + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVector8() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.RgbVector(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVectorAvx() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index d528ea5ff4..800190d2d7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -4,38 +4,37 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class YCbCrColorConversion : ColorConversionBenchmark { - [Config(typeof(Config.ShortMultiFramework))] - public class YCbCrColorConversion : ColorConversionBenchmark + public YCbCrColorConversion() + : base(3) + { + } + + [Benchmark] + public void Scalar() { - public YCbCrColorConversion() - : base(3) - { - } - - [Benchmark] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrAvx(8).ConvertToRgbInplace(values); - } + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVector8() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVectorAvx() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YCbCrAvx(8).ConvertToRgbInplace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index cfc47bdc6e..991d3b0d02 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -4,38 +4,37 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class YccKColorConverter : ColorConversionBenchmark { - [Config(typeof(Config.ShortMultiFramework))] - public class YccKColorConverter : ColorConversionBenchmark + public YccKColorConverter() + : base(4) + { + } + + [Benchmark(Baseline = true)] + public void Scalar() { - public YccKColorConverter() - : base(4) - { - } - - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx2() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values); - } + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVector8() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YccKVector(8).ConvertToRgbInplace(values); + } + + [Benchmark] + public void SimdVectorAvx2() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 1fee65fa0b..53d6028295 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -1,76 +1,74 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +public class DecodeJpeg { - public class DecodeJpeg - { - private JpegDecoder decoder; + private JpegDecoder decoder; - private MemoryStream preloadedImageStream; + private MemoryStream preloadedImageStream; - private void GenericSetup(string imageSubpath) - { - this.decoder = new JpegDecoder(); - byte[] bytes = File.ReadAllBytes(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, imageSubpath)); - this.preloadedImageStream = new MemoryStream(bytes); - } + private void GenericSetup(string imageSubpath) + { + this.decoder = new JpegDecoder(); + byte[] bytes = File.ReadAllBytes(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, imageSubpath)); + this.preloadedImageStream = new MemoryStream(bytes); + } - private void GenericBechmark() - { - this.preloadedImageStream.Position = 0; - using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream); - } + private void GenericBechmark() + { + this.preloadedImageStream.Position = 0; + using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream); + } - [GlobalSetup(Target = nameof(JpegBaselineInterleaved444))] - public void SetupBaselineInterleaved444() => - this.GenericSetup(TestImages.Jpeg.Baseline.Winter444_Interleaved); + [GlobalSetup(Target = nameof(JpegBaselineInterleaved444))] + public void SetupBaselineInterleaved444() => + this.GenericSetup(TestImages.Jpeg.Baseline.Winter444_Interleaved); - [GlobalSetup(Target = nameof(JpegBaselineInterleaved420))] - public void SetupBaselineInterleaved420() => - this.GenericSetup(TestImages.Jpeg.Baseline.Hiyamugi); + [GlobalSetup(Target = nameof(JpegBaselineInterleaved420))] + public void SetupBaselineInterleaved420() => + this.GenericSetup(TestImages.Jpeg.Baseline.Hiyamugi); - [GlobalSetup(Target = nameof(JpegBaseline400))] - public void SetupBaselineSingleComponent() => - this.GenericSetup(TestImages.Jpeg.Baseline.Jpeg400); + [GlobalSetup(Target = nameof(JpegBaseline400))] + public void SetupBaselineSingleComponent() => + this.GenericSetup(TestImages.Jpeg.Baseline.Jpeg400); - [GlobalSetup(Target = nameof(JpegProgressiveNonInterleaved420))] - public void SetupProgressiveNoninterleaved420() => - this.GenericSetup(TestImages.Jpeg.Progressive.Winter420_NonInterleaved); + [GlobalSetup(Target = nameof(JpegProgressiveNonInterleaved420))] + public void SetupProgressiveNoninterleaved420() => + this.GenericSetup(TestImages.Jpeg.Progressive.Winter420_NonInterleaved); - [GlobalCleanup] - public void Cleanup() - { - this.preloadedImageStream.Dispose(); - this.preloadedImageStream = null; - } + [GlobalCleanup] + public void Cleanup() + { + this.preloadedImageStream.Dispose(); + this.preloadedImageStream = null; + } - [Benchmark(Description = "Baseline 4:4:4 Interleaved")] - public void JpegBaselineInterleaved444() => this.GenericBechmark(); + [Benchmark(Description = "Baseline 4:4:4 Interleaved")] + public void JpegBaselineInterleaved444() => this.GenericBechmark(); - [Benchmark(Description = "Baseline 4:2:0 Interleaved")] - public void JpegBaselineInterleaved420() => this.GenericBechmark(); + [Benchmark(Description = "Baseline 4:2:0 Interleaved")] + public void JpegBaselineInterleaved420() => this.GenericBechmark(); - [Benchmark(Description = "Baseline 4:0:0 (grayscale)")] - public void JpegBaseline400() => this.GenericBechmark(); + [Benchmark(Description = "Baseline 4:0:0 (grayscale)")] + public void JpegBaseline400() => this.GenericBechmark(); - [Benchmark(Description = "Progressive 4:2:0 Non-Interleaved")] - public void JpegProgressiveNonInterleaved420() => this.GenericBechmark(); - } + [Benchmark(Description = "Progressive 4:2:0 Non-Interleaved")] + public void JpegProgressiveNonInterleaved420() => this.GenericBechmark(); } /* BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1348 (20H2/October2020Update) 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.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT - DefaultJob : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +[Host] : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT +DefaultJob : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT | Method | Mean | Error | StdDev | @@ -86,8 +84,8 @@ FRESH BENCHMARKS FOR NEW SPECTRAL CONVERSION SETUP 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 +[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 | Mean | Error | StdDev | diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 546051772e..3a3c81b52c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -9,60 +8,59 @@ using SixLabors.ImageSharp.Tests; using SDSize = System.Drawing.Size; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeJpegParseStreamOnly { - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeJpegParseStreamOnly - { - [Params(TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr)] - public string TestImage { get; set; } + [Params(TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr)] + public string TestImage { get; set; } - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - private byte[] jpegBytes; + private byte[] jpegBytes; - [GlobalSetup] - public void Setup() - => this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); + [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 options = new JpegDecoderOptions(); + options.GeneralOptions.SkipMetadata = true; - [Benchmark(Baseline = true, Description = "System.Drawing FULL")] - public SDSize JpegSystemDrawing() + using var decoder = new JpegDecoderCore(options); + var spectralConverter = new NoopSpectralConverter(); + decoder.ParseStream(bufferedStream, spectralConverter, cancellationToken: default); + } + + // 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() { - 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() + public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream); - var options = new JpegDecoderOptions(); - options.GeneralOptions.SkipMetadata = true; - - using var decoder = new JpegDecoderCore(options); - var spectralConverter = new NoopSpectralConverter(); - decoder.ParseStream(bufferedStream, spectralConverter, cancellationToken: default); } - // 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 PrepareForDecoding() { - public override void ConvertStrideBaseline() - { - } - - public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) - { - } - - public override void PrepareForDecoding() - { - } } } } @@ -71,10 +69,10 @@ public override void PrepareForDecoding() BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1083 (20H2/October2020Update) 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.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT - Job-VAJCIU : .NET Core 2.1.26 (CoreCLR 4.6.29812.02, CoreFX 4.6.29812.01), X64 RyuJIT - Job-INPXCR : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT - Job-JRCLOJ : .NET Framework 4.8 (4.8.4390.0), X64 RyuJIT +[Host] : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT +Job-VAJCIU : .NET Core 2.1.26 (CoreCLR 4.6.29812.02, CoreFX 4.6.29812.01), X64 RyuJIT +Job-INPXCR : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT +Job-JRCLOJ : .NET Framework 4.8 (4.8.4390.0), X64 RyuJIT IterationCount=3 LaunchCount=1 WarmupCount=3 | Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs index d6d699c0c8..bece1de5bd 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -1,41 +1,39 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +/// +/// An expensive Jpeg benchmark, running on a wide range of input images, +/// showing aggregate results. +/// +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase { - /// - /// An expensive Jpeg benchmark, running on a wide range of input images, - /// showing aggregate results. - /// - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase - { - protected override IEnumerable InputImageSubfoldersOrFiles - => new[] - { - TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, - TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, - TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, - TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, - TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, - }; + protected override IEnumerable InputImageSubfoldersOrFiles + => new[] + { + TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, + TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, + }; - [Params(InputImageCategory.AllImages)] - public override InputImageCategory InputCategory { get; set; } + [Params(InputImageCategory.AllImages)] + public override InputImageCategory InputCategory { get; set; } - [Benchmark] - public void ImageSharp() - => this.ForEachStream(ms => Image.Load(ms)); + [Benchmark] + public void ImageSharp() + => this.ForEachStream(ms => Image.Load(ms)); - [Benchmark(Baseline = true)] - public void SystemDrawing() - => this.ForEachStream(SDImage.FromStream); - } + [Benchmark(Baseline = true)] + public void SystemDrawing() + => this.ForEachStream(SDImage.FromStream); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index f704bef280..035f800a9b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Tests; @@ -9,67 +8,66 @@ using SDSize = System.Drawing.Size; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +/// +/// Image-specific Jpeg benchmarks +/// +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeJpeg_ImageSpecific { - /// - /// Image-specific Jpeg benchmarks - /// - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeJpeg_ImageSpecific - { - private byte[] jpegBytes; + private byte[] jpegBytes; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); #pragma warning disable SA1115 - [Params( - TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, - TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, + [Params( + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, - // The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr" - // is almost the same as the result for Jpeg420Exif, - // which proves that the execution time for the most common YCbCr 420 path scales linearly. - // TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, - TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr)] + // The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr" + // is almost the same as the result for Jpeg420Exif, + // which proves that the execution time for the most common YCbCr 420 path scales linearly. + // TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr)] - public string TestImage { get; set; } + public string TestImage { get; set; } - [GlobalSetup] - public void ReadImages() - { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } - - [Benchmark(Baseline = true)] - public SDSize SystemDrawing() + [GlobalSetup] + public void ReadImages() + { + if (this.jpegBytes == null) { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = SDImage.FromStream(memoryStream); - return image.Size; + this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); } + } - [Benchmark] - public Size ImageSharp() - { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = Image.Load(new DecoderOptions() { SkipMetadata = true }, memoryStream); - return new Size(image.Width, image.Height); - } + [Benchmark(Baseline = true)] + public SDSize SystemDrawing() + { + using var memoryStream = new MemoryStream(this.jpegBytes); + using var image = SDImage.FromStream(memoryStream); + return image.Size; + } - /* - | Method | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - |------------------------------- |--------------------- |-----------:|------------:|-----------:|------:|--------:|------:|------:|------:|-----------:| - | 'Decode Jpeg - System.Drawing' | Jpg/b(...)e.jpg [21] | 5.122 ms | 1.3978 ms | 0.0766 ms | 1.00 | 0.00 | - | - | - | 176 B | - | 'Decode Jpeg - ImageSharp' | Jpg/b(...)e.jpg [21] | 11.991 ms | 0.2514 ms | 0.0138 ms | 2.34 | 0.03 | - | - | - | 15816 B | - | | | | | | | | | | | | - | 'Decode Jpeg - System.Drawing' | Jpg/b(...)f.jpg [28] | 14.943 ms | 1.8410 ms | 0.1009 ms | 1.00 | 0.00 | - | - | - | 176 B | - | 'Decode Jpeg - ImageSharp' | Jpg/b(...)f.jpg [28] | 29.759 ms | 1.5452 ms | 0.0847 ms | 1.99 | 0.01 | - | - | - | 16824 B | - | | | | | | | | | | | | - | 'Decode Jpeg - System.Drawing' | Jpg/i(...)e.jpg [43] | 388.229 ms | 382.8946 ms | 20.9877 ms | 1.00 | 0.00 | - | - | - | 216 B | - | 'Decode Jpeg - ImageSharp' | Jpg/i(...)e.jpg [43] | 276.490 ms | 195.5104 ms | 10.7166 ms | 0.71 | 0.01 | - | - | - | 36022368 B | - */ + [Benchmark] + public Size ImageSharp() + { + using var memoryStream = new MemoryStream(this.jpegBytes); + using var image = Image.Load(new DecoderOptions() { SkipMetadata = true }, memoryStream); + return new Size(image.Width, image.Height); } + + /* + | Method | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + |------------------------------- |--------------------- |-----------:|------------:|-----------:|------:|--------:|------:|------:|------:|-----------:| + | 'Decode Jpeg - System.Drawing' | Jpg/b(...)e.jpg [21] | 5.122 ms | 1.3978 ms | 0.0766 ms | 1.00 | 0.00 | - | - | - | 176 B | + | 'Decode Jpeg - ImageSharp' | Jpg/b(...)e.jpg [21] | 11.991 ms | 0.2514 ms | 0.0138 ms | 2.34 | 0.03 | - | - | - | 15816 B | + | | | | | | | | | | | | + | 'Decode Jpeg - System.Drawing' | Jpg/b(...)f.jpg [28] | 14.943 ms | 1.8410 ms | 0.1009 ms | 1.00 | 0.00 | - | - | - | 176 B | + | 'Decode Jpeg - ImageSharp' | Jpg/b(...)f.jpg [28] | 29.759 ms | 1.5452 ms | 0.0847 ms | 1.99 | 0.01 | - | - | - | 16824 B | + | | | | | | | | | | | | + | 'Decode Jpeg - System.Drawing' | Jpg/i(...)e.jpg [43] | 388.229 ms | 382.8946 ms | 20.9877 ms | 1.00 | 0.00 | - | - | - | 216 B | + | 'Decode Jpeg - ImageSharp' | Jpg/i(...)e.jpg [43] | 276.490 ms | 195.5104 ms | 10.7166 ms | 0.71 | 0.01 | - | - | - | 36022368 B | + */ } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs index 4280996ffc..d762e8e95e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs @@ -1,95 +1,91 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -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 +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +/// +/// Benchmark for performance comparison between other codecs. +/// +/// +/// This benchmarks tests baseline 4:2:0 chroma sampling path. +/// +public class EncodeJpegComparison { - /// - /// Benchmark for performance comparison between other codecs. - /// - /// - /// This benchmarks tests baseline 4:2:0 chroma sampling path. - /// - 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 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(imageBinaryStream); + this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegEncodingColor.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() { - // 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 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(imageBinaryStream); - this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegEncodingColor.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); - } + this.imageSkiaSharp.Encode(SKEncodedImageFormat.Jpeg, this.Quality).SaveTo(this.destinationStream); + this.destinationStream.Seek(0, SeekOrigin.Begin); } } @@ -97,8 +93,8 @@ public void BenchmarkSkiaSharp() 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 +[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 | diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs index 231ba6c250..98eb0b54dc 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs @@ -1,76 +1,73 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -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 +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +/// +/// Benchmark for all available encoding features of the Jpeg file type. +/// +/// +/// This benchmark does NOT compare ImageSharp to any other jpeg codecs. +/// +public class EncodeJpegFeatures { - /// - /// Benchmark for all available encoding features of the Jpeg file type. - /// - /// - /// This benchmark does NOT compare ImageSharp to any other jpeg codecs. - /// - public class EncodeJpegFeatures - { - // Big enough, 4:4:4 chroma sampling - // No metadata - private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; + // Big enough, 4:4:4 chroma sampling + // No metadata + private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; - public static IEnumerable ColorSpaceValues => new[] - { - JpegEncodingColor.Luminance, - JpegEncodingColor.Rgb, - JpegEncodingColor.YCbCrRatio420, - JpegEncodingColor.YCbCrRatio444, - }; + public static IEnumerable ColorSpaceValues => new[] + { + JpegEncodingColor.Luminance, + JpegEncodingColor.Rgb, + JpegEncodingColor.YCbCrRatio420, + JpegEncodingColor.YCbCrRatio444, + }; - [Params(75, 90, 100)] - public int Quality; + [Params(75, 90, 100)] + public int Quality; - [ParamsSource(nameof(ColorSpaceValues), Priority = -100)] - public JpegEncodingColor TargetColorSpace; + [ParamsSource(nameof(ColorSpaceValues), Priority = -100)] + public JpegEncodingColor TargetColorSpace; - private Image bmpCore; - private JpegEncoder encoder; + private Image bmpCore; + private JpegEncoder encoder; - private MemoryStream destinationStream; + private MemoryStream destinationStream; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); + this.bmpCore = Image.Load(imageBinaryStream); + this.encoder = new JpegEncoder { - using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); - this.bmpCore = Image.Load(imageBinaryStream); - this.encoder = new JpegEncoder - { - Quality = this.Quality, - ColorType = this.TargetColorSpace, - Interleaved = true, - }; - this.destinationStream = new MemoryStream(); - } + Quality = this.Quality, + ColorType = this.TargetColorSpace, + Interleaved = true, + }; + this.destinationStream = new MemoryStream(); + } - [GlobalCleanup] - public void Cleanup() - { - this.bmpCore.Dispose(); - this.bmpCore = null; + [GlobalCleanup] + public void Cleanup() + { + this.bmpCore.Dispose(); + this.bmpCore = null; - this.destinationStream.Dispose(); - this.destinationStream = null; - } + this.destinationStream.Dispose(); + this.destinationStream = null; + } - [Benchmark] - public void Benchmark() - { - this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder); - this.destinationStream.Seek(0, SeekOrigin.Begin); - } + [Benchmark] + public void Benchmark() + { + this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder); + this.destinationStream.Seek(0, SeekOrigin.Begin); } } @@ -78,8 +75,8 @@ public void Benchmark() 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.202 - [Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT - DefaultJob : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT +[Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT +DefaultJob : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT | Method | TargetColorSpace | Quality | Mean | Error | StdDev | diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index a9cbb418a2..0b977bfbc8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -1,39 +1,37 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +[Config(typeof(Config.ShortMultiFramework))] +public class IdentifyJpeg { - [Config(typeof(Config.ShortMultiFramework))] - public class IdentifyJpeg - { - private byte[] jpegBytes; + private byte[] jpegBytes; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] - public string TestImage { get; set; } + [Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] + public string TestImage { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.jpegBytes == null) { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - } + this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); } + } - [Benchmark] - public IImageInfo Identify() - { - using var memoryStream = new MemoryStream(this.jpegBytes); - IImageDecoder decoder = new JpegDecoder(); - return decoder.Identify(DecoderOptions.Default, memoryStream, default); - } + [Benchmark] + public IImageInfo Identify() + { + using var memoryStream = new MemoryStream(this.jpegBytes); + IImageDecoder decoder = new JpegDecoder(); + return decoder.Identify(DecoderOptions.Default, memoryStream, default); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index 153287bb2f..b75d012f97 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -1,139 +1,185 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Drawing; -using System.IO; -using System.Linq; using System.Numerics; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +public abstract class MultiImageBenchmarkBase { - public abstract class MultiImageBenchmarkBase - { - protected Dictionary FileNamesToBytes { get; set; } = new Dictionary(); + protected Dictionary FileNamesToBytes { get; set; } = new Dictionary(); - protected Dictionary> FileNamesToImageSharpImages { get; set; } = new Dictionary>(); + protected Dictionary> FileNamesToImageSharpImages { get; set; } = new Dictionary>(); - protected Dictionary FileNamesToSystemDrawingImages { get; set; } = new Dictionary(); + protected Dictionary FileNamesToSystemDrawingImages { get; set; } = new Dictionary(); + /// + /// The values of this enum separate input files into categories. + /// + public enum InputImageCategory + { /// - /// The values of this enum separate input files into categories. + /// Use all images. /// - public enum InputImageCategory - { - /// - /// Use all images. - /// - AllImages, - - /// - /// Use small images only. - /// - SmallImagesOnly, - - /// - /// Use large images only. - /// - LargeImagesOnly - } - - [Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)] - public virtual InputImageCategory InputCategory { get; set; } - - protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath; - - protected virtual IEnumerable SearchPatterns => new[] { "*.*" }; + AllImages, /// - /// Gets the file names containing these strings are substrings are not processed by the benchmark. + /// Use small images only. /// - protected virtual IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; + SmallImagesOnly, /// - /// Gets folders containing files OR files to be processed by the benchmark. + /// Use large images only. /// - protected IEnumerable AllFoldersOrFiles - => this.InputImageSubfoldersOrFiles.Select(f => Path.Combine(this.BaseFolder, f)); + LargeImagesOnly + } - /// - /// Gets the large image threshold. - /// The images sized above this threshold will be included in. - /// - protected virtual int LargeImageThresholdInBytes => 100000; + [Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)] + public virtual InputImageCategory InputCategory { get; set; } - protected IEnumerable> EnumeratePairsByBenchmarkSettings( - Dictionary input, - Predicate checkIfSmall) - => this.InputCategory switch - { - InputImageCategory.AllImages => input, - InputImageCategory.SmallImagesOnly => input.Where(kv => checkIfSmall(kv.Value)), - InputImageCategory.LargeImagesOnly => input.Where(kv => !checkIfSmall(kv.Value)), - _ => throw new ArgumentOutOfRangeException(), - }; + protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath; - protected IEnumerable> FileNames2Bytes - => - this.EnumeratePairsByBenchmarkSettings( - this.FileNamesToBytes, - arr => arr.Length < this.LargeImageThresholdInBytes); + protected virtual IEnumerable SearchPatterns => new[] { "*.*" }; + + /// + /// Gets the file names containing these strings are substrings are not processed by the benchmark. + /// + protected virtual IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; + + /// + /// Gets folders containing files OR files to be processed by the benchmark. + /// + protected IEnumerable AllFoldersOrFiles + => this.InputImageSubfoldersOrFiles.Select(f => Path.Combine(this.BaseFolder, f)); + + /// + /// Gets the large image threshold. + /// The images sized above this threshold will be included in. + /// + protected virtual int LargeImageThresholdInBytes => 100000; + + protected IEnumerable> EnumeratePairsByBenchmarkSettings( + Dictionary input, + Predicate checkIfSmall) + => this.InputCategory switch + { + InputImageCategory.AllImages => input, + InputImageCategory.SmallImagesOnly => input.Where(kv => checkIfSmall(kv.Value)), + InputImageCategory.LargeImagesOnly => input.Where(kv => !checkIfSmall(kv.Value)), + _ => throw new ArgumentOutOfRangeException(), + }; + + protected IEnumerable> FileNames2Bytes + => + this.EnumeratePairsByBenchmarkSettings( + this.FileNamesToBytes, + arr => arr.Length < this.LargeImageThresholdInBytes); + + protected abstract IEnumerable InputImageSubfoldersOrFiles { get; } + + [GlobalSetup] + public virtual void Setup() + { + if (!Vector.IsHardwareAccelerated) + { + throw new Exception("Vector.IsHardwareAccelerated == false! Check your build settings!"); + } - protected abstract IEnumerable InputImageSubfoldersOrFiles { get; } + // Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated); + this.ReadFilesImpl(); + } - [GlobalSetup] - public virtual void Setup() + protected virtual void ReadFilesImpl() + { + foreach (string path in this.AllFoldersOrFiles) { - if (!Vector.IsHardwareAccelerated) + if (File.Exists(path)) { - throw new Exception("Vector.IsHardwareAccelerated == false! Check your build settings!"); + this.FileNamesToBytes[path] = File.ReadAllBytes(path); + continue; } - // Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated); - this.ReadFilesImpl(); + string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray(); + + string[] allFiles = + this.SearchPatterns.SelectMany( + f => + Directory.EnumerateFiles(path, f, SearchOption.AllDirectories) + .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray(); + + foreach (string fn in allFiles) + { + this.FileNamesToBytes[fn] = File.ReadAllBytes(fn); + } } + } - protected virtual void ReadFilesImpl() + /// + /// Execute code for each image stream. If the returned object of the operation is it will be disposed. + /// + /// The operation to execute. If the returned object is <see cref="IDisposable"/> it will be disposed + protected void ForEachStream(Func operation) + { + foreach (KeyValuePair kv in this.FileNames2Bytes) { - foreach (string path in this.AllFoldersOrFiles) + using var memoryStream = new MemoryStream(kv.Value); + try { - if (File.Exists(path)) - { - this.FileNamesToBytes[path] = File.ReadAllBytes(path); - continue; - } + object obj = operation(memoryStream); + (obj as IDisposable)?.Dispose(); + } + catch (Exception ex) + { + Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); + } + } + } - string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray(); + public abstract class WithImagesPreloaded : MultiImageBenchmarkBase + { + protected override void ReadFilesImpl() + { + base.ReadFilesImpl(); - string[] allFiles = - this.SearchPatterns.SelectMany( - f => - Directory.EnumerateFiles(path, f, SearchOption.AllDirectories) - .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray(); + foreach (KeyValuePair kv in this.FileNamesToBytes) + { + byte[] bytes = kv.Value; + string fn = kv.Key; - foreach (string fn in allFiles) + using (var ms1 = new MemoryStream(bytes)) { - this.FileNamesToBytes[fn] = File.ReadAllBytes(fn); + this.FileNamesToImageSharpImages[fn] = Image.Load(ms1); } + + this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes)); } } - /// - /// Execute code for each image stream. If the returned object of the operation is it will be disposed. - /// - /// The operation to execute. If the returned object is <see cref="IDisposable"/> it will be disposed - protected void ForEachStream(Func operation) + protected IEnumerable>> FileNames2ImageSharpImages + => + this.EnumeratePairsByBenchmarkSettings( + this.FileNamesToImageSharpImages, + img => img.Width * img.Height < this.LargeImageThresholdInPixels); + + protected IEnumerable> FileNames2SystemDrawingImages + => + this.EnumeratePairsByBenchmarkSettings( + this.FileNamesToSystemDrawingImages, + img => img.Width * img.Height < this.LargeImageThresholdInPixels); + + protected virtual int LargeImageThresholdInPixels => 700000; + + protected void ForEachImageSharpImage(Func, object> operation) { - foreach (KeyValuePair kv in this.FileNames2Bytes) + foreach (KeyValuePair> kv in this.FileNames2ImageSharpImages) { - using var memoryStream = new MemoryStream(kv.Value); try { - object obj = operation(memoryStream); + object obj = operation(kv.Value); (obj as IDisposable)?.Dispose(); } catch (Exception ex) @@ -143,101 +189,50 @@ protected void ForEachStream(Func operation) } } - public abstract class WithImagesPreloaded : MultiImageBenchmarkBase + protected void ForEachImageSharpImage(Func, MemoryStream, object> operation) { - protected override void ReadFilesImpl() - { - base.ReadFilesImpl(); - - foreach (KeyValuePair kv in this.FileNamesToBytes) + using var workStream = new MemoryStream(); + this.ForEachImageSharpImage( + img => { - byte[] bytes = kv.Value; - string fn = kv.Key; - - using (var ms1 = new MemoryStream(bytes)) - { - this.FileNamesToImageSharpImages[fn] = Image.Load(ms1); - } - - this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes)); - } - } - - protected IEnumerable>> FileNames2ImageSharpImages - => - this.EnumeratePairsByBenchmarkSettings( - this.FileNamesToImageSharpImages, - img => img.Width * img.Height < this.LargeImageThresholdInPixels); + // ReSharper disable AccessToDisposedClosure + object result = operation(img, workStream); + workStream.Seek(0, SeekOrigin.Begin); - protected IEnumerable> FileNames2SystemDrawingImages - => - this.EnumeratePairsByBenchmarkSettings( - this.FileNamesToSystemDrawingImages, - img => img.Width * img.Height < this.LargeImageThresholdInPixels); - - protected virtual int LargeImageThresholdInPixels => 700000; + // ReSharper restore AccessToDisposedClosure + return result; + }); + } - protected void ForEachImageSharpImage(Func, object> operation) + protected void ForEachSystemDrawingImage(Func operation) + { + foreach (KeyValuePair kv in this.FileNames2SystemDrawingImages) { - foreach (KeyValuePair> kv in this.FileNames2ImageSharpImages) + try { - try - { - object obj = operation(kv.Value); - (obj as IDisposable)?.Dispose(); - } - catch (Exception ex) - { - Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); - } + object obj = operation(kv.Value); + (obj as IDisposable)?.Dispose(); } - } - - protected void ForEachImageSharpImage(Func, MemoryStream, object> operation) - { - using var workStream = new MemoryStream(); - this.ForEachImageSharpImage( - img => - { - // ReSharper disable AccessToDisposedClosure - object result = operation(img, workStream); - workStream.Seek(0, SeekOrigin.Begin); - - // ReSharper restore AccessToDisposedClosure - return result; - }); - } - - protected void ForEachSystemDrawingImage(Func operation) - { - foreach (KeyValuePair kv in this.FileNames2SystemDrawingImages) + catch (Exception ex) { - try - { - object obj = operation(kv.Value); - (obj as IDisposable)?.Dispose(); - } - catch (Exception ex) - { - Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); - } + Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); } } + } - protected void ForEachSystemDrawingImage(Func operation) - { - using var workStream = new MemoryStream(); - this.ForEachSystemDrawingImage( - img => - { - // ReSharper disable AccessToDisposedClosure - object result = operation(img, workStream); - workStream.Seek(0, SeekOrigin.Begin); - - // ReSharper restore AccessToDisposedClosure - return result; - }); - } + protected void ForEachSystemDrawingImage(Func operation) + { + using var workStream = new MemoryStream(); + this.ForEachSystemDrawingImage( + img => + { + // ReSharper disable AccessToDisposedClosure + object result = operation(img, workStream); + workStream.Seek(0, SeekOrigin.Begin); + + // ReSharper restore AccessToDisposedClosure + return result; + }); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs index 69b28b2138..74f5006668 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs @@ -1,67 +1,65 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs -{ - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeFilteredPng - { - private byte[] filter0; - private byte[] filter1; - private byte[] filter2; - private byte[] filter3; - private byte[] averageFilter3bpp; - private byte[] averageFilter4bpp; +namespace SixLabors.ImageSharp.Benchmarks.Codecs; - [GlobalSetup] - public void ReadImages() - { - this.filter0 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter0)); - this.filter1 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.SubFilter3BytesPerPixel)); - this.filter2 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.UpFilter)); - this.filter3 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.PaethFilter3BytesPerPixel)); - this.averageFilter3bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter3BytesPerPixel)); - this.averageFilter4bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter4BytesPerPixel)); - } +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeFilteredPng +{ + private byte[] filter0; + private byte[] filter1; + private byte[] filter2; + private byte[] filter3; + private byte[] averageFilter3bpp; + private byte[] averageFilter4bpp; - [Benchmark(Baseline = true, Description = "None-filtered PNG file")] - public Size PngFilter0() - => LoadPng(this.filter0); + [GlobalSetup] + public void ReadImages() + { + this.filter0 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter0)); + this.filter1 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.SubFilter3BytesPerPixel)); + this.filter2 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.UpFilter)); + this.filter3 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.PaethFilter3BytesPerPixel)); + this.averageFilter3bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter3BytesPerPixel)); + this.averageFilter4bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter4BytesPerPixel)); + } - [Benchmark(Description = "Sub-filtered PNG file")] - public Size PngFilter1() - => LoadPng(this.filter1); + [Benchmark(Baseline = true, Description = "None-filtered PNG file")] + public Size PngFilter0() + => LoadPng(this.filter0); - [Benchmark(Description = "Up-filtered PNG file")] - public Size PngFilter2() - => LoadPng(this.filter2); + [Benchmark(Description = "Sub-filtered PNG file")] + public Size PngFilter1() + => LoadPng(this.filter1); - [Benchmark(Description = "Average-filtered PNG file (3bpp)")] - public Size PngAvgFilter1() - => LoadPng(this.averageFilter3bpp); + [Benchmark(Description = "Up-filtered PNG file")] + public Size PngFilter2() + => LoadPng(this.filter2); - [Benchmark(Description = "Average-filtered PNG file (4bpp)")] - public Size PngAvgFilter2() - => LoadPng(this.averageFilter4bpp); + [Benchmark(Description = "Average-filtered PNG file (3bpp)")] + public Size PngAvgFilter1() + => LoadPng(this.averageFilter3bpp); - [Benchmark(Description = "Paeth-filtered PNG file")] - public Size PngFilter4() - => LoadPng(this.filter3); + [Benchmark(Description = "Average-filtered PNG file (4bpp)")] + public Size PngAvgFilter2() + => LoadPng(this.averageFilter4bpp); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Size LoadPng(byte[] bytes) - { - using var image = Image.Load(bytes); - return image.Size(); - } + [Benchmark(Description = "Paeth-filtered PNG file")] + public Size PngFilter4() + => LoadPng(this.filter3); - private static string TestImageFullPath(string path) - => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, path); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Size LoadPng(byte[] bytes) + { + using var image = Image.Load(bytes); + return image.Size(); } + + private static string TestImageFullPath(string path) + => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, path); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs index 4d41133ef2..a73d62859c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs @@ -1,49 +1,47 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; using SDSize = System.Drawing.Size; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class DecodePng { - [Config(typeof(Config.ShortMultiFramework))] - public class DecodePng - { - private byte[] pngBytes; + private byte[] pngBytes; - private string TestImageFullPath - => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath + => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Png.Splash)] - public string TestImage { get; set; } + [Params(TestImages.Png.Splash)] + public string TestImage { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.pngBytes == null) { - if (this.pngBytes == null) - { - this.pngBytes = File.ReadAllBytes(this.TestImageFullPath); - } + this.pngBytes = File.ReadAllBytes(this.TestImageFullPath); } + } - [Benchmark(Baseline = true, Description = "System.Drawing Png")] - public SDSize PngSystemDrawing() - { - using var memoryStream = new MemoryStream(this.pngBytes); - using var image = SDImage.FromStream(memoryStream); - return image.Size; - } + [Benchmark(Baseline = true, Description = "System.Drawing Png")] + public SDSize PngSystemDrawing() + { + using var memoryStream = new MemoryStream(this.pngBytes); + using var image = SDImage.FromStream(memoryStream); + return image.Size; + } - [Benchmark(Description = "ImageSharp Png")] - public Size PngImageSharp() - { - using var memoryStream = new MemoryStream(this.pngBytes); - using var image = Image.Load(memoryStream); - return image.Size(); - } + [Benchmark(Description = "ImageSharp Png")] + public Size PngImageSharp() + { + using var memoryStream = new MemoryStream(this.pngBytes); + using var image = Image.Load(memoryStream); + return image.Size(); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs index 66db135730..26fb0f4a41 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; @@ -9,84 +8,83 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +/// +/// Benchmarks saving png files using different quantizers. +/// System.Drawing cannot save indexed png files so we cannot compare. +/// +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeIndexedPng { - /// - /// Benchmarks saving png files using different quantizers. - /// System.Drawing cannot save indexed png files so we cannot compare. - /// - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeIndexedPng - { - // System.Drawing needs this. - private Stream bmpStream; - private Image bmpCore; + // System.Drawing needs this. + private Stream bmpStream; + private Image bmpCore; - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.bmpStream == null) { - if (this.bmpStream == null) - { - this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car)); - this.bmpCore = Image.Load(this.bmpStream); - this.bmpStream.Position = 0; - } + this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car)); + this.bmpCore = Image.Load(this.bmpStream); + this.bmpStream.Position = 0; } + } - [GlobalCleanup] - public void Cleanup() - { - this.bmpStream.Dispose(); - this.bmpStream = null; - this.bmpCore.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpStream = null; + this.bmpCore.Dispose(); + } - [Benchmark(Baseline = true, Description = "ImageSharp Octree Png")] - public void PngCoreOctree() - { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.Octree }; - this.bmpCore.SaveAsPng(memoryStream, options); - } + [Benchmark(Baseline = true, Description = "ImageSharp Octree Png")] + public void PngCoreOctree() + { + using var memoryStream = new MemoryStream(); + var options = new PngEncoder { Quantizer = KnownQuantizers.Octree }; + this.bmpCore.SaveAsPng(memoryStream, options); + } - [Benchmark(Description = "ImageSharp Octree NoDither Png")] - public void PngCoreOctreeNoDither() - { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) }; - this.bmpCore.SaveAsPng(memoryStream, options); - } + [Benchmark(Description = "ImageSharp Octree NoDither Png")] + public void PngCoreOctreeNoDither() + { + using var memoryStream = new MemoryStream(); + var options = new PngEncoder { Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) }; + this.bmpCore.SaveAsPng(memoryStream, options); + } - [Benchmark(Description = "ImageSharp Palette Png")] - public void PngCorePalette() - { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe }; - this.bmpCore.SaveAsPng(memoryStream, options); - } + [Benchmark(Description = "ImageSharp Palette Png")] + public void PngCorePalette() + { + using var memoryStream = new MemoryStream(); + var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe }; + this.bmpCore.SaveAsPng(memoryStream, options); + } - [Benchmark(Description = "ImageSharp Palette NoDither Png")] - public void PngCorePaletteNoDither() - { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) }; - this.bmpCore.SaveAsPng(memoryStream, options); - } + [Benchmark(Description = "ImageSharp Palette NoDither Png")] + public void PngCorePaletteNoDither() + { + using var memoryStream = new MemoryStream(); + var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) }; + this.bmpCore.SaveAsPng(memoryStream, options); + } - [Benchmark(Description = "ImageSharp Wu Png")] - public void PngCoreWu() - { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.Wu }; - this.bmpCore.SaveAsPng(memoryStream, options); - } + [Benchmark(Description = "ImageSharp Wu Png")] + public void PngCoreWu() + { + using var memoryStream = new MemoryStream(); + var options = new PngEncoder { Quantizer = KnownQuantizers.Wu }; + this.bmpCore.SaveAsPng(memoryStream, options); + } - [Benchmark(Description = "ImageSharp Wu NoDither Png")] - public void PngCoreWuNoDither() - { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }), ColorType = PngColorType.Palette }; - this.bmpCore.SaveAsPng(memoryStream, options); - } + [Benchmark(Description = "ImageSharp Wu NoDither Png")] + public void PngCoreWuNoDither() + { + using var memoryStream = new MemoryStream(); + var options = new PngEncoder { Quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }), ColorType = PngColorType.Palette }; + this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs index 08056a7ade..5cbe88fee6 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs @@ -2,61 +2,59 @@ // Licensed under the Six Labors Split License. using System.Drawing.Imaging; -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class EncodePng { - [Config(typeof(Config.ShortMultiFramework))] - public class EncodePng - { - // System.Drawing needs this. - private Stream bmpStream; - private SDImage bmpDrawing; - private Image bmpCore; + // System.Drawing needs this. + private Stream bmpStream; + private SDImage bmpDrawing; + private Image bmpCore; - [Params(false)] - public bool LargeImage { get; set; } + [Params(false)] + public bool LargeImage { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.bmpStream == null) { - if (this.bmpStream == null) - { - string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.LargeImage ? TestImages.Jpeg.Baseline.Jpeg420Exif : TestImages.Bmp.Car); - this.bmpStream = File.OpenRead(path); - this.bmpCore = Image.Load(this.bmpStream); - this.bmpStream.Position = 0; - this.bmpDrawing = SDImage.FromStream(this.bmpStream); - } + string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.LargeImage ? TestImages.Jpeg.Baseline.Jpeg420Exif : TestImages.Bmp.Car); + this.bmpStream = File.OpenRead(path); + this.bmpCore = Image.Load(this.bmpStream); + this.bmpStream.Position = 0; + this.bmpDrawing = SDImage.FromStream(this.bmpStream); } + } - [GlobalCleanup] - public void Cleanup() - { - this.bmpStream.Dispose(); - this.bmpStream = null; - this.bmpCore.Dispose(); - this.bmpDrawing.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpStream = null; + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } - [Benchmark(Baseline = true, Description = "System.Drawing Png")] - public void PngSystemDrawing() - { - using var memoryStream = new MemoryStream(); - this.bmpDrawing.Save(memoryStream, ImageFormat.Png); - } + [Benchmark(Baseline = true, Description = "System.Drawing Png")] + public void PngSystemDrawing() + { + using var memoryStream = new MemoryStream(); + this.bmpDrawing.Save(memoryStream, ImageFormat.Png); + } - [Benchmark(Description = "ImageSharp Png")] - public void PngCore() - { - using var memoryStream = new MemoryStream(); - var encoder = new PngEncoder { FilterMethod = PngFilterMethod.None }; - this.bmpCore.SaveAsPng(memoryStream, encoder); - } + [Benchmark(Description = "ImageSharp Png")] + public void PngCore() + { + using var memoryStream = new MemoryStream(); + var encoder = new PngEncoder { FilterMethod = PngFilterMethod.None }; + this.bmpCore.SaveAsPng(memoryStream, encoder); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs index 9e60fecacc..363ecf908b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -2,80 +2,77 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.IO; -using System.Threading; using BenchmarkDotNet.Attributes; using ImageMagick; using Pfim; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs -{ - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeTga - { - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); +namespace SixLabors.ImageSharp.Benchmarks.Codecs; - private readonly PfimConfig pfimConfig = new(allocator: new PfimAllocator()); +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeTga +{ + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - private byte[] data; + private readonly PfimConfig pfimConfig = new(allocator: new PfimAllocator()); - [Params(TestImages.Tga.Bit24BottomLeft)] - public string TestImage { get; set; } + private byte[] data; - [GlobalSetup] - public void SetupData() - => this.data = File.ReadAllBytes(this.TestImageFullPath); + [Params(TestImages.Tga.Bit24BottomLeft)] + public string TestImage { get; set; } - [Benchmark(Baseline = true, Description = "ImageMagick Tga")] - public int TgaImageMagick() - { - var settings = new MagickReadSettings { Format = MagickFormat.Tga }; - using var image = new MagickImage(new MemoryStream(this.data), settings); - return image.Width; - } + [GlobalSetup] + public void SetupData() + => this.data = File.ReadAllBytes(this.TestImageFullPath); - [Benchmark(Description = "ImageSharp Tga")] - public int TgaImageSharp() - { - using var image = Image.Load(this.data); - return image.Width; - } + [Benchmark(Baseline = true, Description = "ImageMagick Tga")] + public int TgaImageMagick() + { + var settings = new MagickReadSettings { Format = MagickFormat.Tga }; + using var image = new MagickImage(new MemoryStream(this.data), settings); + return image.Width; + } - [Benchmark(Description = "Pfim Tga")] - public int TgaPfim() - { - using var image = Targa.Create(this.data, this.pfimConfig); - return image.Width; - } + [Benchmark(Description = "ImageSharp Tga")] + public int TgaImageSharp() + { + using var image = Image.Load(this.data); + return image.Width; + } - private class PfimAllocator : IImageAllocator - { - private int rented; - private readonly ArrayPool shared = ArrayPool.Shared; + [Benchmark(Description = "Pfim Tga")] + public int TgaPfim() + { + using var image = Targa.Create(this.data, this.pfimConfig); + return image.Width; + } - public byte[] Rent(int size) => this.shared.Rent(size); + private class PfimAllocator : IImageAllocator + { + private int rented; + private readonly ArrayPool shared = ArrayPool.Shared; - public void Return(byte[] data) - { - Interlocked.Decrement(ref this.rented); - this.shared.Return(data); - } + public byte[] Rent(int size) => this.shared.Rent(size); - public int Rented => this.rented; + public void Return(byte[] data) + { + Interlocked.Decrement(ref this.rented); + this.shared.Return(data); } - /* RESULTS (07/01/2020) - | Method | Runtime | TestImage | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | - |------------------ |-------------- |-------------------- |-------------:|-------------:|-----------:|------:|-------:|------:|------:|----------:| - | 'ImageMagick Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 1,778.965 us | 1,711.088 us | 93.7905 us | 1.000 | 1.9531 | - | - | 13668 B | - | 'ImageSharp Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 38.659 us | 6.886 us | 0.3774 us | 0.022 | 0.3052 | - | - | 1316 B | - | 'Pfim Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 6.752 us | 10.268 us | 0.5628 us | 0.004 | 0.0687 | - | - | 313 B | - | | | | | | | | | | | | - | 'ImageMagick Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 1,407.585 us | 124.215 us | 6.8087 us | 1.000 | 1.9531 | - | - | 13307 B | - | 'ImageSharp Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 17.958 us | 9.352 us | 0.5126 us | 0.013 | 0.2747 | - | - | 1256 B | - | 'Pfim Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 5.645 us | 2.279 us | 0.1249 us | 0.004 | 0.0610 | - | - | 280 B | - */ + public int Rented => this.rented; } + + /* RESULTS (07/01/2020) + | Method | Runtime | TestImage | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | + |------------------ |-------------- |-------------------- |-------------:|-------------:|-----------:|------:|-------:|------:|------:|----------:| + | 'ImageMagick Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 1,778.965 us | 1,711.088 us | 93.7905 us | 1.000 | 1.9531 | - | - | 13668 B | + | 'ImageSharp Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 38.659 us | 6.886 us | 0.3774 us | 0.022 | 0.3052 | - | - | 1316 B | + | 'Pfim Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 6.752 us | 10.268 us | 0.5628 us | 0.004 | 0.0687 | - | - | 313 B | + | | | | | | | | | | | | + | 'ImageMagick Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 1,407.585 us | 124.215 us | 6.8087 us | 1.000 | 1.9531 | - | - | 13307 B | + | 'ImageSharp Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 17.958 us | 9.352 us | 0.5126 us | 0.013 | 0.2747 | - | - | 1256 B | + | 'Pfim Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 5.645 us | 2.279 us | 0.1249 us | 0.004 | 0.0610 | - | - | 280 B | + */ } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs index 64c15c8fec..935706b41b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs @@ -1,56 +1,54 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using ImageMagick; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeTga { - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeTga - { - private MagickImage tgaMagick; - private Image tga; + private MagickImage tgaMagick; + private Image tga; - private string TestImageFullPath - => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath + => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Tga.Bit24BottomLeft)] - public string TestImage { get; set; } + [Params(TestImages.Tga.Bit24BottomLeft)] + public string TestImage { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.tga == null) { - if (this.tga == null) - { - this.tga = Image.Load(this.TestImageFullPath); - this.tgaMagick = new MagickImage(this.TestImageFullPath); - } + this.tga = Image.Load(this.TestImageFullPath); + this.tgaMagick = new MagickImage(this.TestImageFullPath); } + } - [GlobalCleanup] - public void Cleanup() - { - this.tga.Dispose(); - this.tga = null; - this.tgaMagick.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.tga.Dispose(); + this.tga = null; + this.tgaMagick.Dispose(); + } - [Benchmark(Baseline = true, Description = "Magick Tga")] - public void MagickTga() - { - using var memoryStream = new MemoryStream(); - this.tgaMagick.Write(memoryStream, MagickFormat.Tga); - } + [Benchmark(Baseline = true, Description = "Magick Tga")] + public void MagickTga() + { + using var memoryStream = new MemoryStream(); + this.tgaMagick.Write(memoryStream, MagickFormat.Tga); + } - [Benchmark(Description = "ImageSharp Tga")] - public void ImageSharpTga() - { - using var memoryStream = new MemoryStream(); - this.tga.SaveAsTga(memoryStream); - } + [Benchmark(Description = "ImageSharp Tga")] + public void ImageSharpTga() + { + using var memoryStream = new MemoryStream(); + this.tga.SaveAsTga(memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index a0752fa1f7..4edb251fa7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs @@ -5,86 +5,84 @@ // Use the scripts gen_big.ps1 and gen_medium.ps1 in tests\Images\Input\Tiff\Benchmarks to generate those images. //// #define BIG_TESTS -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; using SDSize = System.Drawing.Size; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[MarkdownExporter] +[HtmlExporter] +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeTiff { - [MarkdownExporter] - [HtmlExporter] - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeTiff - { - private string prevImage; + private string prevImage; - private byte[] data; + private byte[] data; #if BIG_TESTS - private static readonly int BufferSize = 1024 * 68; + private static readonly int BufferSize = 1024 * 68; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Path.Combine(TestImages.Tiff.Benchmark_Path, this.TestImage)); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Path.Combine(TestImages.Tiff.Benchmark_Path, this.TestImage)); - [Params( - TestImages.Tiff.Benchmark_BwFax3, - //// TestImages.Tiff.Benchmark_RgbFax4, // fax4 is not supported yet. - TestImages.Tiff.Benchmark_GrayscaleUncompressed, - TestImages.Tiff.Benchmark_PaletteUncompressed, - TestImages.Tiff.Benchmark_RgbDeflate, - TestImages.Tiff.Benchmark_RgbLzw, - TestImages.Tiff.Benchmark_RgbPackbits, - TestImages.Tiff.Benchmark_RgbUncompressed)] - public string TestImage { get; set; } + [Params( + TestImages.Tiff.Benchmark_BwFax3, + //// TestImages.Tiff.Benchmark_RgbFax4, // fax4 is not supported yet. + TestImages.Tiff.Benchmark_GrayscaleUncompressed, + TestImages.Tiff.Benchmark_PaletteUncompressed, + TestImages.Tiff.Benchmark_RgbDeflate, + TestImages.Tiff.Benchmark_RgbLzw, + TestImages.Tiff.Benchmark_RgbPackbits, + TestImages.Tiff.Benchmark_RgbUncompressed)] + public string TestImage { get; set; } #else - private static readonly int BufferSize = Configuration.Default.StreamProcessingBufferSize; + private static readonly int BufferSize = Configuration.Default.StreamProcessingBufferSize; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params( - TestImages.Tiff.CcittFax3AllTermCodes, - TestImages.Tiff.Fax4Compressed2, - TestImages.Tiff.HuffmanRleAllMakeupCodes, - TestImages.Tiff.Calliphora_GrayscaleUncompressed, - TestImages.Tiff.Calliphora_RgbPaletteLzw_Predictor, - TestImages.Tiff.Calliphora_RgbDeflate_Predictor, - TestImages.Tiff.Calliphora_RgbLzwPredictor, - TestImages.Tiff.Calliphora_RgbPackbits, - TestImages.Tiff.Calliphora_RgbUncompressed)] - public string TestImage { get; set; } + [Params( + TestImages.Tiff.CcittFax3AllTermCodes, + TestImages.Tiff.Fax4Compressed2, + TestImages.Tiff.HuffmanRleAllMakeupCodes, + TestImages.Tiff.Calliphora_GrayscaleUncompressed, + TestImages.Tiff.Calliphora_RgbPaletteLzw_Predictor, + TestImages.Tiff.Calliphora_RgbDeflate_Predictor, + TestImages.Tiff.Calliphora_RgbLzwPredictor, + TestImages.Tiff.Calliphora_RgbPackbits, + TestImages.Tiff.Calliphora_RgbUncompressed)] + public string TestImage { get; set; } #endif - [IterationSetup] - public void ReadImages() + [IterationSetup] + public void ReadImages() + { + if (this.prevImage != this.TestImage) { - if (this.prevImage != this.TestImage) - { - this.data = File.ReadAllBytes(this.TestImageFullPath); - this.prevImage = this.TestImage; - } + this.data = File.ReadAllBytes(this.TestImageFullPath); + this.prevImage = this.TestImage; } + } - [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] - public SDSize TiffSystemDrawing() + [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] + public SDSize TiffSystemDrawing() + { + using (var memoryStream = new MemoryStream(this.data)) + using (var image = SDImage.FromStream(memoryStream)) { - using (var memoryStream = new MemoryStream(this.data)) - using (var image = SDImage.FromStream(memoryStream)) - { - return image.Size; - } + return image.Size; } + } - [Benchmark(Description = "ImageSharp Tiff")] - public Size TiffCore() + [Benchmark(Description = "ImageSharp Tiff")] + public Size TiffCore() + { + using (var ms = new MemoryStream(this.data)) + using (var image = Image.Load(ms)) { - using (var ms = new MemoryStream(this.data)) - using (var image = Image.Load(ms)) - { - return image.Size(); - } + return image.Size(); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs index eea23a7aa6..ab3b0e95ea 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -1,9 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Drawing.Imaging; -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Tiff; @@ -12,122 +10,121 @@ using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[MarkdownExporter] +[HtmlExporter] +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeTiff { - [MarkdownExporter] - [HtmlExporter] - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeTiff - { - private Stream stream; - private SDImage drawing; - private Image core; + private Stream stream; + private SDImage drawing; + private Image core; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Tiff.Calliphora_RgbUncompressed)] - public string TestImage { get; set; } + [Params(TestImages.Tiff.Calliphora_RgbUncompressed)] + public string TestImage { get; set; } - [Params( - TiffCompression.None, + [Params( + TiffCompression.None, - // System.Drawing does not support Deflate or PackBits - // TiffCompression.Deflate, - // TiffCompression.PackBits, - TiffCompression.Lzw, - TiffCompression.CcittGroup3Fax, - TiffCompression.Ccitt1D)] - public TiffCompression Compression { get; set; } + // System.Drawing does not support Deflate or PackBits + // TiffCompression.Deflate, + // TiffCompression.PackBits, + TiffCompression.Lzw, + TiffCompression.CcittGroup3Fax, + TiffCompression.Ccitt1D)] + public TiffCompression Compression { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.stream == null) { - if (this.stream == null) - { - this.stream = File.OpenRead(this.TestImageFullPath); - this.core = Image.Load(this.stream); - this.stream.Position = 0; - this.drawing = SDImage.FromStream(this.stream); - } + this.stream = File.OpenRead(this.TestImageFullPath); + this.core = Image.Load(this.stream); + this.stream.Position = 0; + this.drawing = SDImage.FromStream(this.stream); } + } - [GlobalCleanup] - public void Cleanup() - { - this.core.Dispose(); - this.drawing.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.core.Dispose(); + this.drawing.Dispose(); + } - [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] - public void SystemDrawing() + [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] + public void SystemDrawing() + { + ImageCodecInfo codec = FindCodecForType("image/tiff"); + using var parameters = new EncoderParameters(1) { - ImageCodecInfo codec = FindCodecForType("image/tiff"); - using var parameters = new EncoderParameters(1) - { - Param = { [0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)) } - }; + Param = { [0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)) } + }; - using var memoryStream = new MemoryStream(); - this.drawing.Save(memoryStream, codec, parameters); - } + using var memoryStream = new MemoryStream(); + this.drawing.Save(memoryStream, codec, parameters); + } - [Benchmark(Description = "ImageSharp Tiff")] - public void TiffCore() - { - TiffPhotometricInterpretation photometricInterpretation = - IsOneBitCompression(this.Compression) ? - TiffPhotometricInterpretation.WhiteIsZero : - TiffPhotometricInterpretation.Rgb; - - var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; - using var memoryStream = new MemoryStream(); - this.core.SaveAsTiff(memoryStream, encoder); - } + [Benchmark(Description = "ImageSharp Tiff")] + public void TiffCore() + { + TiffPhotometricInterpretation photometricInterpretation = + IsOneBitCompression(this.Compression) ? + TiffPhotometricInterpretation.WhiteIsZero : + TiffPhotometricInterpretation.Rgb; + + var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; + using var memoryStream = new MemoryStream(); + this.core.SaveAsTiff(memoryStream, encoder); + } - private static ImageCodecInfo FindCodecForType(string mimeType) - { - ImageCodecInfo[] imgEncoders = ImageCodecInfo.GetImageEncoders(); + private static ImageCodecInfo FindCodecForType(string mimeType) + { + ImageCodecInfo[] imgEncoders = ImageCodecInfo.GetImageEncoders(); - for (int i = 0; i < imgEncoders.GetLength(0); i++) + for (int i = 0; i < imgEncoders.GetLength(0); i++) + { + if (imgEncoders[i].MimeType == mimeType) { - if (imgEncoders[i].MimeType == mimeType) - { - return imgEncoders[i]; - } + return imgEncoders[i]; } - - return null; } - private static EncoderValue Cast(TiffCompression compression) + return null; + } + + private static EncoderValue Cast(TiffCompression compression) + { + switch (compression) { - switch (compression) - { - case TiffCompression.None: - return EncoderValue.CompressionNone; + case TiffCompression.None: + return EncoderValue.CompressionNone; - case TiffCompression.CcittGroup3Fax: - return EncoderValue.CompressionCCITT3; + case TiffCompression.CcittGroup3Fax: + return EncoderValue.CompressionCCITT3; - case TiffCompression.Ccitt1D: - return EncoderValue.CompressionRle; + case TiffCompression.Ccitt1D: + return EncoderValue.CompressionRle; - case TiffCompression.Lzw: - return EncoderValue.CompressionLZW; + case TiffCompression.Lzw: + return EncoderValue.CompressionLZW; - default: - throw new NotSupportedException(compression.ToString()); - } + default: + throw new NotSupportedException(compression.ToString()); } + } - public static bool IsOneBitCompression(TiffCompression compression) + public static bool IsOneBitCompression(TiffCompression compression) + { + if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) { - if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) - { - return true; - } - - return false; + return true; } + + return false; } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs index f49310b9c3..34a4ad5931 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using ImageMagick; @@ -9,96 +8,95 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[MarkdownExporter] +[HtmlExporter] +[Config(typeof(Config.ShortMultiFramework))] +public class DecodeWebp { - [MarkdownExporter] - [HtmlExporter] - [Config(typeof(Config.ShortMultiFramework))] - public class DecodeWebp + private Configuration configuration; + + private byte[] webpLossyBytes; + + private byte[] webpLosslessBytes; + + private string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImageLossy); + + private string TestImageLosslessFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImageLossless); + + [Params(TestImages.Webp.Lossy.Earth)] + public string TestImageLossy { get; set; } + + [Params(TestImages.Webp.Lossless.Earth)] + public string TestImageLossless { get; set; } + + [GlobalSetup] + public void ReadImages() + { + this.configuration = Configuration.CreateDefaultInstance(); + new WebpConfigurationModule().Configure(this.configuration); + + this.webpLossyBytes ??= File.ReadAllBytes(this.TestImageLossyFullPath); + this.webpLosslessBytes ??= File.ReadAllBytes(this.TestImageLosslessFullPath); + } + + [Benchmark(Description = "Magick Lossy Webp")] + public int WebpLossyMagick() + { + var settings = new MagickReadSettings { Format = MagickFormat.WebP }; + using var memoryStream = new MemoryStream(this.webpLossyBytes); + using var image = new MagickImage(memoryStream, settings); + return image.Width; + } + + [Benchmark(Description = "ImageSharp Lossy Webp")] + public int WebpLossy() { - private Configuration configuration; - - private byte[] webpLossyBytes; - - private byte[] webpLosslessBytes; - - private string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImageLossy); - - private string TestImageLosslessFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImageLossless); - - [Params(TestImages.Webp.Lossy.Earth)] - public string TestImageLossy { get; set; } - - [Params(TestImages.Webp.Lossless.Earth)] - public string TestImageLossless { get; set; } - - [GlobalSetup] - public void ReadImages() - { - this.configuration = Configuration.CreateDefaultInstance(); - new WebpConfigurationModule().Configure(this.configuration); - - this.webpLossyBytes ??= File.ReadAllBytes(this.TestImageLossyFullPath); - this.webpLosslessBytes ??= File.ReadAllBytes(this.TestImageLosslessFullPath); - } - - [Benchmark(Description = "Magick Lossy Webp")] - public int WebpLossyMagick() - { - var settings = new MagickReadSettings { Format = MagickFormat.WebP }; - using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = new MagickImage(memoryStream, settings); - return image.Width; - } - - [Benchmark(Description = "ImageSharp Lossy Webp")] - public int WebpLossy() - { - using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = Image.Load(memoryStream); - return image.Height; - } - - [Benchmark(Description = "Magick Lossless Webp")] - public int WebpLosslessMagick() - { - var settings = new MagickReadSettings { Format = MagickFormat.WebP }; - using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = new MagickImage(memoryStream, settings); - return image.Width; - } - - [Benchmark(Description = "ImageSharp Lossless Webp")] - public int WebpLossless() - { - using var memoryStream = new MemoryStream(this.webpLosslessBytes); - using var image = Image.Load(memoryStream); - return image.Height; - } - - /* Results 04.11.2021 - * BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1320 (21H1/May2021Update) - Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores - .NET SDK=6.0.100-rc.2.21505.57 - [Host] : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT - Job-WQLXJO : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT - Job-OJJAMD : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT - Job-OMFOAS : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT - - | Method | Job | Runtime | Arguments | TestImageLossy | TestImageLossless | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | - |--------------------------- |----------- |--------------------- |---------------------- |---------------------- |------------------------- |-----------:|----------:|--------:|---------:|------:|------:|----------:| - | 'Magick Lossy Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 107.9 ms | 28.91 ms | 1.58 ms | - | - | - | 25 KB | - | 'ImageSharp Lossy Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 282.3 ms | 25.40 ms | 1.39 ms | 500.0000 | - | - | 2,428 KB | - | 'Magick Lossless Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.3 ms | 11.99 ms | 0.66 ms | - | - | - | 16 KB | - | 'ImageSharp Lossless Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 280.2 ms | 6.21 ms | 0.34 ms | - | - | - | 2,092 KB | - | 'Magick Lossy Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.2 ms | 9.32 ms | 0.51 ms | - | - | - | 15 KB | - | 'ImageSharp Lossy Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 295.8 ms | 21.25 ms | 1.16 ms | 500.0000 | - | - | 2,427 KB | - | 'Magick Lossless Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.5 ms | 4.07 ms | 0.22 ms | - | - | - | 15 KB | - | 'ImageSharp Lossless Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 464.0 ms | 55.70 ms | 3.05 ms | - | - | - | 2,090 KB | - | 'Magick Lossy Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 108.0 ms | 29.60 ms | 1.62 ms | - | - | - | 32 KB | - | 'ImageSharp Lossy Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 564.9 ms | 29.69 ms | 1.63 ms | - | - | - | 2,436 KB | - | 'Magick Lossless Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.2 ms | 4.74 ms | 0.26 ms | - | - | - | 18 KB | - | 'ImageSharp Lossless Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 1,767.5 ms | 106.33 ms | 5.83 ms | - | - | - | 9,729 KB | - */ + using var memoryStream = new MemoryStream(this.webpLossyBytes); + using var image = Image.Load(memoryStream); + return image.Height; } + + [Benchmark(Description = "Magick Lossless Webp")] + public int WebpLosslessMagick() + { + var settings = new MagickReadSettings { Format = MagickFormat.WebP }; + using var memoryStream = new MemoryStream(this.webpLossyBytes); + using var image = new MagickImage(memoryStream, settings); + return image.Width; + } + + [Benchmark(Description = "ImageSharp Lossless Webp")] + public int WebpLossless() + { + using var memoryStream = new MemoryStream(this.webpLosslessBytes); + using var image = Image.Load(memoryStream); + return image.Height; + } + + /* Results 04.11.2021 + * BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1320 (21H1/May2021Update) + Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores + .NET SDK=6.0.100-rc.2.21505.57 + [Host] : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT + Job-WQLXJO : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT + Job-OJJAMD : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT + Job-OMFOAS : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT + + | Method | Job | Runtime | Arguments | TestImageLossy | TestImageLossless | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | + |--------------------------- |----------- |--------------------- |---------------------- |---------------------- |------------------------- |-----------:|----------:|--------:|---------:|------:|------:|----------:| + | 'Magick Lossy Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 107.9 ms | 28.91 ms | 1.58 ms | - | - | - | 25 KB | + | 'ImageSharp Lossy Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 282.3 ms | 25.40 ms | 1.39 ms | 500.0000 | - | - | 2,428 KB | + | 'Magick Lossless Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.3 ms | 11.99 ms | 0.66 ms | - | - | - | 16 KB | + | 'ImageSharp Lossless Webp' | Job-HLWZLL | .NET 5.0 | /p:DebugType=portable | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 280.2 ms | 6.21 ms | 0.34 ms | - | - | - | 2,092 KB | + | 'Magick Lossy Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.2 ms | 9.32 ms | 0.51 ms | - | - | - | 15 KB | + | 'ImageSharp Lossy Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 295.8 ms | 21.25 ms | 1.16 ms | 500.0000 | - | - | 2,427 KB | + | 'Magick Lossless Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.5 ms | 4.07 ms | 0.22 ms | - | - | - | 15 KB | + | 'ImageSharp Lossless Webp' | Job-ALQPDS | .NET Core 3.1 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 464.0 ms | 55.70 ms | 3.05 ms | - | - | - | 2,090 KB | + | 'Magick Lossy Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 108.0 ms | 29.60 ms | 1.62 ms | - | - | - | 32 KB | + | 'ImageSharp Lossy Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 564.9 ms | 29.69 ms | 1.63 ms | - | - | - | 2,436 KB | + | 'Magick Lossless Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 106.2 ms | 4.74 ms | 0.26 ms | - | - | - | 18 KB | + | 'ImageSharp Lossless Webp' | Job-RYVVNN | .NET Framework 4.7.2 | Default | Webp/earth_lossy.webp | Webp/earth_lossless.webp | 1,767.5 ms | 106.33 ms | 5.83 ms | - | - | - | 9,729 KB | + */ } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs index ee36b1bd03..65b4ae2c31 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using ImageMagick; using ImageMagick.Formats; @@ -9,135 +8,134 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Codecs +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[MarkdownExporter] +[HtmlExporter] +[Config(typeof(Config.ShortMultiFramework))] +public class EncodeWebp { - [MarkdownExporter] - [HtmlExporter] - [Config(typeof(Config.ShortMultiFramework))] - public class EncodeWebp - { - private MagickImage webpMagick; - private Image webp; + private MagickImage webpMagick; + private Image webp; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Png.Bike)] // The bike image will have all 3 transforms as lossless webp. - public string TestImage { get; set; } + [Params(TestImages.Png.Bike)] // The bike image will have all 3 transforms as lossless webp. + public string TestImage { get; set; } - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.webp == null) { - if (this.webp == null) - { - this.webp = Image.Load(this.TestImageFullPath); - this.webpMagick = new MagickImage(this.TestImageFullPath); - } + this.webp = Image.Load(this.TestImageFullPath); + this.webpMagick = new MagickImage(this.TestImageFullPath); } + } - [GlobalCleanup] - public void Cleanup() - { - this.webp.Dispose(); - this.webpMagick.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.webp.Dispose(); + this.webpMagick.Dispose(); + } + + [Benchmark(Description = "Magick Webp Lossy")] + public void MagickWebpLossy() + { + using var memoryStream = new MemoryStream(); - [Benchmark(Description = "Magick Webp Lossy")] - public void MagickWebpLossy() + var defines = new WebPWriteDefines { - using var memoryStream = new MemoryStream(); - - var defines = new WebPWriteDefines - { - Lossless = false, - Method = 4, - AlphaCompression = WebPAlphaCompression.None, - FilterStrength = 60, - SnsStrength = 50, - Pass = 1, - - // 100 means off. - NearLossless = 100 - }; - - this.webpMagick.Quality = 75; - this.webpMagick.Write(memoryStream, defines); - } + Lossless = false, + Method = 4, + AlphaCompression = WebPAlphaCompression.None, + FilterStrength = 60, + SnsStrength = 50, + Pass = 1, + + // 100 means off. + NearLossless = 100 + }; + + this.webpMagick.Quality = 75; + this.webpMagick.Write(memoryStream, defines); + } - [Benchmark(Description = "ImageSharp Webp Lossy")] - public void ImageSharpWebpLossy() + [Benchmark(Description = "ImageSharp Webp Lossy")] + public void ImageSharpWebpLossy() + { + using var memoryStream = new MemoryStream(); + this.webp.Save(memoryStream, new WebpEncoder() { - using var memoryStream = new MemoryStream(); - this.webp.Save(memoryStream, new WebpEncoder() - { - FileFormat = WebpFileFormatType.Lossy, - Method = WebpEncodingMethod.Level4, - UseAlphaCompression = false, - FilterStrength = 60, - SpatialNoiseShaping = 50, - EntropyPasses = 1 - }); - } + FileFormat = WebpFileFormatType.Lossy, + Method = WebpEncodingMethod.Level4, + UseAlphaCompression = false, + FilterStrength = 60, + SpatialNoiseShaping = 50, + EntropyPasses = 1 + }); + } - [Benchmark(Baseline = true, Description = "Magick Webp Lossless")] - public void MagickWebpLossless() + [Benchmark(Baseline = true, Description = "Magick Webp Lossless")] + public void MagickWebpLossless() + { + using var memoryStream = new MemoryStream(); + var defines = new WebPWriteDefines { - using var memoryStream = new MemoryStream(); - var defines = new WebPWriteDefines - { - Lossless = true, - Method = 4, - - // 100 means off. - NearLossless = 100 - }; - - this.webpMagick.Quality = 75; - this.webpMagick.Write(memoryStream, defines); - } + Lossless = true, + Method = 4, + + // 100 means off. + NearLossless = 100 + }; - [Benchmark(Description = "ImageSharp Webp Lossless")] - public void ImageSharpWebpLossless() + this.webpMagick.Quality = 75; + this.webpMagick.Write(memoryStream, defines); + } + + [Benchmark(Description = "ImageSharp Webp Lossless")] + public void ImageSharpWebpLossless() + { + using var memoryStream = new MemoryStream(); + this.webp.Save(memoryStream, new WebpEncoder() { - using var memoryStream = new MemoryStream(); - this.webp.Save(memoryStream, new WebpEncoder() - { - FileFormat = WebpFileFormatType.Lossless, - Method = WebpEncodingMethod.Level4, - NearLossless = false, - - // This is equal to exact = false in libwebp, which is the default. - TransparentColorMode = WebpTransparentColorMode.Clear - }); - } + FileFormat = WebpFileFormatType.Lossless, + Method = WebpEncodingMethod.Level4, + NearLossless = false, - /* Results 04.11.2021 - * Summary * - BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1320 (21H1/May2021Update) - Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores - .NET SDK=6.0.100-rc.2.21505.57 - [Host] : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT - Job-WQLXJO : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT - Job-OJJAMD : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT - Job-OMFOAS : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT - - IterationCount=3 LaunchCount=1 WarmupCount=3 - - | Method | Job | Runtime | Arguments | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - |--------------------------- |----------- |--------------------- |---------------------- |------------- |----------:|----------:|---------:|------:|--------:|------------:|----------:|----------:|-----------:| - | 'Magick Webp Lossy' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 23.33 ms | 1.491 ms | 0.082 ms | 0.15 | 0.00 | - | - | - | 67 KB | - | 'ImageSharp Webp Lossy' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 245.80 ms | 24.288 ms | 1.331 ms | 1.53 | 0.01 | 135000.0000 | - | - | 552,713 KB | - | 'Magick Webp Lossless' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 160.36 ms | 11.131 ms | 0.610 ms | 1.00 | 0.00 | - | - | - | 518 KB | - | 'ImageSharp Webp Lossless' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 313.93 ms | 45.605 ms | 2.500 ms | 1.96 | 0.01 | 34000.0000 | 5000.0000 | 2000.0000 | 161,670 KB | - | | | | | | | | | | | | | | | - | 'Magick Webp Lossy' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 23.36 ms | 2.289 ms | 0.125 ms | 0.15 | 0.00 | - | - | - | 67 KB | - | 'ImageSharp Webp Lossy' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 254.64 ms | 19.620 ms | 1.075 ms | 1.59 | 0.00 | 135000.0000 | - | - | 552,713 KB | - | 'Magick Webp Lossless' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 160.30 ms | 9.549 ms | 0.523 ms | 1.00 | 0.00 | - | - | - | 518 KB | - | 'ImageSharp Webp Lossless' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 320.35 ms | 22.924 ms | 1.257 ms | 2.00 | 0.01 | 34000.0000 | 5000.0000 | 2000.0000 | 161,669 KB | - | | | | | | | | | | | | | | | - | 'Magick Webp Lossy' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 23.37 ms | 0.908 ms | 0.050 ms | 0.15 | 0.00 | - | - | - | 68 KB | - | 'ImageSharp Webp Lossy' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 378.67 ms | 25.540 ms | 1.400 ms | 2.36 | 0.01 | 135000.0000 | - | - | 554,351 KB | - | 'Magick Webp Lossless' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 160.13 ms | 5.115 ms | 0.280 ms | 1.00 | 0.00 | - | - | - | 520 KB | - | 'ImageSharp Webp Lossless' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 379.01 ms | 71.192 ms | 3.902 ms | 2.37 | 0.02 | 34000.0000 | 5000.0000 | 2000.0000 | 162,119 KB | - */ + // This is equal to exact = false in libwebp, which is the default. + TransparentColorMode = WebpTransparentColorMode.Clear + }); } + + /* Results 04.11.2021 + * Summary * + BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1320 (21H1/May2021Update) + Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores + .NET SDK=6.0.100-rc.2.21505.57 + [Host] : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT + Job-WQLXJO : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT + Job-OJJAMD : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT + Job-OMFOAS : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT + + IterationCount=3 LaunchCount=1 WarmupCount=3 + + | Method | Job | Runtime | Arguments | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + |--------------------------- |----------- |--------------------- |---------------------- |------------- |----------:|----------:|---------:|------:|--------:|------------:|----------:|----------:|-----------:| + | 'Magick Webp Lossy' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 23.33 ms | 1.491 ms | 0.082 ms | 0.15 | 0.00 | - | - | - | 67 KB | + | 'ImageSharp Webp Lossy' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 245.80 ms | 24.288 ms | 1.331 ms | 1.53 | 0.01 | 135000.0000 | - | - | 552,713 KB | + | 'Magick Webp Lossless' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 160.36 ms | 11.131 ms | 0.610 ms | 1.00 | 0.00 | - | - | - | 518 KB | + | 'ImageSharp Webp Lossless' | Job-WQLXJO | .NET 5.0 | /p:DebugType=portable | Png/Bike.png | 313.93 ms | 45.605 ms | 2.500 ms | 1.96 | 0.01 | 34000.0000 | 5000.0000 | 2000.0000 | 161,670 KB | + | | | | | | | | | | | | | | | + | 'Magick Webp Lossy' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 23.36 ms | 2.289 ms | 0.125 ms | 0.15 | 0.00 | - | - | - | 67 KB | + | 'ImageSharp Webp Lossy' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 254.64 ms | 19.620 ms | 1.075 ms | 1.59 | 0.00 | 135000.0000 | - | - | 552,713 KB | + | 'Magick Webp Lossless' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 160.30 ms | 9.549 ms | 0.523 ms | 1.00 | 0.00 | - | - | - | 518 KB | + | 'ImageSharp Webp Lossless' | Job-OJJAMD | .NET Core 3.1 | Default | Png/Bike.png | 320.35 ms | 22.924 ms | 1.257 ms | 2.00 | 0.01 | 34000.0000 | 5000.0000 | 2000.0000 | 161,669 KB | + | | | | | | | | | | | | | | | + | 'Magick Webp Lossy' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 23.37 ms | 0.908 ms | 0.050 ms | 0.15 | 0.00 | - | - | - | 68 KB | + | 'ImageSharp Webp Lossy' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 378.67 ms | 25.540 ms | 1.400 ms | 2.36 | 0.01 | 135000.0000 | - | - | 554,351 KB | + | 'Magick Webp Lossless' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 160.13 ms | 5.115 ms | 0.280 ms | 1.00 | 0.00 | - | - | - | 520 KB | + | 'ImageSharp Webp Lossless' | Job-OMFOAS | .NET Framework 4.7.2 | Default | Png/Bike.png | 379.01 ms | 71.192 ms | 3.902 ms | 2.37 | 0.02 | 34000.0000 | 5000.0000 | 2000.0000 | 162,119 KB | + */ } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs index 8909b2c0d7..e27bf07e99 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; using BenchmarkDotNet.Attributes; @@ -9,82 +8,81 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - public abstract class FromRgba32Bytes - where TPixel : unmanaged, IPixel - { - private IMemoryOwner destination; - - private IMemoryOwner source; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - private Configuration configuration; +public abstract class FromRgba32Bytes + where TPixel : unmanaged, IPixel +{ + private IMemoryOwner destination; - [Params( - 128, - 1024, - 2048)] - public int Count { get; set; } + private IMemoryOwner source; - [GlobalSetup] - public void Setup() - { - this.configuration = Configuration.Default; - this.destination = this.configuration.MemoryAllocator.Allocate(this.Count); - this.source = this.configuration.MemoryAllocator.Allocate(this.Count * 4); - } + private Configuration configuration; - [GlobalCleanup] - public void Cleanup() - { - this.destination.Dispose(); - this.source.Dispose(); - } + [Params( + 128, + 1024, + 2048)] + public int Count { get; set; } - // [Benchmark] - public void Naive() - { - Span s = this.source.GetSpan(); - Span d = this.destination.GetSpan(); + [GlobalSetup] + public void Setup() + { + this.configuration = Configuration.Default; + this.destination = this.configuration.MemoryAllocator.Allocate(this.Count); + this.source = this.configuration.MemoryAllocator.Allocate(this.Count * 4); + } - for (int i = 0; i < this.Count; i++) - { - int i4 = i * 4; - var c = default(TPixel); - c.FromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); - d[i] = c; - } - } + [GlobalCleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } - [Benchmark(Baseline = true)] - public void CommonBulk() - { - new PixelOperations().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } + // [Benchmark] + public void Naive() + { + Span s = this.source.GetSpan(); + Span d = this.destination.GetSpan(); - [Benchmark] - public void OptimizedBulk() + for (int i = 0; i < this.Count; i++) { - PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); + int i4 = i * 4; + var c = default(TPixel); + c.FromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); + d[i] = c; } } - public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes + [Benchmark(Baseline = true)] + public void CommonBulk() { + new PixelOperations().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); } - public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes + [Benchmark] + public void OptimizedBulk() { - // RESULTS: - // Method | Count | Mean | Error | StdDev | Scaled | - // -------------- |------ |-----------:|----------:|----------:|-------:| - // CommonBulk | 128 | 207.1 ns | 3.723 ns | 3.300 ns | 1.00 | - // OptimizedBulk | 128 | 166.5 ns | 1.204 ns | 1.005 ns | 0.80 | - // | | | | | | - // CommonBulk | 1024 | 1,333.9 ns | 12.426 ns | 11.624 ns | 1.00 | - // OptimizedBulk | 1024 | 974.1 ns | 18.803 ns | 16.669 ns | 0.73 | - // | | | | | | - // CommonBulk | 2048 | 2,625.4 ns | 30.143 ns | 26.721 ns | 1.00 | - // OptimizedBulk | 2048 | 1,843.0 ns | 20.505 ns | 18.177 ns | 0.70 | + PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); } } + +public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes +{ +} + +public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes +{ + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | + // -------------- |------ |-----------:|----------:|----------:|-------:| + // CommonBulk | 128 | 207.1 ns | 3.723 ns | 3.300 ns | 1.00 | + // OptimizedBulk | 128 | 166.5 ns | 1.204 ns | 1.005 ns | 0.80 | + // | | | | | | + // CommonBulk | 1024 | 1,333.9 ns | 12.426 ns | 11.624 ns | 1.00 | + // OptimizedBulk | 1024 | 974.1 ns | 18.803 ns | 16.669 ns | 0.73 | + // | | | | | | + // CommonBulk | 2048 | 2,625.4 ns | 30.143 ns | 26.721 ns | 1.00 | + // OptimizedBulk | 2048 | 1,843.0 ns | 20.505 ns | 18.177 ns | 0.70 | +} diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs index bb2985ab71..d1cb616780 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; @@ -13,156 +12,155 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - [Config(typeof(Config.ShortCore31))] - public abstract class FromVector4 - where TPixel : unmanaged, IPixel - { - protected IMemoryOwner source; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - protected IMemoryOwner destination; +[Config(typeof(Config.ShortCore31))] +public abstract class FromVector4 + where TPixel : unmanaged, IPixel +{ + protected IMemoryOwner source; - protected Configuration Configuration => Configuration.Default; + protected IMemoryOwner destination; - // [Params(64, 2048)] - [Params(64, 256, 2048)] - public int Count { get; set; } + protected Configuration Configuration => Configuration.Default; - [GlobalSetup] - public void Setup() - { - this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); - this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); - } + // [Params(64, 2048)] + [Params(64, 256, 2048)] + public int Count { get; set; } - [GlobalCleanup] - public void Cleanup() - { - this.destination.Dispose(); - this.source.Dispose(); - } + [GlobalSetup] + public void Setup() + { + this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); + } - // [Benchmark] - public void PerElement() - { - ref Vector4 s = ref MemoryMarshal.GetReference(this.source.GetSpan()); - ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); - for (int i = 0; i < this.Count; i++) - { - Unsafe.Add(ref d, i).FromVector4(Unsafe.Add(ref s, i)); - } - } + [GlobalCleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } - [Benchmark(Baseline = true)] - public void PixelOperations_Base() + // [Benchmark] + public void PerElement() + { + ref Vector4 s = ref MemoryMarshal.GetReference(this.source.GetSpan()); + ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); + for (int i = 0; i < this.Count; i++) { - new PixelOperations().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + Unsafe.Add(ref d, i).FromVector4(Unsafe.Add(ref s, i)); } + } - [Benchmark] - public void PixelOperations_Specialized() - { - PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); - } + [Benchmark(Baseline = true)] + public void PixelOperations_Base() + { + new PixelOperations().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); } - public class FromVector4Rgba32 : FromVector4 + [Benchmark] + public void PixelOperations_Specialized() { - [Benchmark] - public void FallbackIntrinsics128() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + } +} - SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats); - } +public class FromVector4Rgba32 : FromVector4 +{ + [Benchmark] + public void FallbackIntrinsics128() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - [Benchmark] - public void ExtendedIntrinsic() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats); + } - SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); - } + [Benchmark] + public void ExtendedIntrinsic() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - [Benchmark] - public void UseHwIntrinsics() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); + } - SimdUtils.HwIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); - } + [Benchmark] + public void UseHwIntrinsics() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - private static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 }; + SimdUtils.HwIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); + } - [Benchmark] - public void UseAvx2_Grouped() - { - Span src = MemoryMarshal.Cast(this.source.GetSpan()); - Span dest = MemoryMarshal.Cast(this.destination.GetSpan()); + private static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 }; - int n = dest.Length / Vector.Count; + [Benchmark] + public void UseAvx2_Grouped() + { + Span src = MemoryMarshal.Cast(this.source.GetSpan()); + Span dest = MemoryMarshal.Cast(this.destination.GetSpan()); - ref Vector256 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(src)); - ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + int n = dest.Length / Vector.Count; - ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); - Vector256 mask = Unsafe.As>(ref maskBase); + ref Vector256 sourceBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(src)); + ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - var maxBytes = Vector256.Create(255f); + ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); + Vector256 mask = Unsafe.As>(ref maskBase); - for (int i = 0; i < n; i++) - { - ref Vector256 s = ref Unsafe.Add(ref sourceBase, i * 4); + var maxBytes = Vector256.Create(255f); - Vector256 f0 = s; - Vector256 f1 = Unsafe.Add(ref s, 1); - Vector256 f2 = Unsafe.Add(ref s, 2); - Vector256 f3 = Unsafe.Add(ref s, 3); + for (int i = 0; i < n; i++) + { + ref Vector256 s = ref Unsafe.Add(ref sourceBase, i * 4); - f0 = Avx.Multiply(maxBytes, f0); - f1 = Avx.Multiply(maxBytes, f1); - f2 = Avx.Multiply(maxBytes, f2); - f3 = Avx.Multiply(maxBytes, f3); + Vector256 f0 = s; + Vector256 f1 = Unsafe.Add(ref s, 1); + Vector256 f2 = Unsafe.Add(ref s, 2); + Vector256 f3 = Unsafe.Add(ref s, 3); - Vector256 w0 = Avx.ConvertToVector256Int32(f0); - Vector256 w1 = Avx.ConvertToVector256Int32(f1); - Vector256 w2 = Avx.ConvertToVector256Int32(f2); - Vector256 w3 = Avx.ConvertToVector256Int32(f3); + f0 = Avx.Multiply(maxBytes, f0); + f1 = Avx.Multiply(maxBytes, f1); + f2 = Avx.Multiply(maxBytes, f2); + f3 = Avx.Multiply(maxBytes, f3); - Vector256 u0 = Avx2.PackSignedSaturate(w0, w1); - Vector256 u1 = Avx2.PackSignedSaturate(w2, w3); - Vector256 b = Avx2.PackUnsignedSaturate(u0, u1); - b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte(); + Vector256 w0 = Avx.ConvertToVector256Int32(f0); + Vector256 w1 = Avx.ConvertToVector256Int32(f1); + Vector256 w2 = Avx.ConvertToVector256Int32(f2); + Vector256 w3 = Avx.ConvertToVector256Int32(f3); - Unsafe.Add(ref destBase, i) = b; - } - } + Vector256 u0 = Avx2.PackSignedSaturate(w0, w1); + Vector256 u1 = Avx2.PackSignedSaturate(w2, w3); + Vector256 b = Avx2.PackUnsignedSaturate(u0, u1); + b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 ConvertToInt32(Vector256 vf, Vector256 scale) - { - vf = Avx.Multiply(scale, vf); - return Avx.ConvertToVector256Int32(vf); + Unsafe.Add(ref destBase, i) = b; } + } - // *** RESULTS 2020 March: *** - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.200-preview-014971 - // Job-IUZXZT : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT - // - // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |---------------------------- |------ |-----------:|------------:|----------:|------:|--------:|------:|------:|------:|----------:| - // | FallbackIntrinsics128 | 1024 | 2,952.6 ns | 1,680.77 ns | 92.13 ns | 3.32 | 0.16 | - | - | - | - | - // | BasicIntrinsics256 | 1024 | 1,664.5 ns | 928.11 ns | 50.87 ns | 1.87 | 0.09 | - | - | - | - | - // | ExtendedIntrinsic | 1024 | 890.6 ns | 375.48 ns | 20.58 ns | 1.00 | 0.00 | - | - | - | - | - // | UseAvx2 | 1024 | 299.0 ns | 30.47 ns | 1.67 ns | 0.34 | 0.01 | - | - | - | - | - // | UseAvx2_Grouped | 1024 | 318.1 ns | 48.19 ns | 2.64 ns | 0.36 | 0.01 | - | - | - | - | - // | PixelOperations_Base | 1024 | 8,136.9 ns | 1,834.82 ns | 100.57 ns | 9.14 | 0.26 | - | - | - | 24 B | - // | PixelOperations_Specialized | 1024 | 951.1 ns | 123.93 ns | 6.79 ns | 1.07 | 0.03 | - | - | - | - | + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ConvertToInt32(Vector256 vf, Vector256 scale) + { + vf = Avx.Multiply(scale, vf); + return Avx.ConvertToVector256Int32(vf); } + + // *** RESULTS 2020 March: *** + // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores + // .NET Core SDK=3.1.200-preview-014971 + // Job-IUZXZT : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT + // + // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + // |---------------------------- |------ |-----------:|------------:|----------:|------:|--------:|------:|------:|------:|----------:| + // | FallbackIntrinsics128 | 1024 | 2,952.6 ns | 1,680.77 ns | 92.13 ns | 3.32 | 0.16 | - | - | - | - | + // | BasicIntrinsics256 | 1024 | 1,664.5 ns | 928.11 ns | 50.87 ns | 1.87 | 0.09 | - | - | - | - | + // | ExtendedIntrinsic | 1024 | 890.6 ns | 375.48 ns | 20.58 ns | 1.00 | 0.00 | - | - | - | - | + // | UseAvx2 | 1024 | 299.0 ns | 30.47 ns | 1.67 ns | 0.34 | 0.01 | - | - | - | - | + // | UseAvx2_Grouped | 1024 | 318.1 ns | 48.19 ns | 2.64 ns | 0.36 | 0.01 | - | - | - | - | + // | PixelOperations_Base | 1024 | 8,136.9 ns | 1,834.82 ns | 100.57 ns | 9.14 | 0.26 | - | - | - | 24 B | + // | PixelOperations_Specialized | 1024 | 951.1 ns | 123.93 ns | 6.79 ns | 1.07 | 0.03 | - | - | - | - | } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs index abb355f326..2effbd1d59 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs @@ -4,12 +4,11 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.ShortMultiFramework))] +public class FromVector4_Rgb24 : FromVector4 { - [Config(typeof(Config.ShortMultiFramework))] - public class FromVector4_Rgb24 : FromVector4 - { - } } // 2020-11-02 diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs index cdbfe41863..df308cdd4b 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs @@ -1,87 +1,85 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Pad3Shuffle4Channel - { - private static readonly DefaultPad3Shuffle4 Control = new DefaultPad3Shuffle4(1, 0, 3, 2); - private static readonly XYZWPad3Shuffle4 ControlFast = default; - private byte[] source; - private byte[] destination; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - [GlobalSetup] - public void Setup() - { - this.source = new byte[this.Count]; - new Random(this.Count).NextBytes(this.source); - this.destination = new byte[this.Count * 4 / 3]; - } +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Pad3Shuffle4Channel +{ + private static readonly DefaultPad3Shuffle4 Control = new DefaultPad3Shuffle4(1, 0, 3, 2); + private static readonly XYZWPad3Shuffle4 ControlFast = default; + private byte[] source; + private byte[] destination; - [Params(96, 384, 768, 1536)] - public int Count { get; set; } + [GlobalSetup] + public void Setup() + { + this.source = new byte[this.Count]; + new Random(this.Count).NextBytes(this.source); + this.destination = new byte[this.Count * 4 / 3]; + } - [Benchmark] - public void Pad3Shuffle4() - { - SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); - } + [Params(96, 384, 768, 1536)] + public int Count { get; set; } - [Benchmark] - public void Pad3Shuffle4FastFallback() - { - SimdUtils.Pad3Shuffle4(this.source, this.destination, ControlFast); - } + [Benchmark] + public void Pad3Shuffle4() + { + SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); } - // 2020-10-30 - // ########## - // - // BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.403 - // [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // - // Runtime=.NET Core 3.1 - // - // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |------------------------- |------------------- |-------------------------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:|------:|------:|------:|----------:| - // | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 120.64 ns | 7.190 ns | 21.200 ns | 114.26 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 2. AVX | Empty | 96 | 23.63 ns | 0.175 ns | 0.155 ns | 23.65 ns | 0.15 | 0.01 | - | - | - | - | - // | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 25.25 ns | 0.356 ns | 0.298 ns | 25.27 ns | 0.17 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 14.80 ns | 0.358 ns | 1.032 ns | 14.64 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 96 | 24.84 ns | 0.376 ns | 0.333 ns | 24.74 ns | 1.57 | 0.06 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 96 | 24.58 ns | 0.471 ns | 0.704 ns | 24.38 ns | 1.60 | 0.09 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 258.92 ns | 4.873 ns | 4.069 ns | 257.95 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 2. AVX | Empty | 384 | 41.41 ns | 0.859 ns | 1.204 ns | 41.33 ns | 0.16 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 40.74 ns | 0.848 ns | 0.793 ns | 40.48 ns | 0.16 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 74.50 ns | 0.490 ns | 0.383 ns | 74.49 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 384 | 40.74 ns | 0.624 ns | 0.584 ns | 40.72 ns | 0.55 | 0.01 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 384 | 38.28 ns | 0.534 ns | 0.417 ns | 38.22 ns | 0.51 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 503.91 ns | 6.466 ns | 6.048 ns | 501.58 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 2. AVX | Empty | 768 | 62.86 ns | 0.332 ns | 0.277 ns | 62.80 ns | 0.12 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 64.59 ns | 0.469 ns | 0.415 ns | 64.62 ns | 0.13 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 110.51 ns | 0.592 ns | 0.554 ns | 110.33 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 768 | 64.72 ns | 1.306 ns | 1.090 ns | 64.51 ns | 0.59 | 0.01 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 768 | 62.11 ns | 0.816 ns | 0.682 ns | 61.98 ns | 0.56 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 1,005.84 ns | 13.176 ns | 12.325 ns | 1,004.70 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 2. AVX | Empty | 1536 | 110.05 ns | 0.256 ns | 0.214 ns | 110.04 ns | 0.11 | 0.00 | - | - | - | - | - // | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.545 ns | 0.483 ns | 110.09 ns | 0.11 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 1536 | 111.54 ns | 2.173 ns | 2.901 ns | 111.27 ns | 0.51 | 0.01 | - | - | - | - | - // | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - | + [Benchmark] + public void Pad3Shuffle4FastFallback() + { + SimdUtils.Pad3Shuffle4(this.source, this.destination, ControlFast); + } } + +// 2020-10-30 +// ########## +// +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=3.1.403 +// [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// +// Runtime=.NET Core 3.1 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |------------------------- |------------------- |-------------------------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:|------:|------:|------:|----------:| +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 120.64 ns | 7.190 ns | 21.200 ns | 114.26 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. AVX | Empty | 96 | 23.63 ns | 0.175 ns | 0.155 ns | 23.65 ns | 0.15 | 0.01 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 25.25 ns | 0.356 ns | 0.298 ns | 25.27 ns | 0.17 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 14.80 ns | 0.358 ns | 1.032 ns | 14.64 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 96 | 24.84 ns | 0.376 ns | 0.333 ns | 24.74 ns | 1.57 | 0.06 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 96 | 24.58 ns | 0.471 ns | 0.704 ns | 24.38 ns | 1.60 | 0.09 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 258.92 ns | 4.873 ns | 4.069 ns | 257.95 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. AVX | Empty | 384 | 41.41 ns | 0.859 ns | 1.204 ns | 41.33 ns | 0.16 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 40.74 ns | 0.848 ns | 0.793 ns | 40.48 ns | 0.16 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 74.50 ns | 0.490 ns | 0.383 ns | 74.49 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 384 | 40.74 ns | 0.624 ns | 0.584 ns | 40.72 ns | 0.55 | 0.01 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 384 | 38.28 ns | 0.534 ns | 0.417 ns | 38.22 ns | 0.51 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 503.91 ns | 6.466 ns | 6.048 ns | 501.58 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. AVX | Empty | 768 | 62.86 ns | 0.332 ns | 0.277 ns | 62.80 ns | 0.12 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 64.59 ns | 0.469 ns | 0.415 ns | 64.62 ns | 0.13 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 110.51 ns | 0.592 ns | 0.554 ns | 110.33 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 768 | 64.72 ns | 1.306 ns | 1.090 ns | 64.51 ns | 0.59 | 0.01 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 768 | 62.11 ns | 0.816 ns | 0.682 ns | 61.98 ns | 0.56 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 1,005.84 ns | 13.176 ns | 12.325 ns | 1,004.70 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. AVX | Empty | 1536 | 110.05 ns | 0.256 ns | 0.214 ns | 110.04 ns | 0.11 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.545 ns | 0.483 ns | 110.09 ns | 0.11 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 1536 | 111.54 ns | 2.173 ns | 2.901 ns | 111.27 ns | 0.51 | 0.01 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs index 7be7e651cf..d30693c6c7 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs @@ -1,68 +1,66 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - [Config(typeof(Config.ShortCore31))] - public class PremultiplyVector4 - { - private static readonly Vector4[] Vectors = CreateVectors(); +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - [Benchmark(Baseline = true)] - public void PremultiplyBaseline() - { - ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); +[Config(typeof(Config.ShortCore31))] +public class PremultiplyVector4 +{ + private static readonly Vector4[] Vectors = CreateVectors(); - for (int i = 0; i < Vectors.Length; i++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, i); - Premultiply(ref v); - } - } + [Benchmark(Baseline = true)] + public void PremultiplyBaseline() + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); - [Benchmark] - public void Premultiply() + for (int i = 0; i < Vectors.Length; i++) { - Numerics.Premultiply(Vectors); + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + Premultiply(ref v); } + } - [MethodImpl(InliningOptions.ShortMethod)] - private static void Premultiply(ref Vector4 source) - { - float w = source.W; - source *= w; - source.W = w; - } + [Benchmark] + public void Premultiply() + { + Numerics.Premultiply(Vectors); + } - private static Vector4[] CreateVectors() - { - var rnd = new Random(42); - return GenerateRandomVectorArray(rnd, 2048, 0, 1); - } + [MethodImpl(InliningOptions.ShortMethod)] + private static void Premultiply(ref Vector4 source) + { + float w = source.W; + source *= w; + source.W = w; + } - private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) - { - var values = new Vector4[length]; + private static Vector4[] CreateVectors() + { + var rnd = new Random(42); + return GenerateRandomVectorArray(rnd, 2048, 0, 1); + } - for (int i = 0; i < length; i++) - { - ref Vector4 v = ref values[i]; - v.X = GetRandomFloat(rnd, minVal, maxVal); - v.Y = GetRandomFloat(rnd, minVal, maxVal); - v.Z = GetRandomFloat(rnd, minVal, maxVal); - v.W = GetRandomFloat(rnd, minVal, maxVal); - } + private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) + { + var values = new Vector4[length]; - return values; + for (int i = 0; i < length; i++) + { + ref Vector4 v = ref values[i]; + v.X = GetRandomFloat(rnd, minVal, maxVal); + v.Y = GetRandomFloat(rnd, minVal, maxVal); + v.Z = GetRandomFloat(rnd, minVal, maxVal); + v.W = GetRandomFloat(rnd, minVal, maxVal); } - private static float GetRandomFloat(Random rnd, float minVal, float maxVal) - => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; + return values; } + + private static float GetRandomFloat(Random rnd, float minVal, float maxVal) + => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs index bc586e6eab..1795ac30bb 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs @@ -7,53 +7,52 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +public abstract class Rgb24Bytes + where TPixel : unmanaged, IPixel { - public abstract class Rgb24Bytes - where TPixel : unmanaged, IPixel + private IMemoryOwner source; + + private IMemoryOwner destination; + + private Configuration configuration; + + [Params(16, 128, 1024)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() { - private IMemoryOwner source; - - private IMemoryOwner destination; - - private Configuration configuration; - - [Params(16, 128, 1024)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.configuration = Configuration.Default; - this.source = this.configuration.MemoryAllocator.Allocate(this.Count); - this.destination = this.configuration.MemoryAllocator.Allocate(this.Count * 3); - } - - [GlobalCleanup] - public void Cleanup() - { - this.source.Dispose(); - this.destination.Dispose(); - } - - [Benchmark(Baseline = true)] - public void CommonBulk() => - new PixelOperations().ToRgb24Bytes( - this.configuration, - this.source.GetSpan(), - this.destination.GetSpan(), - this.Count); - - [Benchmark] - public void OptimizedBulk() => - PixelOperations.Instance.ToRgb24Bytes( - this.configuration, - this.source.GetSpan(), - this.destination.GetSpan(), - this.Count); + this.configuration = Configuration.Default; + this.source = this.configuration.MemoryAllocator.Allocate(this.Count); + this.destination = this.configuration.MemoryAllocator.Allocate(this.Count * 3); } - public class Rgb24Bytes_Rgba32 : Rgb24Bytes + [GlobalCleanup] + public void Cleanup() { + this.source.Dispose(); + this.destination.Dispose(); } + + [Benchmark(Baseline = true)] + public void CommonBulk() => + new PixelOperations().ToRgb24Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); + + [Benchmark] + public void OptimizedBulk() => + PixelOperations.Instance.ToRgb24Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); +} + +public class Rgb24Bytes_Rgba32 : Rgb24Bytes +{ } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs index 72da55e52e..7f6f5901ff 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs @@ -1,64 +1,62 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Shuffle3Channel { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Shuffle3Channel - { - private static readonly DefaultShuffle3 Control = new DefaultShuffle3(1, 0, 2); - private byte[] source; - private byte[] destination; + private static readonly DefaultShuffle3 Control = new DefaultShuffle3(1, 0, 2); + private byte[] source; + private byte[] destination; - [GlobalSetup] - public void Setup() - { - this.source = new byte[this.Count]; - new Random(this.Count).NextBytes(this.source); - this.destination = new byte[this.Count]; - } + [GlobalSetup] + public void Setup() + { + this.source = new byte[this.Count]; + new Random(this.Count).NextBytes(this.source); + this.destination = new byte[this.Count]; + } - [Params(96, 384, 768, 1536)] - public int Count { get; set; } + [Params(96, 384, 768, 1536)] + public int Count { get; set; } - [Benchmark] - public void Shuffle3() - { - SimdUtils.Shuffle3(this.source, this.destination, Control); - } + [Benchmark] + public void Shuffle3() + { + SimdUtils.Shuffle3(this.source, this.destination, Control); } - - // 2020-11-02 - // ########## - // - // BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.403 - // [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // - // Runtime=.NET Core 3.1 - // - // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |--------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:| - // | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 48.46 ns | 1.034 ns | 2.438 ns | 47.46 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle3 | 2. AVX | Empty | 96 | 32.42 ns | 0.537 ns | 0.476 ns | 32.34 ns | 0.66 | 0.04 | - | - | - | - | - // | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 32.51 ns | 0.373 ns | 0.349 ns | 32.56 ns | 0.66 | 0.03 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 199.04 ns | 1.512 ns | 1.180 ns | 199.17 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle3 | 2. AVX | Empty | 384 | 71.20 ns | 2.654 ns | 7.784 ns | 69.60 ns | 0.41 | 0.02 | - | - | - | - | - // | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 63.23 ns | 0.569 ns | 0.505 ns | 63.21 ns | 0.32 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 391.28 ns | 5.087 ns | 3.972 ns | 391.22 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle3 | 2. AVX | Empty | 768 | 109.12 ns | 2.149 ns | 2.010 ns | 108.66 ns | 0.28 | 0.01 | - | - | - | - | - // | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 106.51 ns | 0.734 ns | 0.613 ns | 106.56 ns | 0.27 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle3 | 2. AVX | Empty | 1536 | 190.41 ns | 1.090 ns | 0.851 ns | 190.38 ns | 0.25 | 0.00 | - | - | - | - | - // | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - | } + +// 2020-11-02 +// ########## +// +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=3.1.403 +// [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// +// Runtime=.NET Core 3.1 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:| +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 48.46 ns | 1.034 ns | 2.438 ns | 47.46 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 2. AVX | Empty | 96 | 32.42 ns | 0.537 ns | 0.476 ns | 32.34 ns | 0.66 | 0.04 | - | - | - | - | +// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 32.51 ns | 0.373 ns | 0.349 ns | 32.56 ns | 0.66 | 0.03 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 199.04 ns | 1.512 ns | 1.180 ns | 199.17 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 2. AVX | Empty | 384 | 71.20 ns | 2.654 ns | 7.784 ns | 69.60 ns | 0.41 | 0.02 | - | - | - | - | +// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 63.23 ns | 0.569 ns | 0.505 ns | 63.21 ns | 0.32 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 391.28 ns | 5.087 ns | 3.972 ns | 391.22 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 2. AVX | Empty | 768 | 109.12 ns | 2.149 ns | 2.010 ns | 108.66 ns | 0.28 | 0.01 | - | - | - | - | +// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 106.51 ns | 0.734 ns | 0.613 ns | 106.56 ns | 0.27 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 2. AVX | Empty | 1536 | 190.41 ns | 1.090 ns | 0.851 ns | 190.38 ns | 0.25 | 0.00 | - | - | - | - | +// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs index 446e7d5dc0..0a0afb7050 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs @@ -1,95 +1,93 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class Shuffle4Slice3Channel - { - private static readonly DefaultShuffle4Slice3 Control = new DefaultShuffle4Slice3(1, 0, 3, 2); - private static readonly XYZWShuffle4Slice3 ControlFast = default; - private byte[] source; - private byte[] destination; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - [GlobalSetup] - public void Setup() - { - this.source = new byte[this.Count]; - new Random(this.Count).NextBytes(this.source); - this.destination = new byte[(int)(this.Count * (3 / 4F))]; - } +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class Shuffle4Slice3Channel +{ + private static readonly DefaultShuffle4Slice3 Control = new DefaultShuffle4Slice3(1, 0, 3, 2); + private static readonly XYZWShuffle4Slice3 ControlFast = default; + private byte[] source; + private byte[] destination; - [Params(128, 256, 512, 1024, 2048)] - public int Count { get; set; } + [GlobalSetup] + public void Setup() + { + this.source = new byte[this.Count]; + new Random(this.Count).NextBytes(this.source); + this.destination = new byte[(int)(this.Count * (3 / 4F))]; + } - [Benchmark] - public void Shuffle4Slice3() - { - SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); - } + [Params(128, 256, 512, 1024, 2048)] + public int Count { get; set; } - [Benchmark] - public void Shuffle4Slice3FastFallback() - { - SimdUtils.Shuffle4Slice3(this.source, this.destination, ControlFast); - } + [Benchmark] + public void Shuffle4Slice3() + { + SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); } - // 2020-10-29 - // ########## - // - // BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.403 - // [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // - // Runtime=.NET Core 3.1 - // - // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:| - // | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 56.44 ns | 2.843 ns | 8.382 ns | 56.70 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 2. AVX | Empty | 128 | 27.15 ns | 0.556 ns | 0.762 ns | 27.34 ns | 0.41 | 0.03 | - | - | - | - | - // | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 128 | 26.36 ns | 0.321 ns | 0.268 ns | 26.26 ns | 0.38 | 0.02 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 25.85 ns | 0.494 ns | 0.462 ns | 25.84 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 128 | 26.15 ns | 0.113 ns | 0.106 ns | 26.16 ns | 1.01 | 0.02 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 128 | 25.57 ns | 0.078 ns | 0.061 ns | 25.56 ns | 0.99 | 0.02 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 97.47 ns | 0.327 ns | 0.289 ns | 97.35 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 2. AVX | Empty | 256 | 32.61 ns | 0.107 ns | 0.095 ns | 32.62 ns | 0.33 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.21 ns | 0.169 ns | 0.150 ns | 33.15 ns | 0.34 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 52.34 ns | 0.779 ns | 0.729 ns | 51.94 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 256 | 32.16 ns | 0.111 ns | 0.104 ns | 32.16 ns | 0.61 | 0.01 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.61 ns | 0.342 ns | 0.319 ns | 33.62 ns | 0.64 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 210.74 ns | 3.825 ns | 5.956 ns | 207.70 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 2. AVX | Empty | 512 | 51.03 ns | 0.535 ns | 0.501 ns | 51.18 ns | 0.24 | 0.01 | - | - | - | - | - // | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 512 | 66.60 ns | 1.313 ns | 1.613 ns | 65.93 ns | 0.31 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 119.12 ns | 1.905 ns | 1.689 ns | 118.52 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 512 | 50.33 ns | 0.382 ns | 0.339 ns | 50.41 ns | 0.42 | 0.01 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 512 | 49.25 ns | 0.555 ns | 0.492 ns | 49.26 ns | 0.41 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 423.55 ns | 4.891 ns | 4.336 ns | 423.27 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 2. AVX | Empty | 1024 | 77.13 ns | 1.355 ns | 2.264 ns | 76.19 ns | 0.19 | 0.01 | - | - | - | - | - // | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 79.39 ns | 0.103 ns | 0.086 ns | 79.37 ns | 0.19 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 226.57 ns | 2.930 ns | 2.598 ns | 226.10 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 1024 | 80.25 ns | 1.647 ns | 2.082 ns | 80.98 ns | 0.35 | 0.01 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 84.99 ns | 1.234 ns | 1.155 ns | 85.60 ns | 0.38 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 794.96 ns | 1.735 ns | 1.538 ns | 795.15 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 2. AVX | Empty | 2048 | 128.41 ns | 0.417 ns | 0.390 ns | 128.24 ns | 0.16 | 0.00 | - | - | - | - | - // | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 127.24 ns | 0.294 ns | 0.229 ns | 127.23 ns | 0.16 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | | - // | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 2048 | 126.93 ns | 0.382 ns | 0.339 ns | 126.94 ns | 0.33 | 0.00 | - | - | - | - | - // | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - | + [Benchmark] + public void Shuffle4Slice3FastFallback() + { + SimdUtils.Shuffle4Slice3(this.source, this.destination, ControlFast); + } } + +// 2020-10-29 +// ########## +// +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=3.1.403 +// [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// +// Runtime=.NET Core 3.1 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:| +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 56.44 ns | 2.843 ns | 8.382 ns | 56.70 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. AVX | Empty | 128 | 27.15 ns | 0.556 ns | 0.762 ns | 27.34 ns | 0.41 | 0.03 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 128 | 26.36 ns | 0.321 ns | 0.268 ns | 26.26 ns | 0.38 | 0.02 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 25.85 ns | 0.494 ns | 0.462 ns | 25.84 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 128 | 26.15 ns | 0.113 ns | 0.106 ns | 26.16 ns | 1.01 | 0.02 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 128 | 25.57 ns | 0.078 ns | 0.061 ns | 25.56 ns | 0.99 | 0.02 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 97.47 ns | 0.327 ns | 0.289 ns | 97.35 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. AVX | Empty | 256 | 32.61 ns | 0.107 ns | 0.095 ns | 32.62 ns | 0.33 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.21 ns | 0.169 ns | 0.150 ns | 33.15 ns | 0.34 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 52.34 ns | 0.779 ns | 0.729 ns | 51.94 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 256 | 32.16 ns | 0.111 ns | 0.104 ns | 32.16 ns | 0.61 | 0.01 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.61 ns | 0.342 ns | 0.319 ns | 33.62 ns | 0.64 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 210.74 ns | 3.825 ns | 5.956 ns | 207.70 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. AVX | Empty | 512 | 51.03 ns | 0.535 ns | 0.501 ns | 51.18 ns | 0.24 | 0.01 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 512 | 66.60 ns | 1.313 ns | 1.613 ns | 65.93 ns | 0.31 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 119.12 ns | 1.905 ns | 1.689 ns | 118.52 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 512 | 50.33 ns | 0.382 ns | 0.339 ns | 50.41 ns | 0.42 | 0.01 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 512 | 49.25 ns | 0.555 ns | 0.492 ns | 49.26 ns | 0.41 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 423.55 ns | 4.891 ns | 4.336 ns | 423.27 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. AVX | Empty | 1024 | 77.13 ns | 1.355 ns | 2.264 ns | 76.19 ns | 0.19 | 0.01 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 79.39 ns | 0.103 ns | 0.086 ns | 79.37 ns | 0.19 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 226.57 ns | 2.930 ns | 2.598 ns | 226.10 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 1024 | 80.25 ns | 1.647 ns | 2.082 ns | 80.98 ns | 0.35 | 0.01 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 84.99 ns | 1.234 ns | 1.155 ns | 85.60 ns | 0.38 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 794.96 ns | 1.735 ns | 1.538 ns | 795.15 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. AVX | Empty | 2048 | 128.41 ns | 0.417 ns | 0.390 ns | 128.24 ns | 0.16 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 127.24 ns | 0.294 ns | 0.229 ns | 127.23 ns | 0.16 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 2048 | 126.93 ns | 0.382 ns | 0.339 ns | 126.94 ns | 0.33 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs index 427d3f318b..6323e1c55c 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs @@ -1,67 +1,65 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class ShuffleByte4Channel { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class ShuffleByte4Channel - { - private byte[] source; - private byte[] destination; + private byte[] source; + private byte[] destination; - [GlobalSetup] - public void Setup() - { - this.source = new byte[this.Count]; - new Random(this.Count).NextBytes(this.source); - this.destination = new byte[this.Count]; - } + [GlobalSetup] + public void Setup() + { + this.source = new byte[this.Count]; + new Random(this.Count).NextBytes(this.source); + this.destination = new byte[this.Count]; + } - [Params(128, 256, 512, 1024, 2048)] - public int Count { get; set; } + [Params(128, 256, 512, 1024, 2048)] + public int Count { get; set; } - [Benchmark] - public void Shuffle4Channel() - { - SimdUtils.Shuffle4(this.source, this.destination, default); - } + [Benchmark] + public void Shuffle4Channel() + { + SimdUtils.Shuffle4(this.source, this.destination, default); } - - // 2020-10-29 - // ########## - // - // BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.403 - // [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // - // Runtime=.NET Core 3.1 - // - // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:| - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 17.39 ns | 0.187 ns | 0.175 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 128 | 21.72 ns | 0.299 ns | 0.279 ns | 1.25 | 0.02 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 18.10 ns | 0.346 ns | 0.289 ns | 1.04 | 0.02 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 35.51 ns | 0.711 ns | 0.790 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 256 | 23.90 ns | 0.508 ns | 0.820 ns | 0.69 | 0.02 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 20.40 ns | 0.133 ns | 0.111 ns | 0.57 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 73.39 ns | 0.310 ns | 0.259 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 512 | 26.10 ns | 0.418 ns | 0.391 ns | 0.36 | 0.01 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 27.59 ns | 0.556 ns | 0.571 ns | 0.38 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 150.64 ns | 2.903 ns | 2.716 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 1024 | 38.67 ns | 0.801 ns | 1.889 ns | 0.24 | 0.02 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 47.13 ns | 0.948 ns | 1.054 ns | 0.31 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 2048 | 57.37 ns | 1.152 ns | 1.078 ns | 0.18 | 0.01 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - | } + +// 2020-10-29 +// ########## +// +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=3.1.403 +// [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// +// Runtime=.NET Core 3.1 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:| +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 17.39 ns | 0.187 ns | 0.175 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 128 | 21.72 ns | 0.299 ns | 0.279 ns | 1.25 | 0.02 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 18.10 ns | 0.346 ns | 0.289 ns | 1.04 | 0.02 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 35.51 ns | 0.711 ns | 0.790 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 256 | 23.90 ns | 0.508 ns | 0.820 ns | 0.69 | 0.02 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 20.40 ns | 0.133 ns | 0.111 ns | 0.57 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 73.39 ns | 0.310 ns | 0.259 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 512 | 26.10 ns | 0.418 ns | 0.391 ns | 0.36 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 27.59 ns | 0.556 ns | 0.571 ns | 0.38 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 150.64 ns | 2.903 ns | 2.716 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 1024 | 38.67 ns | 0.801 ns | 1.889 ns | 0.24 | 0.02 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 47.13 ns | 0.948 ns | 1.054 ns | 0.31 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 2048 | 57.37 ns | 1.152 ns | 1.078 ns | 0.18 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs index 5c5208c4b1..bdeb880828 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs @@ -1,68 +1,66 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] +public class ShuffleFloat4Channel { - [Config(typeof(Config.HwIntrinsics_SSE_AVX))] - public class ShuffleFloat4Channel - { - private static readonly byte Control = default(WXYZShuffle4).Control; - private float[] source; - private float[] destination; + private static readonly byte Control = default(WXYZShuffle4).Control; + private float[] source; + private float[] destination; - [GlobalSetup] - public void Setup() - { - this.source = new Random(this.Count).GenerateRandomFloatArray(this.Count, 0, 256); - this.destination = new float[this.Count]; - } + [GlobalSetup] + public void Setup() + { + this.source = new Random(this.Count).GenerateRandomFloatArray(this.Count, 0, 256); + this.destination = new float[this.Count]; + } - [Params(128, 256, 512, 1024, 2048)] - public int Count { get; set; } + [Params(128, 256, 512, 1024, 2048)] + public int Count { get; set; } - [Benchmark] - public void Shuffle4Channel() - { - SimdUtils.Shuffle4(this.source, this.destination, Control); - } + [Benchmark] + public void Shuffle4Channel() + { + SimdUtils.Shuffle4(this.source, this.destination, Control); } - - // 2020-10-29 - // ########## - // - // BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.403 - // [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - // - // Runtime=.NET Core 3.1 - // - // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:| - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 63.647 ns | 0.5475 ns | 0.4853 ns | 1.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 128 | 9.818 ns | 0.1457 ns | 0.1292 ns | 0.15 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 15.267 ns | 0.1005 ns | 0.0940 ns | 0.24 | - | - | - | - | - // | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 125.586 ns | 1.9312 ns | 1.8064 ns | 1.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 256 | 15.878 ns | 0.1983 ns | 0.1758 ns | 0.13 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 29.170 ns | 0.2925 ns | 0.2442 ns | 0.23 | - | - | - | - | - // | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 263.859 ns | 2.6660 ns | 2.3634 ns | 1.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 512 | 29.452 ns | 0.3334 ns | 0.3118 ns | 0.11 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 52.912 ns | 0.1932 ns | 0.1713 ns | 0.20 | - | - | - | - | - // | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 495.717 ns | 1.9850 ns | 1.8567 ns | 1.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 1024 | 53.757 ns | 0.3212 ns | 0.2847 ns | 0.11 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 107.815 ns | 1.6201 ns | 1.3528 ns | 0.22 | - | - | - | - | - // | | | | | | | | | | | | | - // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - | - // | Shuffle4Channel | 2. AVX | Empty | 2048 | 105.120 ns | 0.6140 ns | 0.5443 ns | 0.11 | - | - | - | - | - // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - | } + +// 2020-10-29 +// ########## +// +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=3.1.403 +// [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 1. No HwIntrinsics : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 2. AVX : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// 3. SSE : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT +// +// Runtime=.NET Core 3.1 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:| +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 63.647 ns | 0.5475 ns | 0.4853 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 128 | 9.818 ns | 0.1457 ns | 0.1292 ns | 0.15 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 15.267 ns | 0.1005 ns | 0.0940 ns | 0.24 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 125.586 ns | 1.9312 ns | 1.8064 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 256 | 15.878 ns | 0.1983 ns | 0.1758 ns | 0.13 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 29.170 ns | 0.2925 ns | 0.2442 ns | 0.23 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 263.859 ns | 2.6660 ns | 2.3634 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 512 | 29.452 ns | 0.3334 ns | 0.3118 ns | 0.11 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 52.912 ns | 0.1932 ns | 0.1713 ns | 0.20 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 495.717 ns | 1.9850 ns | 1.8567 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 1024 | 53.757 ns | 0.3212 ns | 0.2847 ns | 0.11 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 107.815 ns | 1.6201 ns | 1.3528 ns | 0.22 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. AVX | Empty | 2048 | 105.120 ns | 0.6140 ns | 0.5443 ns | 0.11 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs index 9f17a22953..b6e0696735 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; using BenchmarkDotNet.Attributes; @@ -9,80 +8,79 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - public abstract class ToRgba32Bytes - where TPixel : unmanaged, IPixel - { - private IMemoryOwner source; - - private IMemoryOwner destination; - - private Configuration configuration; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - [Params(16, 128, 1024)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.configuration = Configuration.Default; - this.source = this.configuration.MemoryAllocator.Allocate(this.Count); - this.destination = this.configuration.MemoryAllocator.Allocate(this.Count * 4); - } +public abstract class ToRgba32Bytes + where TPixel : unmanaged, IPixel +{ + private IMemoryOwner source; - [GlobalCleanup] - public void Cleanup() - { - this.source.Dispose(); - this.destination.Dispose(); - } + private IMemoryOwner destination; - // [Benchmark] - public void Naive() - { - Span s = this.source.GetSpan(); - Span d = this.destination.GetSpan(); - - for (int i = 0; i < this.Count; i++) - { - TPixel c = s[i]; - int i4 = i * 4; - Rgba32 rgba = default; - c.ToRgba32(ref rgba); - d[i4] = rgba.R; - d[i4 + 1] = rgba.G; - d[i4 + 2] = rgba.B; - d[i4 + 3] = rgba.A; - } - } + private Configuration configuration; - [Benchmark(Baseline = true)] - public void CommonBulk() => - new PixelOperations().ToRgba32Bytes( - this.configuration, - this.source.GetSpan(), - this.destination.GetSpan(), - this.Count); - - [Benchmark] - public void OptimizedBulk() => - PixelOperations.Instance.ToRgba32Bytes( - this.configuration, - this.source.GetSpan(), - this.destination.GetSpan(), - this.Count); - } + [Params(16, 128, 1024)] + public int Count { get; set; } - public class ToRgba32Bytes_FromRgba32 : ToRgba32Bytes + [GlobalSetup] + public void Setup() { + this.configuration = Configuration.Default; + this.source = this.configuration.MemoryAllocator.Allocate(this.Count); + this.destination = this.configuration.MemoryAllocator.Allocate(this.Count * 4); } - public class ToRgba32Bytes_FromArgb32 : ToRgba32Bytes + [GlobalCleanup] + public void Cleanup() { + this.source.Dispose(); + this.destination.Dispose(); } - public class ToRgba32Bytes_FromBgra32 : ToRgba32Bytes + // [Benchmark] + public void Naive() { + Span s = this.source.GetSpan(); + Span d = this.destination.GetSpan(); + + for (int i = 0; i < this.Count; i++) + { + TPixel c = s[i]; + int i4 = i * 4; + Rgba32 rgba = default; + c.ToRgba32(ref rgba); + d[i4] = rgba.R; + d[i4 + 1] = rgba.G; + d[i4 + 2] = rgba.B; + d[i4 + 3] = rgba.A; + } } + + [Benchmark(Baseline = true)] + public void CommonBulk() => + new PixelOperations().ToRgba32Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); + + [Benchmark] + public void OptimizedBulk() => + PixelOperations.Instance.ToRgba32Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); +} + +public class ToRgba32Bytes_FromRgba32 : ToRgba32Bytes +{ +} + +public class ToRgba32Bytes_FromArgb32 : ToRgba32Bytes +{ +} + +public class ToRgba32Bytes_FromBgra32 : ToRgba32Bytes +{ } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index b73076ffa6..31dfd0be93 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; using System.Numerics; using BenchmarkDotNet.Attributes; @@ -10,53 +9,52 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - public abstract class ToVector4 - where TPixel : unmanaged, IPixel - { - protected IMemoryOwner source; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - protected IMemoryOwner destination; +public abstract class ToVector4 + where TPixel : unmanaged, IPixel +{ + protected IMemoryOwner source; - protected Configuration Configuration => Configuration.Default; + protected IMemoryOwner destination; - [Params(64, 256, 2048)] // 512, 1024 - public int Count { get; set; } + protected Configuration Configuration => Configuration.Default; - [GlobalSetup] - public void Setup() - { - this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); - this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); - } + [Params(64, 256, 2048)] // 512, 1024 + public int Count { get; set; } - [GlobalCleanup] - public void Cleanup() - { - this.source.Dispose(); - this.destination.Dispose(); - } + [GlobalSetup] + public void Setup() + { + this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); + } - // [Benchmark] - public void Naive() - { - Span s = this.source.GetSpan(); - Span d = this.destination.GetSpan(); + [GlobalCleanup] + public void Cleanup() + { + this.source.Dispose(); + this.destination.Dispose(); + } - for (int i = 0; i < this.Count; i++) - { - d[i] = s[i].ToVector4(); - } - } + // [Benchmark] + public void Naive() + { + Span s = this.source.GetSpan(); + Span d = this.destination.GetSpan(); - [Benchmark] - public void PixelOperations_Specialized() + for (int i = 0; i < this.Count; i++) { - PixelOperations.Instance.ToVector4( - this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); + d[i] = s[i].ToVector4(); } } + + [Benchmark] + public void PixelOperations_Specialized() + { + PixelOperations.Instance.ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); + } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs index e3193ec1be..b2f12cf9d9 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs @@ -6,39 +6,38 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.ShortMultiFramework))] +public class ToVector4_Bgra32 : ToVector4 { - [Config(typeof(Config.ShortMultiFramework))] - public class ToVector4_Bgra32 : ToVector4 + [Benchmark(Baseline = true)] + public void PixelOperations_Base() { - [Benchmark(Baseline = true)] - public void PixelOperations_Base() - { - new PixelOperations().ToVector4( - this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); - } - - // RESULTS: - // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | - // ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:| - // PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B | - // PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B | - // PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B | - // PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B | - // PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B | - // PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B | - // PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B | + new PixelOperations().ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); } + + // RESULTS: + // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | + // ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:| + // PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B | + // PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B | + // PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B | + // PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B | + // PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B | + // PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B | + // PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B | } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs index 69e4b7779f..f22868335d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs @@ -6,19 +6,18 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.ShortMultiFramework))] +public class ToVector4_Rgb24 : ToVector4 { - [Config(typeof(Config.ShortMultiFramework))] - public class ToVector4_Rgb24 : ToVector4 + [Benchmark(Baseline = true)] + public void PixelOperations_Base() { - [Benchmark(Baseline = true)] - public void PixelOperations_Base() - { - new PixelOperations().ToVector4( - this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); - } + new PixelOperations().ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs index ec51e104e9..6c28605161 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -10,156 +9,155 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; + +[Config(typeof(Config.ShortCore31))] +public class ToVector4_Rgba32 : ToVector4 { - [Config(typeof(Config.ShortCore31))] - public class ToVector4_Rgba32 : ToVector4 + [Benchmark] + public void FallbackIntrinsics128() { - [Benchmark] - public void FallbackIntrinsics128() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(sBytes, dFloats); - } + SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(sBytes, dFloats); + } - [Benchmark] - public void PixelOperations_Base() - => new PixelOperations().ToVector4( - this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); + [Benchmark] + public void PixelOperations_Base() + => new PixelOperations().ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); - [Benchmark] - public void ExtendedIntrinsics() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + [Benchmark] + public void ExtendedIntrinsics() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); - } + SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); + } - [Benchmark] - public void HwIntrinsics() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + [Benchmark] + public void HwIntrinsics() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); - } + SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); + } - // [Benchmark] - public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + // [Benchmark] + public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - int n = dFloats.Length / Vector.Count; + int n = dFloats.Length / Vector.Count; - ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); - ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); - ref Vector destBaseU = ref Unsafe.As, Vector>(ref destBase); + ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); + ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); + ref Vector destBaseU = ref Unsafe.As, Vector>(ref destBase); - for (int i = 0; i < n; i++) - { - Vector b = Unsafe.Add(ref sourceBase, i); + for (int i = 0; i < n; i++) + { + Vector b = Unsafe.Add(ref sourceBase, i); - Vector.Widen(b, out Vector s0, out Vector s1); - Vector.Widen(s0, out Vector w0, out Vector w1); - Vector.Widen(s1, out Vector w2, out Vector w3); + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); - ref Vector d = ref Unsafe.Add(ref destBaseU, i * 4); - d = w0; - Unsafe.Add(ref d, 1) = w1; - Unsafe.Add(ref d, 2) = w2; - Unsafe.Add(ref d, 3) = w3; - } + ref Vector d = ref Unsafe.Add(ref destBaseU, i * 4); + d = w0; + Unsafe.Add(ref d, 1) = w1; + Unsafe.Add(ref d, 2) = w2; + Unsafe.Add(ref d, 3) = w3; + } - n = dFloats.Length / Vector.Count; - var scale = new Vector(1f / 255f); + n = dFloats.Length / Vector.Count; + var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) - { - ref Vector dRef = ref Unsafe.Add(ref destBase, i); + for (int i = 0; i < n; i++) + { + ref Vector dRef = ref Unsafe.Add(ref destBase, i); - var du = Vector.AsVectorInt32(dRef); - var v = Vector.ConvertToSingle(du); - v *= scale; + var du = Vector.AsVectorInt32(dRef); + var v = Vector.ConvertToSingle(du); + v *= scale; - dRef = v; - } + dRef = v; } + } - // [Benchmark] - public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_ConvertInSameLoop() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - - int n = dFloats.Length / Vector.Count; - - ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); - ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); - var scale = new Vector(1f / 255f); - - for (int i = 0; i < n; i++) - { - Vector b = Unsafe.Add(ref sourceBase, i); - - Vector.Widen(b, out Vector s0, out Vector s1); - Vector.Widen(s0, out Vector w0, out Vector w1); - Vector.Widen(s1, out Vector w2, out Vector w3); - - Vector f0 = ConvertToNormalizedSingle(w0, scale); - Vector f1 = ConvertToNormalizedSingle(w1, scale); - Vector f2 = ConvertToNormalizedSingle(w2, scale); - Vector f3 = ConvertToNormalizedSingle(w3, scale); - - ref Vector d = ref Unsafe.Add(ref destBase, i * 4); - d = f0; - Unsafe.Add(ref d, 1) = f1; - Unsafe.Add(ref d, 2) = f2; - Unsafe.Add(ref d, 3) = f3; - } - } + // [Benchmark] + public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_ConvertInSameLoop() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + int n = dFloats.Length / Vector.Count; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector ConvertToNormalizedSingle(Vector u, Vector scale) + ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); + ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); + var scale = new Vector(1f / 255f); + + for (int i = 0; i < n; i++) { - var vi = Vector.AsVectorInt32(u); - var v = Vector.ConvertToSingle(vi); - v *= scale; - return v; + Vector b = Unsafe.Add(ref sourceBase, i); + + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); + + Vector f0 = ConvertToNormalizedSingle(w0, scale); + Vector f1 = ConvertToNormalizedSingle(w1, scale); + Vector f2 = ConvertToNormalizedSingle(w2, scale); + Vector f3 = ConvertToNormalizedSingle(w3, scale); + + ref Vector d = ref Unsafe.Add(ref destBase, i * 4); + d = f0; + Unsafe.Add(ref d, 1) = f1; + Unsafe.Add(ref d, 2) = f2; + Unsafe.Add(ref d, 3) = f3; } + } - /*RESULTS (2018 October): - - Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | - ---------------------------- |-------- |------ |------------:|-------------:|------------:|-------:|---------:|-------:|----------:| - FallbackIntrinsics128 | Clr | 64 | 287.62 ns | 6.026 ns | 0.3405 ns | 1.19 | 0.00 | - | 0 B | - BasicIntrinsics256 | Clr | 64 | 240.83 ns | 10.585 ns | 0.5981 ns | 1.00 | 0.00 | - | 0 B | - ExtendedIntrinsics | Clr | 64 | 168.28 ns | 11.478 ns | 0.6485 ns | 0.70 | 0.00 | - | 0 B | - PixelOperations_Base | Clr | 64 | 334.08 ns | 38.048 ns | 2.1498 ns | 1.39 | 0.01 | 0.0072 | 24 B | - PixelOperations_Specialized | Clr | 64 | 255.41 ns | 10.939 ns | 0.6181 ns | 1.06 | 0.00 | - | 0 B | <--- ceremonial overhead has been minimized! - | | | | | | | | | | - FallbackIntrinsics128 | Core | 64 | 183.29 ns | 8.931 ns | 0.5046 ns | 1.32 | 0.00 | - | 0 B | - BasicIntrinsics256 | Core | 64 | 139.18 ns | 7.633 ns | 0.4313 ns | 1.00 | 0.00 | - | 0 B | - ExtendedIntrinsics | Core | 64 | 66.29 ns | 16.366 ns | 0.9247 ns | 0.48 | 0.01 | - | 0 B | - PixelOperations_Base | Core | 64 | 257.75 ns | 16.959 ns | 0.9582 ns | 1.85 | 0.01 | 0.0072 | 24 B | - PixelOperations_Specialized | Core | 64 | 90.14 ns | 9.955 ns | 0.5625 ns | 0.65 | 0.00 | - | 0 B | - | | | | | | | | | | - FallbackIntrinsics128 | Clr | 2048 | 5,011.84 ns | 347.991 ns | 19.6621 ns | 1.22 | 0.01 | - | 0 B | - BasicIntrinsics256 | Clr | 2048 | 4,119.35 ns | 720.153 ns | 40.6900 ns | 1.00 | 0.00 | - | 0 B | - ExtendedIntrinsics | Clr | 2048 | 1,195.29 ns | 164.389 ns | 9.2883 ns |!! 0.29 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! - PixelOperations_Base | Clr | 2048 | 6,820.58 ns | 823.433 ns | 46.5255 ns | 1.66 | 0.02 | - | 24 B | - PixelOperations_Specialized | Clr | 2048 | 4,203.53 ns | 176.714 ns | 9.9847 ns | 1.02 | 0.01 | - | 0 B | <--- can't yet detect whether ExtendedIntrinsics are available :( - | | | | | | | | | | - FallbackIntrinsics128 | Core | 2048 | 5,017.89 ns | 4,021.533 ns | 227.2241 ns | 1.24 | 0.05 | - | 0 B | - BasicIntrinsics256 | Core | 2048 | 4,046.51 ns | 1,150.390 ns | 64.9992 ns | 1.00 | 0.00 | - | 0 B | - ExtendedIntrinsics | Core | 2048 | 1,130.59 ns | 832.588 ns | 47.0427 ns |!! 0.28 | 0.01 | - | 0 B | <--- ExtendedIntrinsics rock! - PixelOperations_Base | Core | 2048 | 6,752.68 ns | 272.820 ns | 15.4148 ns | 1.67 | 0.02 | - | 24 B | - PixelOperations_Specialized | Core | 2048 | 1,126.13 ns | 79.192 ns | 4.4745 ns |!! 0.28 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! - */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector ConvertToNormalizedSingle(Vector u, Vector scale) + { + var vi = Vector.AsVectorInt32(u); + var v = Vector.ConvertToSingle(vi); + v *= scale; + return v; } + + /*RESULTS (2018 October): + + Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | + ---------------------------- |-------- |------ |------------:|-------------:|------------:|-------:|---------:|-------:|----------:| + FallbackIntrinsics128 | Clr | 64 | 287.62 ns | 6.026 ns | 0.3405 ns | 1.19 | 0.00 | - | 0 B | + BasicIntrinsics256 | Clr | 64 | 240.83 ns | 10.585 ns | 0.5981 ns | 1.00 | 0.00 | - | 0 B | + ExtendedIntrinsics | Clr | 64 | 168.28 ns | 11.478 ns | 0.6485 ns | 0.70 | 0.00 | - | 0 B | + PixelOperations_Base | Clr | 64 | 334.08 ns | 38.048 ns | 2.1498 ns | 1.39 | 0.01 | 0.0072 | 24 B | + PixelOperations_Specialized | Clr | 64 | 255.41 ns | 10.939 ns | 0.6181 ns | 1.06 | 0.00 | - | 0 B | <--- ceremonial overhead has been minimized! + | | | | | | | | | | + FallbackIntrinsics128 | Core | 64 | 183.29 ns | 8.931 ns | 0.5046 ns | 1.32 | 0.00 | - | 0 B | + BasicIntrinsics256 | Core | 64 | 139.18 ns | 7.633 ns | 0.4313 ns | 1.00 | 0.00 | - | 0 B | + ExtendedIntrinsics | Core | 64 | 66.29 ns | 16.366 ns | 0.9247 ns | 0.48 | 0.01 | - | 0 B | + PixelOperations_Base | Core | 64 | 257.75 ns | 16.959 ns | 0.9582 ns | 1.85 | 0.01 | 0.0072 | 24 B | + PixelOperations_Specialized | Core | 64 | 90.14 ns | 9.955 ns | 0.5625 ns | 0.65 | 0.00 | - | 0 B | + | | | | | | | | | | + FallbackIntrinsics128 | Clr | 2048 | 5,011.84 ns | 347.991 ns | 19.6621 ns | 1.22 | 0.01 | - | 0 B | + BasicIntrinsics256 | Clr | 2048 | 4,119.35 ns | 720.153 ns | 40.6900 ns | 1.00 | 0.00 | - | 0 B | + ExtendedIntrinsics | Clr | 2048 | 1,195.29 ns | 164.389 ns | 9.2883 ns |!! 0.29 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! + PixelOperations_Base | Clr | 2048 | 6,820.58 ns | 823.433 ns | 46.5255 ns | 1.66 | 0.02 | - | 24 B | + PixelOperations_Specialized | Clr | 2048 | 4,203.53 ns | 176.714 ns | 9.9847 ns | 1.02 | 0.01 | - | 0 B | <--- can't yet detect whether ExtendedIntrinsics are available :( + | | | | | | | | | | + FallbackIntrinsics128 | Core | 2048 | 5,017.89 ns | 4,021.533 ns | 227.2241 ns | 1.24 | 0.05 | - | 0 B | + BasicIntrinsics256 | Core | 2048 | 4,046.51 ns | 1,150.390 ns | 64.9992 ns | 1.00 | 0.00 | - | 0 B | + ExtendedIntrinsics | Core | 2048 | 1,130.59 ns | 832.588 ns | 47.0427 ns |!! 0.28 | 0.01 | - | 0 B | <--- ExtendedIntrinsics rock! + PixelOperations_Base | Core | 2048 | 6,752.68 ns | 272.820 ns | 15.4148 ns | 1.67 | 0.02 | - | 24 B | + PixelOperations_Specialized | Core | 2048 | 1,126.13 ns | 79.192 ns | 4.4745 ns |!! 0.28 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! + */ } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs index 0dcead7195..ea36a341fc 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs @@ -1,68 +1,66 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - [Config(typeof(Config.ShortCore31))] - public class UnPremultiplyVector4 - { - private static readonly Vector4[] Vectors = CreateVectors(); +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - [Benchmark(Baseline = true)] - public void UnPremultiplyBaseline() - { - ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); +[Config(typeof(Config.ShortCore31))] +public class UnPremultiplyVector4 +{ + private static readonly Vector4[] Vectors = CreateVectors(); - for (int i = 0; i < Vectors.Length; i++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, i); - UnPremultiply(ref v); - } - } + [Benchmark(Baseline = true)] + public void UnPremultiplyBaseline() + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); - [Benchmark] - public void UnPremultiply() + for (int i = 0; i < Vectors.Length; i++) { - Numerics.UnPremultiply(Vectors); + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + UnPremultiply(ref v); } + } - [MethodImpl(InliningOptions.ShortMethod)] - private static void UnPremultiply(ref Vector4 source) - { - float w = source.W; - source /= w; - source.W = w; - } + [Benchmark] + public void UnPremultiply() + { + Numerics.UnPremultiply(Vectors); + } - private static Vector4[] CreateVectors() - { - var rnd = new Random(42); - return GenerateRandomVectorArray(rnd, 2048, 0, 1); - } + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnPremultiply(ref Vector4 source) + { + float w = source.W; + source /= w; + source.W = w; + } - private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) - { - var values = new Vector4[length]; + private static Vector4[] CreateVectors() + { + var rnd = new Random(42); + return GenerateRandomVectorArray(rnd, 2048, 0, 1); + } - for (int i = 0; i < length; i++) - { - ref Vector4 v = ref values[i]; - v.X = GetRandomFloat(rnd, minVal, maxVal); - v.Y = GetRandomFloat(rnd, minVal, maxVal); - v.Z = GetRandomFloat(rnd, minVal, maxVal); - v.W = GetRandomFloat(rnd, minVal, maxVal); - } + private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) + { + var values = new Vector4[length]; - return values; + for (int i = 0; i < length; i++) + { + ref Vector4 v = ref values[i]; + v.X = GetRandomFloat(rnd, minVal, maxVal); + v.Y = GetRandomFloat(rnd, minVal, maxVal); + v.Z = GetRandomFloat(rnd, minVal, maxVal); + v.W = GetRandomFloat(rnd, minVal, maxVal); } - private static float GetRandomFloat(Random rnd, float minVal, float maxVal) - => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; + return values; } + + private static float GetRandomFloat(Random rnd, float minVal, float maxVal) + => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 54d6247ae3..1ff8013b28 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -5,22 +5,21 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks -{ - using SystemColor = System.Drawing.Color; +namespace SixLabors.ImageSharp.Benchmarks; + +using SystemColor = System.Drawing.Color; - public class ColorEquality +public class ColorEquality +{ + [Benchmark(Baseline = true, Description = "System.Drawing Color Equals")] + public bool SystemDrawingColorEqual() { - [Benchmark(Baseline = true, Description = "System.Drawing Color Equals")] - public bool SystemDrawingColorEqual() - { - return SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); - } + return SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); + } - [Benchmark(Description = "ImageSharp Color Equals")] - public bool ColorEqual() - { - return new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); - } + [Benchmark(Description = "ImageSharp Color Equals")] + public bool ColorEqual() + { + return new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); } } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs index d16315a466..da09a85232 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -9,28 +9,27 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion; using Illuminants = Colourful.Illuminants; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; + +public class ColorspaceCieXyzToCieLabConvert { - public class ColorspaceCieXyzToCieLabConvert - { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); - private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.D50).ToLab(Illuminants.D50).Build(); + private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.D50).ToLab(Illuminants.D50).Build(); - [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + [Benchmark(Baseline = true, Description = "Colourful Convert")] + public double ColourfulConvert() + { + return ColourfulConverter.Convert(XYZColor).L; + } - [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToCieLab(CieXyz).L; - } + [Benchmark(Description = "ImageSharp Convert")] + public float ColorSpaceConvert() + { + return ColorSpaceConverter.ToCieLab(CieXyz).L; } } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs index 30a7dd9639..c3317c5d94 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -9,28 +9,27 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion; using Illuminants = Colourful.Illuminants; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; + +public class ColorspaceCieXyzToHunterLabConvert { - public class ColorspaceCieXyzToHunterLabConvert - { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); - private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.C).ToHunterLab(Illuminants.C).Build(); + private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.C).ToHunterLab(Illuminants.C).Build(); - [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + [Benchmark(Baseline = true, Description = "Colourful Convert")] + public double ColourfulConvert() + { + return ColourfulConverter.Convert(XYZColor).L; + } - [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToHunterLab(CieXyz).L; - } + [Benchmark(Description = "ImageSharp Convert")] + public float ColorSpaceConvert() + { + return ColorSpaceConverter.ToHunterLab(CieXyz).L; } } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index c6a51b3ec5..ba213e5b0a 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -8,28 +8,27 @@ using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; + +public class ColorspaceCieXyzToLmsConvert { - public class ColorspaceCieXyzToLmsConvert - { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); - private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ().ToLMS().Build(); + private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ().ToLMS().Build(); - [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + [Benchmark(Baseline = true, Description = "Colourful Convert")] + public double ColourfulConvert() + { + return ColourfulConverter.Convert(XYZColor).L; + } - [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToLms(CieXyz).L; - } + [Benchmark(Description = "ImageSharp Convert")] + public float ColorSpaceConvert() + { + return ColorSpaceConverter.ToLms(CieXyz).L; } } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index c8d23e944f..d7a5deafa7 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -8,28 +8,27 @@ using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; + +public class ColorspaceCieXyzToRgbConvert { - public class ColorspaceCieXyzToRgbConvert - { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); - private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(RGBWorkingSpaces.sRGB.WhitePoint).ToRGB(RGBWorkingSpaces.sRGB).Build(); + private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(RGBWorkingSpaces.sRGB.WhitePoint).ToRGB(RGBWorkingSpaces.sRGB).Build(); - [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).R; - } + [Benchmark(Baseline = true, Description = "Colourful Convert")] + public double ColourfulConvert() + { + return ColourfulConverter.Convert(XYZColor).R; + } - [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToRgb(CieXyz).R; - } + [Benchmark(Description = "ImageSharp Convert")] + public float ColorSpaceConvert() + { + return ColorSpaceConverter.ToRgb(CieXyz).R; } } diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index 1d20d3bc98..48b4880d94 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -8,28 +8,27 @@ using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; + +public class RgbWorkingSpaceAdapt { - public class RgbWorkingSpaceAdapt - { - private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb); + private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb); - private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717); + private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }); + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }); - private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.WideGamutRGB).ToRGB(RGBWorkingSpaces.sRGB).Build(); + private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.WideGamutRGB).ToRGB(RGBWorkingSpaces.sRGB).Build(); - [Benchmark(Baseline = true, Description = "Colourful Adapt")] - public RGBColor ColourfulConvert() - { - return ColourfulConverter.Convert(RGBColor); - } + [Benchmark(Baseline = true, Description = "Colourful Adapt")] + public RGBColor ColourfulConvert() + { + return ColourfulConverter.Convert(RGBColor); + } - [Benchmark(Description = "ImageSharp Adapt")] - public Rgb ColorSpaceConvert() - { - return ColorSpaceConverter.Adapt(Rgb); - } + [Benchmark(Description = "ImageSharp Adapt")] + public Rgb ColorSpaceConvert() + { + return ColorSpaceConverter.Adapt(Rgb); } } diff --git a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs index eacfc599e3..14d848bcb7 100644 --- a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs +++ b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs @@ -4,48 +4,47 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +public class YcbCrToRgb { - public class YcbCrToRgb + [Benchmark(Baseline = true, Description = "Floating Point Conversion")] + public Vector3 YcbCrToRgba() { - [Benchmark(Baseline = true, Description = "Floating Point Conversion")] - public Vector3 YcbCrToRgba() - { - int y = 255; - int cb = 128; - int cr = 128; - - int ccb = cb - 128; - int ccr = cr - 128; - - byte r = (byte)Numerics.Clamp(y + (1.402F * ccr), 0, 255); - byte g = (byte)Numerics.Clamp(y - (0.34414F * ccb) - (0.71414F * ccr), 0, 255); - byte b = (byte)Numerics.Clamp(y + (1.772F * ccb), 0, 255); - - return new Vector3(r, g, b); - } - - [Benchmark(Description = "Scaled Integer Conversion")] - public Vector3 YcbCrToRgbaScaled() - { - int y = 255; - int cb = 128; - int cr = 128; - - int ccb = cb - 128; - int ccr = cr - 128; - - // Scale by 1024, add .5F and truncate value - int r0 = 1436 * ccr; // (1.402F * 1024) + .5F - int g0 = 352 * ccb; // (0.34414F * 1024) + .5F - int g1 = 731 * ccr; // (0.71414F * 1024) + .5F - int b0 = 1815 * ccb; // (1.772F * 1024) + .5F - - byte r = (byte)Numerics.Clamp(y + (r0 >> 10), 0, 255); - byte g = (byte)Numerics.Clamp(y - (g0 >> 10) - (g1 >> 10), 0, 255); - byte b = (byte)Numerics.Clamp(y + (b0 >> 10), 0, 255); - - return new Vector3(r, g, b); - } + int y = 255; + int cb = 128; + int cr = 128; + + int ccb = cb - 128; + int ccr = cr - 128; + + byte r = (byte)Numerics.Clamp(y + (1.402F * ccr), 0, 255); + byte g = (byte)Numerics.Clamp(y - (0.34414F * ccb) - (0.71414F * ccr), 0, 255); + byte b = (byte)Numerics.Clamp(y + (1.772F * ccb), 0, 255); + + return new Vector3(r, g, b); + } + + [Benchmark(Description = "Scaled Integer Conversion")] + public Vector3 YcbCrToRgbaScaled() + { + int y = 255; + int cb = 128; + int cr = 128; + + int ccb = cb - 128; + int ccr = cr - 128; + + // Scale by 1024, add .5F and truncate value + int r0 = 1436 * ccr; // (1.402F * 1024) + .5F + int g0 = 352 * ccb; // (0.34414F * 1024) + .5F + int g1 = 731 * ccr; // (0.71414F * 1024) + .5F + int b0 = 1815 * ccb; // (1.772F * 1024) + .5F + + byte r = (byte)Numerics.Clamp(y + (r0 >> 10), 0, 255); + byte g = (byte)Numerics.Clamp(y - (g0 >> 10) - (g1 >> 10), 0, 255); + byte b = (byte)Numerics.Clamp(y + (b0 >> 10), 0, 255); + + return new Vector3(r, g, b); } } diff --git a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs index 4de745005f..5a0c574f17 100644 --- a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs +++ b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs @@ -5,75 +5,74 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +public partial class Config { - public partial class Config - { - private const string On = "1"; - private const string Off = "0"; + private const string On = "1"; + private const string Off = "0"; - // See https://github.com/SixLabors/ImageSharp/pull/1229#discussion_r440477861 - // * EnableHWIntrinsic - // * EnableSSE - // * EnableSSE2 - // * EnableAES - // * EnablePCLMULQDQ - // * EnableSSE3 - // * EnableSSSE3 - // * EnableSSE41 - // * EnableSSE42 - // * EnablePOPCNT - // * EnableAVX - // * EnableFMA - // * EnableAVX2 - // * EnableBMI1 - // * EnableBMI2 - // * EnableLZCNT - // - // `FeatureSIMD` ends up impacting all SIMD support(including `System.Numerics`) but not things - // like `LZCNT`, `BMI1`, or `BMI2` - // `EnableSSE3_4` is a legacy switch that exists for compat and is basically the same as `EnableSSE3` - private const string EnableAES = "COMPlus_EnableAES"; - private const string EnableAVX = "COMPlus_EnableAVX"; - private const string EnableAVX2 = "COMPlus_EnableAVX2"; - private const string EnableBMI1 = "COMPlus_EnableBMI1"; - private const string EnableBMI2 = "COMPlus_EnableBMI2"; - private const string EnableFMA = "COMPlus_EnableFMA"; - private const string EnableHWIntrinsic = "COMPlus_EnableHWIntrinsic"; - private const string EnableLZCNT = "COMPlus_EnableLZCNT"; - private const string EnablePCLMULQDQ = "COMPlus_EnablePCLMULQDQ"; - private const string EnablePOPCNT = "COMPlus_EnablePOPCNT"; - private const string EnableSSE = "COMPlus_EnableSSE"; - private const string EnableSSE2 = "COMPlus_EnableSSE2"; - private const string EnableSSE3 = "COMPlus_EnableSSE3"; - private const string EnableSSE3_4 = "COMPlus_EnableSSE3_4"; - private const string EnableSSE41 = "COMPlus_EnableSSE41"; - private const string EnableSSE42 = "COMPlus_EnableSSE42"; - private const string EnableSSSE3 = "COMPlus_EnableSSSE3"; - private const string FeatureSIMD = "COMPlus_FeatureSIMD"; + // See https://github.com/SixLabors/ImageSharp/pull/1229#discussion_r440477861 + // * EnableHWIntrinsic + // * EnableSSE + // * EnableSSE2 + // * EnableAES + // * EnablePCLMULQDQ + // * EnableSSE3 + // * EnableSSSE3 + // * EnableSSE41 + // * EnableSSE42 + // * EnablePOPCNT + // * EnableAVX + // * EnableFMA + // * EnableAVX2 + // * EnableBMI1 + // * EnableBMI2 + // * EnableLZCNT + // + // `FeatureSIMD` ends up impacting all SIMD support(including `System.Numerics`) but not things + // like `LZCNT`, `BMI1`, or `BMI2` + // `EnableSSE3_4` is a legacy switch that exists for compat and is basically the same as `EnableSSE3` + private const string EnableAES = "COMPlus_EnableAES"; + private const string EnableAVX = "COMPlus_EnableAVX"; + private const string EnableAVX2 = "COMPlus_EnableAVX2"; + private const string EnableBMI1 = "COMPlus_EnableBMI1"; + private const string EnableBMI2 = "COMPlus_EnableBMI2"; + private const string EnableFMA = "COMPlus_EnableFMA"; + private const string EnableHWIntrinsic = "COMPlus_EnableHWIntrinsic"; + private const string EnableLZCNT = "COMPlus_EnableLZCNT"; + private const string EnablePCLMULQDQ = "COMPlus_EnablePCLMULQDQ"; + private const string EnablePOPCNT = "COMPlus_EnablePOPCNT"; + private const string EnableSSE = "COMPlus_EnableSSE"; + private const string EnableSSE2 = "COMPlus_EnableSSE2"; + private const string EnableSSE3 = "COMPlus_EnableSSE3"; + private const string EnableSSE3_4 = "COMPlus_EnableSSE3_4"; + private const string EnableSSE41 = "COMPlus_EnableSSE41"; + private const string EnableSSE42 = "COMPlus_EnableSSE42"; + private const string EnableSSSE3 = "COMPlus_EnableSSSE3"; + private const string FeatureSIMD = "COMPlus_FeatureSIMD"; - public class HwIntrinsics_SSE_AVX : Config + public class HwIntrinsics_SSE_AVX : Config + { + public HwIntrinsics_SSE_AVX() { - public HwIntrinsics_SSE_AVX() + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) + .WithEnvironmentVariables( + new EnvironmentVariable(EnableHWIntrinsic, Off), + new EnvironmentVariable(FeatureSIMD, Off)) + .WithId("1. No HwIntrinsics").AsBaseline()); + + if (Sse.IsSupported) { this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) - .WithEnvironmentVariables( - new EnvironmentVariable(EnableHWIntrinsic, Off), - new EnvironmentVariable(FeatureSIMD, Off)) - .WithId("1. No HwIntrinsics").AsBaseline()); - - if (Sse.IsSupported) - { - this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) - .WithEnvironmentVariables(new EnvironmentVariable(EnableAVX, Off)) - .WithId("2. SSE")); - } + .WithEnvironmentVariables(new EnvironmentVariable(EnableAVX, Off)) + .WithId("2. SSE")); + } - if (Avx.IsSupported) - { - this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) - .WithId("3. AVX")); - } + if (Avx.IsSupported) + { + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) + .WithId("3. AVX")); } } } diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index f05c0af0f4..d95f4a202c 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -11,44 +11,43 @@ using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Reports; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +public partial class Config : ManualConfig { - public partial class Config : ManualConfig + public Config() { - public Config() - { - this.AddDiagnoser(MemoryDiagnoser.Default); + this.AddDiagnoser(MemoryDiagnoser.Default); #if OS_WINDOWS - if (this.IsElevated) - { - this.AddDiagnoser(new NativeMemoryProfiler()); - } + if (this.IsElevated) + { + this.AddDiagnoser(new NativeMemoryProfiler()); + } #endif - this.SummaryStyle = SummaryStyle.Default.WithMaxParameterColumnWidth(50); - } + this.SummaryStyle = SummaryStyle.Default.WithMaxParameterColumnWidth(50); + } - public class MultiFramework : Config - { - public MultiFramework() => this.AddJob( - Job.Default.WithRuntime(CoreRuntime.Core60).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); - } + public class MultiFramework : Config + { + public MultiFramework() => this.AddJob( + Job.Default.WithRuntime(CoreRuntime.Core60).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + } - public class ShortMultiFramework : Config - { - public ShortMultiFramework() => this.AddJob( - Job.Default.WithRuntime(CoreRuntime.Core60).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); - } + public class ShortMultiFramework : Config + { + public ShortMultiFramework() => this.AddJob( + Job.Default.WithRuntime(CoreRuntime.Core60).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + } - public class ShortCore31 : Config - { - public ShortCore31() - => this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); - } + public class ShortCore31 : Config + { + public ShortCore31() + => this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); + } #if OS_WINDOWS - private bool IsElevated => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); + private bool IsElevated => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); #endif - } } diff --git a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs index 3c58eac06e..1aac3cff50 100644 --- a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs +++ b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs @@ -1,72 +1,70 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Compression.Zlib; using SharpAdler32 = ICSharpCode.SharpZipLib.Checksum.Adler32; -namespace SixLabors.ImageSharp.Benchmarks.General -{ - [Config(typeof(Config.ShortMultiFramework))] - public class Adler32Benchmark - { - private byte[] data; - private readonly SharpAdler32 adler = new SharpAdler32(); +namespace SixLabors.ImageSharp.Benchmarks.General; - [Params(1024, 2048, 4096)] - public int Count { get; set; } +[Config(typeof(Config.ShortMultiFramework))] +public class Adler32Benchmark +{ + private byte[] data; + private readonly SharpAdler32 adler = new SharpAdler32(); - [GlobalSetup] - public void SetUp() - { - this.data = new byte[this.Count]; - new Random(1).NextBytes(this.data); - } + [Params(1024, 2048, 4096)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public long SharpZipLibCalculate() - { - this.adler.Reset(); - this.adler.Update(this.data); - return this.adler.Value; - } + [GlobalSetup] + public void SetUp() + { + this.data = new byte[this.Count]; + new Random(1).NextBytes(this.data); + } - [Benchmark] - public uint SixLaborsCalculate() - { - return Adler32.Calculate(this.data); - } + [Benchmark(Baseline = true)] + public long SharpZipLibCalculate() + { + this.adler.Reset(); + this.adler.Update(this.data); + return this.adler.Value; } - // ########## 17/05/2020 ########## - // - // | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |--------------------- |-------------- |------ |------------:|------------:|----------:|------:|--------:|------:|------:|------:|----------:| - // | SharpZipLibCalculate | .NET 4.7.2 | 1024 | 793.18 ns | 775.66 ns | 42.516 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET 4.7.2 | 1024 | 384.86 ns | 15.64 ns | 0.857 ns | 0.49 | 0.03 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 2.1 | 1024 | 790.31 ns | 353.34 ns | 19.368 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 2.1 | 1024 | 465.28 ns | 652.41 ns | 35.761 ns | 0.59 | 0.03 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 3.1 | 1024 | 877.25 ns | 97.89 ns | 5.365 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 3.1 | 1024 | 45.60 ns | 13.28 ns | 0.728 ns | 0.05 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET 4.7.2 | 2048 | 1,537.04 ns | 428.44 ns | 23.484 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET 4.7.2 | 2048 | 849.76 ns | 1,066.34 ns | 58.450 ns | 0.55 | 0.04 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 2.1 | 2048 | 1,616.97 ns | 276.70 ns | 15.167 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 2.1 | 2048 | 790.77 ns | 691.71 ns | 37.915 ns | 0.49 | 0.03 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 3.1 | 2048 | 1,735.11 ns | 1,374.22 ns | 75.325 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 3.1 | 2048 | 87.80 ns | 56.84 ns | 3.116 ns | 0.05 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET 4.7.2 | 4096 | 3,054.53 ns | 796.41 ns | 43.654 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET 4.7.2 | 4096 | 1,538.90 ns | 487.02 ns | 26.695 ns | 0.50 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 2.1 | 4096 | 3,223.48 ns | 32.32 ns | 1.771 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 2.1 | 4096 | 1,547.60 ns | 309.72 ns | 16.977 ns | 0.48 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 3.1 | 4096 | 3,672.33 ns | 1,095.81 ns | 60.065 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 3.1 | 4096 | 159.44 ns | 36.31 ns | 1.990 ns | 0.04 | 0.00 | - | - | - | - | + [Benchmark] + public uint SixLaborsCalculate() + { + return Adler32.Calculate(this.data); + } } + +// ########## 17/05/2020 ########## +// +// | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------------------- |-------------- |------ |------------:|------------:|----------:|------:|--------:|------:|------:|------:|----------:| +// | SharpZipLibCalculate | .NET 4.7.2 | 1024 | 793.18 ns | 775.66 ns | 42.516 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET 4.7.2 | 1024 | 384.86 ns | 15.64 ns | 0.857 ns | 0.49 | 0.03 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 2.1 | 1024 | 790.31 ns | 353.34 ns | 19.368 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 2.1 | 1024 | 465.28 ns | 652.41 ns | 35.761 ns | 0.59 | 0.03 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 3.1 | 1024 | 877.25 ns | 97.89 ns | 5.365 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 3.1 | 1024 | 45.60 ns | 13.28 ns | 0.728 ns | 0.05 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET 4.7.2 | 2048 | 1,537.04 ns | 428.44 ns | 23.484 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET 4.7.2 | 2048 | 849.76 ns | 1,066.34 ns | 58.450 ns | 0.55 | 0.04 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 2.1 | 2048 | 1,616.97 ns | 276.70 ns | 15.167 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 2.1 | 2048 | 790.77 ns | 691.71 ns | 37.915 ns | 0.49 | 0.03 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 3.1 | 2048 | 1,735.11 ns | 1,374.22 ns | 75.325 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 3.1 | 2048 | 87.80 ns | 56.84 ns | 3.116 ns | 0.05 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET 4.7.2 | 4096 | 3,054.53 ns | 796.41 ns | 43.654 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET 4.7.2 | 4096 | 1,538.90 ns | 487.02 ns | 26.695 ns | 0.50 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 2.1 | 4096 | 3,223.48 ns | 32.32 ns | 1.771 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 2.1 | 4096 | 1,547.60 ns | 309.72 ns | 16.977 ns | 0.48 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 3.1 | 4096 | 3,672.33 ns | 1,095.81 ns | 60.065 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 3.1 | 4096 | 159.44 ns | 36.31 ns | 1.990 ns | 0.04 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index 76706e2d4d..f0a36ee1d6 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -1,130 +1,126 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp; +namespace SixLabors.ImageSharp.Benchmarks.General; -namespace SixLabors.ImageSharp.Benchmarks.General -{ - /* - Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | +/* + Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | -------------------------------------------- |------ |---------:|---------:|---------:|-------:|---------:| - 'Emulated 2D array access using flat array' | 32 | 224.2 ns | 4.739 ns | 13.75 ns | 0.65 | 0.07 | - 'Array access using 2D array' | 32 | 346.6 ns | 9.225 ns | 26.91 ns | 1.00 | 0.00 | - 'Array access using a jagged array' | 32 | 229.3 ns | 6.028 ns | 17.58 ns | 0.67 | 0.07 | - 'Array access using DenseMatrix' | 32 | 223.2 ns | 5.248 ns | 15.22 ns | 0.65 | 0.07 | - - * - */ - public class Array2D - { - private float[] flatArray; +'Emulated 2D array access using flat array' | 32 | 224.2 ns | 4.739 ns | 13.75 ns | 0.65 | 0.07 | + 'Array access using 2D array' | 32 | 346.6 ns | 9.225 ns | 26.91 ns | 1.00 | 0.00 | + 'Array access using a jagged array' | 32 | 229.3 ns | 6.028 ns | 17.58 ns | 0.67 | 0.07 | + 'Array access using DenseMatrix' | 32 | 223.2 ns | 5.248 ns | 15.22 ns | 0.65 | 0.07 | + + * + */ +public class Array2D +{ + private float[] flatArray; - private float[,] array2D; + private float[,] array2D; - private float[][] jaggedData; + private float[][] jaggedData; - private DenseMatrix matrix; + private DenseMatrix matrix; - [Params(4, 16, 32)] - public int Count { get; set; } + [Params(4, 16, 32)] + public int Count { get; set; } - public int Min { get; private set; } + public int Min { get; private set; } - public int Max { get; private set; } + public int Max { get; private set; } - [GlobalSetup] - public void SetUp() - { - this.flatArray = new float[this.Count * this.Count]; - this.array2D = new float[this.Count, this.Count]; - this.jaggedData = new float[this.Count][]; + [GlobalSetup] + public void SetUp() + { + this.flatArray = new float[this.Count * this.Count]; + this.array2D = new float[this.Count, this.Count]; + this.jaggedData = new float[this.Count][]; - for (int i = 0; i < this.Count; i++) - { - this.jaggedData[i] = new float[this.Count]; - } + for (int i = 0; i < this.Count; i++) + { + this.jaggedData[i] = new float[this.Count]; + } - this.matrix = new DenseMatrix(this.array2D); + this.matrix = new DenseMatrix(this.array2D); - this.Min = (this.Count / 2) - 10; - this.Min = Math.Max(0, this.Min); - this.Max = this.Min + Math.Min(10, this.Count); - } + this.Min = (this.Count / 2) - 10; + this.Min = Math.Max(0, this.Min); + this.Max = this.Min + Math.Min(10, this.Count); + } - [Benchmark(Description = "Emulated 2D array access using flat array")] - public float FlatArrayIndex() + [Benchmark(Description = "Emulated 2D array access using flat array")] + public float FlatArrayIndex() + { + float[] a = this.flatArray; + float s = 0; + int count = this.Count; + for (int i = this.Min; i < this.Max; i++) { - float[] a = this.flatArray; - float s = 0; - int count = this.Count; - for (int i = this.Min; i < this.Max; i++) + for (int j = this.Min; j < this.Max; j++) { - for (int j = this.Min; j < this.Max; j++) - { - ref float v = ref a[(count * i) + j]; - v = i * j; - s += v; - } + ref float v = ref a[(count * i) + j]; + v = i * j; + s += v; } - - return s; } - [Benchmark(Baseline = true, Description = "Array access using 2D array")] - public float Array2DIndex() + return s; + } + + [Benchmark(Baseline = true, Description = "Array access using 2D array")] + public float Array2DIndex() + { + float s = 0; + float[,] a = this.array2D; + for (int i = this.Min; i < this.Max; i++) { - float s = 0; - float[,] a = this.array2D; - for (int i = this.Min; i < this.Max; i++) + for (int j = this.Min; j < this.Max; j++) { - for (int j = this.Min; j < this.Max; j++) - { - ref float v = ref a[i, j]; - v = i * j; - s += v; - } + ref float v = ref a[i, j]; + v = i * j; + s += v; } - - return s; } - [Benchmark(Description = "Array access using a jagged array")] - public float ArrayJaggedIndex() + return s; + } + + [Benchmark(Description = "Array access using a jagged array")] + public float ArrayJaggedIndex() + { + float s = 0; + float[][] a = this.jaggedData; + for (int i = this.Min; i < this.Max; i++) { - float s = 0; - float[][] a = this.jaggedData; - for (int i = this.Min; i < this.Max; i++) + for (int j = this.Min; j < this.Max; j++) { - for (int j = this.Min; j < this.Max; j++) - { - ref float v = ref a[i][j]; - v = i * j; - s += v; - } + ref float v = ref a[i][j]; + v = i * j; + s += v; } - - return s; } - [Benchmark(Description = "Array access using DenseMatrix")] - public float ArrayMatrixIndex() + return s; + } + + [Benchmark(Description = "Array access using DenseMatrix")] + public float ArrayMatrixIndex() + { + float s = 0; + DenseMatrix a = this.matrix; + for (int i = this.Min; i < this.Max; i++) { - float s = 0; - DenseMatrix a = this.matrix; - for (int i = this.Min; i < this.Max; i++) + for (int j = this.Min; j < this.Max; j++) { - for (int j = this.Min; j < this.Max; j++) - { - ref float v = ref a[i, j]; - v = i * j; - s += v; - } + ref float v = ref a[i, j]; + v = i * j; + s += v; } - - return s; } + + return s; } } diff --git a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs index 1c102945e5..034f20e05f 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs @@ -1,59 +1,57 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General; + +public class ArrayReverse { - public class ArrayReverse - { - [Params(4, 16, 32)] - public int Count { get; set; } + [Params(4, 16, 32)] + public int Count { get; set; } - private byte[] source; + private byte[] source; - private byte[] destination; + private byte[] destination; - [GlobalSetup] - public void SetUp() - { - this.source = new byte[this.Count]; - this.destination = new byte[this.Count]; - } + [GlobalSetup] + public void SetUp() + { + this.source = new byte[this.Count]; + this.destination = new byte[this.Count]; + } - [Benchmark(Baseline = true, Description = "Copy using Array.Reverse()")] - public void ReverseArray() - { - Array.Reverse(this.source, 0, this.Count); - } + [Benchmark(Baseline = true, Description = "Copy using Array.Reverse()")] + public void ReverseArray() + { + Array.Reverse(this.source, 0, this.Count); + } + + [Benchmark(Description = "Reverse using loop")] + public void ReverseLoop() + { + this.ReverseBytes(this.source, 0, this.Count); - [Benchmark(Description = "Reverse using loop")] - public void ReverseLoop() + /* + for (int i = 0; i < this.source.Length / 2; i++) { - this.ReverseBytes(this.source, 0, this.Count); - - /* - for (int i = 0; i < this.source.Length / 2; i++) - { - byte tmp = this.source[i]; - this.source[i] = this.source[this.source.Length - i - 1]; - this.source[this.source.Length - i - 1] = tmp; - }*/ - } + byte tmp = this.source[i]; + this.source[i] = this.source[this.source.Length - i - 1]; + this.source[this.source.Length - i - 1] = tmp; + }*/ + } - public void ReverseBytes(byte[] source, int index, int length) + public void ReverseBytes(byte[] source, int index, int length) + { + int i = index; + int j = index + length - 1; + while (i < j) { - int i = index; - int j = index + length - 1; - while (i < j) - { - byte temp = source[i]; - source[i] = source[j]; - source[j] = temp; - i++; - j--; - } + byte temp = source[i]; + source[i] = source[j]; + source[j] = temp; + i++; + j--; } } } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs index b51834a52a..ac87ea5d1f 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs @@ -1,43 +1,41 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class Abs { - public class Abs - { - [Params(-1, 1)] - public int X { get; set; } + [Params(-1, 1)] + public int X { get; set; } - [Benchmark(Baseline = true, Description = "Maths Abs")] - public int MathAbs() - { - int x = this.X; - return Math.Abs(x); - } + [Benchmark(Baseline = true, Description = "Maths Abs")] + public int MathAbs() + { + int x = this.X; + return Math.Abs(x); + } - [Benchmark(Description = "Conditional Abs")] - public int ConditionalAbs() - { - int x = this.X; - return x < 0 ? -x : x; - } + [Benchmark(Description = "Conditional Abs")] + public int ConditionalAbs() + { + int x = this.X; + return x < 0 ? -x : x; + } - [Benchmark(Description = "Bitwise Abs")] - public int AbsBitwise() - { - int x = this.X; - return (x ^ (x >> 31)) - (x >> 31); - } + [Benchmark(Description = "Bitwise Abs")] + public int AbsBitwise() + { + int x = this.X; + return (x ^ (x >> 31)) - (x >> 31); + } - [Benchmark(Description = "Bitwise Abs With Variable")] - public int AbsBitwiseVer() - { - int x = this.X; - int y = x >> 31; - return (x ^ y) - y; - } + [Benchmark(Description = "Bitwise Abs With Variable")] + public int AbsBitwiseVer() + { + int x = this.X; + int y = x >> 31; + return (x ^ y) - y; } } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs index 8cab72f51a..dd0bc28785 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs @@ -1,70 +1,68 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class ClampFloat { - public class ClampFloat + private readonly float min = -1.5f; + private readonly float max = 2.5f; + private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + + [Benchmark(Baseline = true)] + public float UsingMathF() { - private readonly float min = -1.5f; - private readonly float max = 2.5f; - private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + float acc = 0; - [Benchmark(Baseline = true)] - public float UsingMathF() + for (int i = 0; i < Values.Length; i++) { - float acc = 0; + acc += ClampUsingMathF(Values[i], this.min, this.max); + } - for (int i = 0; i < Values.Length; i++) - { - acc += ClampUsingMathF(Values[i], this.min, this.max); - } + return acc; + } - return acc; - } + [Benchmark] + public float UsingBranching() + { + float acc = 0; - [Benchmark] - public float UsingBranching() + for (int i = 0; i < Values.Length; i++) { - float acc = 0; + acc += ClampUsingBranching(Values[i], this.min, this.max); + } - for (int i = 0; i < Values.Length; i++) - { - acc += ClampUsingBranching(Values[i], this.min, this.max); - } + return acc; + } - return acc; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ClampUsingMathF(float x, float min, float max) + { + return Math.Min(max, Math.Max(min, x)); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float ClampUsingMathF(float x, float min, float max) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ClampUsingBranching(float x, float min, float max) + { + if (x >= max) { - return Math.Min(max, Math.Max(min, x)); + return max; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float ClampUsingBranching(float x, float min, float max) + if (x <= min) { - if (x >= max) - { - return max; - } - - if (x <= min) - { - return min; - } - - return x; + return min; } - // RESULTS: - // Method | Mean | Error | StdDev | Scaled | - // --------------- |---------:|----------:|----------:|-------:| - // UsingMathF | 30.37 ns | 0.3764 ns | 0.3337 ns | 1.00 | - // UsingBranching | 18.66 ns | 0.1043 ns | 0.0871 ns | 0.61 | + return x; } + + // RESULTS: + // Method | Mean | Error | StdDev | Scaled | + // --------------- |---------:|----------:|----------:|-------:| + // UsingMathF | 30.37 ns | 0.3764 ns | 0.3337 ns | 1.00 | + // UsingBranching | 18.66 ns | 0.1043 ns | 0.0871 ns | 0.61 | } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs index 94c56603fb..40ea0d253b 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs @@ -1,92 +1,90 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class ClampInt32IntoByte { - public class ClampInt32IntoByte + [Params(-1, 0, 255, 256)] + public int Value { get; set; } + + [Benchmark(Baseline = true, Description = "Maths Clamp")] + public byte ClampMaths() { - [Params(-1, 0, 255, 256)] - public int Value { get; set; } + int value = this.Value; + return (byte)Math.Min(Math.Max(byte.MinValue, value), byte.MaxValue); + } - [Benchmark(Baseline = true, Description = "Maths Clamp")] - public byte ClampMaths() - { - int value = this.Value; - return (byte)Math.Min(Math.Max(byte.MinValue, value), byte.MaxValue); - } + [Benchmark(Description = "No Maths Clamp")] + public byte ClampNoMaths() + { + int value = this.Value; + value = value >= byte.MaxValue ? byte.MaxValue : value; + return (byte)(value <= byte.MinValue ? byte.MinValue : value); + } - [Benchmark(Description = "No Maths Clamp")] - public byte ClampNoMaths() - { - int value = this.Value; - value = value >= byte.MaxValue ? byte.MaxValue : value; - return (byte)(value <= byte.MinValue ? byte.MinValue : value); - } + [Benchmark(Description = "No Maths No Equals Clamp")] + public byte ClampNoMathsNoEquals() + { + int value = this.Value; + value = value > byte.MaxValue ? byte.MaxValue : value; + return (byte)(value < byte.MinValue ? byte.MinValue : value); + } - [Benchmark(Description = "No Maths No Equals Clamp")] - public byte ClampNoMathsNoEquals() + [Benchmark(Description = "No Maths Clamp No Ternary")] + public byte ClampNoMathsNoTernary() + { + int value = this.Value; + + if (value >= byte.MaxValue) { - int value = this.Value; - value = value > byte.MaxValue ? byte.MaxValue : value; - return (byte)(value < byte.MinValue ? byte.MinValue : value); + return byte.MaxValue; } - [Benchmark(Description = "No Maths Clamp No Ternary")] - public byte ClampNoMathsNoTernary() + if (value <= byte.MinValue) { - int value = this.Value; - - if (value >= byte.MaxValue) - { - return byte.MaxValue; - } - - if (value <= byte.MinValue) - { - return byte.MinValue; - } - - return (byte)value; + return byte.MinValue; } - [Benchmark(Description = "No Maths No Equals Clamp No Ternary")] - public byte ClampNoMathsEqualsNoTernary() - { - int value = this.Value; + return (byte)value; + } - if (value > byte.MaxValue) - { - return byte.MaxValue; - } + [Benchmark(Description = "No Maths No Equals Clamp No Ternary")] + public byte ClampNoMathsEqualsNoTernary() + { + int value = this.Value; - if (value < byte.MinValue) - { - return byte.MinValue; - } + if (value > byte.MaxValue) + { + return byte.MaxValue; + } - return (byte)value; - } + if (value < byte.MinValue) + { + return byte.MinValue; + } - [Benchmark(Description = "Clamp using Bitwise Abs")] - public byte ClampBitwise() - { - int x = this.Value; - int absMax = byte.MaxValue - x; - x = (x + byte.MaxValue - AbsBitwiseVer(ref absMax)) >> 1; - x = (x + byte.MinValue + AbsBitwiseVer(ref x)) >> 1; + return (byte)value; + } - return (byte)x; - } + [Benchmark(Description = "Clamp using Bitwise Abs")] + public byte ClampBitwise() + { + int x = this.Value; + int absMax = byte.MaxValue - x; + x = (x + byte.MaxValue - AbsBitwiseVer(ref absMax)) >> 1; + x = (x + byte.MinValue + AbsBitwiseVer(ref x)) >> 1; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int AbsBitwiseVer(ref int x) - { - int y = x >> 31; - return (x ^ y) - y; - } + return (byte)x; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int AbsBitwiseVer(ref int x) + { + int y = x >> 31; + return (x ^ y) - y; } } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs index 67c82a2407..d47fcb687e 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs @@ -1,43 +1,41 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class ClampSpan { - public class ClampSpan + private static readonly int[] A = new int[2048]; + private static readonly int[] B = new int[2048]; + + public void Setup() { - private static readonly int[] A = new int[2048]; - private static readonly int[] B = new int[2048]; + var r = new Random(); - public void Setup() + for (int i = 0; i < A.Length; i++) { - var r = new Random(); - - for (int i = 0; i < A.Length; i++) - { - int x = r.Next(); - A[i] = x; - B[i] = x; - } + int x = r.Next(); + A[i] = x; + B[i] = x; } + } - [Benchmark(Baseline = true)] - public void ClampNoIntrinsics() + [Benchmark(Baseline = true)] + public void ClampNoIntrinsics() + { + for (int i = 0; i < A.Length; i++) { - for (int i = 0; i < A.Length; i++) - { - ref int x = ref A[i]; - x = Numerics.Clamp(x, 64, 128); - } + ref int x = ref A[i]; + x = Numerics.Clamp(x, 64, 128); } + } - [Benchmark] - public void ClampVectorIntrinsics() - { - Numerics.Clamp(B, 64, 128); - } + [Benchmark] + public void ClampVectorIntrinsics() + { + Numerics.Clamp(B, 64, 128); } } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs index 6941a7efe3..186f88bb71 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs @@ -5,56 +5,55 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class ClampVector4 { - public class ClampVector4 + private readonly float min = -1.5f; + private readonly float max = 2.5f; + private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + + [Benchmark(Baseline = true)] + public Vector4 UsingVectorClamp() { - private readonly float min = -1.5f; - private readonly float max = 2.5f; - private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + Vector4 acc = Vector4.Zero; - [Benchmark(Baseline = true)] - public Vector4 UsingVectorClamp() + for (int i = 0; i < Values.Length; i++) { - Vector4 acc = Vector4.Zero; - - for (int i = 0; i < Values.Length; i++) - { - acc += ClampUsingVectorClamp(Values[i], this.min, this.max); - } - - return acc; + acc += ClampUsingVectorClamp(Values[i], this.min, this.max); } - [Benchmark] - public Vector4 UsingVectorMinMax() - { - Vector4 acc = Vector4.Zero; - - for (int i = 0; i < Values.Length; i++) - { - acc += ClampUsingVectorMinMax(Values[i], this.min, this.max); - } + return acc; + } - return acc; - } + [Benchmark] + public Vector4 UsingVectorMinMax() + { + Vector4 acc = Vector4.Zero; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 ClampUsingVectorClamp(float x, float min, float max) + for (int i = 0; i < Values.Length; i++) { - return Vector4.Clamp(new Vector4(x), new Vector4(min), new Vector4(max)); + acc += ClampUsingVectorMinMax(Values[i], this.min, this.max); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 ClampUsingVectorMinMax(float x, float min, float max) - { - return Vector4.Min(new Vector4(max), Vector4.Max(new Vector4(min), new Vector4(x))); - } + return acc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ClampUsingVectorClamp(float x, float min, float max) + { + return Vector4.Clamp(new Vector4(x), new Vector4(min), new Vector4(max)); + } - // RESULTS - // | Method | Mean | Error | StdDev | Ratio | - // |------------------ |---------:|---------:|---------:|------:| - // | UsingVectorClamp | 75.21 ns | 1.572 ns | 4.057 ns | 1.00 | - // | UsingVectorMinMax | 15.35 ns | 0.356 ns | 0.789 ns | 0.20 | + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ClampUsingVectorMinMax(float x, float min, float max) + { + return Vector4.Min(new Vector4(max), Vector4.Max(new Vector4(min), new Vector4(x))); } + + // RESULTS + // | Method | Mean | Error | StdDev | Ratio | + // |------------------ |---------:|---------:|---------:|------:| + // | UsingVectorClamp | 75.21 ns | 1.572 ns | 4.057 ns | 1.00 | + // | UsingVectorMinMax | 15.35 ns | 0.356 ns | 0.789 ns | 0.20 | } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs index cbb35a2a5f..937966ad5e 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs @@ -3,23 +3,22 @@ using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +[LongRunJob] +public class ModuloPowerOfTwoConstant { - [LongRunJob] - public class ModuloPowerOfTwoConstant - { - private readonly int value = 42; + private readonly int value = 42; - [Benchmark(Baseline = true)] - public int Standard() - { - return this.value % 8; - } + [Benchmark(Baseline = true)] + public int Standard() + { + return this.value % 8; + } - [Benchmark] - public int Bitwise() - { - return Numerics.Modulo8(this.value); - } + [Benchmark] + public int Bitwise() + { + return Numerics.Modulo8(this.value); } } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs index 48bf0946e2..5973eda5f1 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs @@ -3,32 +3,31 @@ using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath -{ - [LongRunJob] - public class ModuloPowerOfTwoVariable - { - private readonly int value = 42; +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; - private readonly int m = 32; +[LongRunJob] +public class ModuloPowerOfTwoVariable +{ + private readonly int value = 42; - [Benchmark(Baseline = true)] - public int Standard() - { - return this.value % this.m; - } + private readonly int m = 32; - [Benchmark] - public int Bitwise() - { - return Numerics.ModuloP2(this.value, this.m); - } + [Benchmark(Baseline = true)] + public int Standard() + { + return this.value % this.m; + } - // RESULTS: - // - // Method | Mean | Error | StdDev | Median | Scaled | ScaledSD | - // --------- |----------:|----------:|----------:|----------:|-------:|---------:| - // Standard | 1.2465 ns | 0.0093 ns | 0.0455 ns | 1.2423 ns | 1.00 | 0.00 | - // Bitwise | 0.0265 ns | 0.0103 ns | 0.0515 ns | 0.0000 ns | 0.02 | 0.04 | + [Benchmark] + public int Bitwise() + { + return Numerics.ModuloP2(this.value, this.m); } + + // RESULTS: + // + // Method | Mean | Error | StdDev | Median | Scaled | ScaledSD | + // --------- |----------:|----------:|----------:|----------:|-------:|---------:| + // Standard | 1.2465 ns | 0.0093 ns | 0.0455 ns | 1.2423 ns | 1.00 | 0.00 | + // Bitwise | 0.0265 ns | 0.0103 ns | 0.0515 ns | 0.0000 ns | 0.02 | 0.04 | } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs index 2074767ae8..7f447f0bad 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs @@ -1,42 +1,40 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class Pow { - public class Pow - { - [Params(-1.333F, 1.333F)] - public float X { get; set; } + [Params(-1.333F, 1.333F)] + public float X { get; set; } - [Benchmark(Baseline = true, Description = "Math.Pow 2")] - public float MathPow() - { - float x = this.X; - return (float)Math.Pow(x, 2); - } + [Benchmark(Baseline = true, Description = "Math.Pow 2")] + public float MathPow() + { + float x = this.X; + return (float)Math.Pow(x, 2); + } - [Benchmark(Description = "Pow x2")] - public float PowMult() - { - float x = this.X; - return x * x; - } + [Benchmark(Description = "Pow x2")] + public float PowMult() + { + float x = this.X; + return x * x; + } - [Benchmark(Description = "Math.Pow 3")] - public float MathPow3() - { - float x = this.X; - return (float)Math.Pow(x, 3); - } + [Benchmark(Description = "Math.Pow 3")] + public float MathPow3() + { + float x = this.X; + return (float)Math.Pow(x, 3); + } - [Benchmark(Description = "Pow x3")] - public float PowMult3() - { - float x = this.X; - return x * x * x; - } + [Benchmark(Description = "Pow x3")] + public float PowMult3() + { + float x = this.X; + return x * x * x; } } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs index 71936d8b08..c90a7f3181 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs @@ -1,25 +1,23 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; + +public class Round { - public class Round - { - private const float Input = .51F; + private const float Input = .51F; - [Benchmark] - public int ConvertTo() => Convert.ToInt32(Input); + [Benchmark] + public int ConvertTo() => Convert.ToInt32(Input); - [Benchmark] - public int MathRound() => (int)Math.Round(Input); + [Benchmark] + public int MathRound() => (int)Math.Round(Input); - // Results 20th Jan 2019 - // Method | Mean | Error | StdDev | Median | - //---------- |----------:|----------:|----------:|----------:| - // ConvertTo | 3.1967 ns | 0.1234 ns | 0.2129 ns | 3.2340 ns | - // MathRound | 0.0528 ns | 0.0374 ns | 0.1079 ns | 0.0000 ns | - } + // Results 20th Jan 2019 + // Method | Mean | Error | StdDev | Median | + //---------- |----------:|----------:|----------:|----------:| + // ConvertTo | 3.1967 ns | 0.1234 ns | 0.2129 ns | 3.2340 ns | + // MathRound | 0.0528 ns | 0.0374 ns | 0.1079 ns | 0.0000 ns | } diff --git a/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs index 7712fc4e69..6b27cb3db0 100644 --- a/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs +++ b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs @@ -5,42 +5,41 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General; + +public class Buffer2D_DangerousGetRowSpan { - public class Buffer2D_DangerousGetRowSpan + private const int Height = 1024; + + [Params(0.5, 2.0, 10.0)] + public double SizeMegaBytes { get; set; } + + private Buffer2D buffer; + + [GlobalSetup] + public unsafe void Setup() { - private const int Height = 1024; - - [Params(0.5, 2.0, 10.0)] - public double SizeMegaBytes { get; set; } - - private Buffer2D buffer; - - [GlobalSetup] - public unsafe void Setup() - { - int totalElements = (int)(1024 * 1024 * this.SizeMegaBytes) / sizeof(Rgba32); - - int width = totalElements / Height; - MemoryAllocator allocator = Configuration.Default.MemoryAllocator; - this.buffer = allocator.Allocate2D(width, Height); - } - - [GlobalCleanup] - public void Cleanup() => this.buffer.Dispose(); - - [Benchmark] - public int DangerousGetRowSpan() => - this.buffer.DangerousGetRowSpan(1).Length + - this.buffer.DangerousGetRowSpan(Height - 1).Length; - - // BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044 - // Intel Core i9-10900X CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores - // - // | Method | SizeMegaBytes | Mean | Error | StdDev | - // |-------------------- |-------------- |----------:|----------:|----------:| - // | DangerousGetRowSpan | 0.5 | 7.498 ns | 0.1784 ns | 0.3394 ns | - // | DangerousGetRowSpan | 2 | 6.542 ns | 0.1565 ns | 0.3659 ns | - // | DangerousGetRowSpan | 10 | 38.556 ns | 0.6604 ns | 0.8587 ns | + int totalElements = (int)(1024 * 1024 * this.SizeMegaBytes) / sizeof(Rgba32); + + int width = totalElements / Height; + MemoryAllocator allocator = Configuration.Default.MemoryAllocator; + this.buffer = allocator.Allocate2D(width, Height); } + + [GlobalCleanup] + public void Cleanup() => this.buffer.Dispose(); + + [Benchmark] + public int DangerousGetRowSpan() => + this.buffer.DangerousGetRowSpan(1).Length + + this.buffer.DangerousGetRowSpan(Height - 1).Length; + + // BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044 + // Intel Core i9-10900X CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores + // + // | Method | SizeMegaBytes | Mean | Error | StdDev | + // |-------------------- |-------------- |----------:|----------:|----------:| + // | DangerousGetRowSpan | 0.5 | 7.498 ns | 0.1784 ns | 0.3394 ns | + // | DangerousGetRowSpan | 2 | 6.542 ns | 0.1565 ns | 0.3659 ns | + // | DangerousGetRowSpan | 10 | 38.556 ns | 0.6604 ns | 0.8587 ns | } diff --git a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs index 102876c7b8..3929f7c5ac 100644 --- a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs +++ b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs @@ -1,228 +1,226 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General; + +/// +/// Compare different methods for copying native and/or managed buffers. +/// Conclusions: +/// - Span.CopyTo() has terrible performance on classic .NET Framework +/// - Buffer.MemoryCopy() performance is good enough for all sizes (but needs pinning) +/// +[Config(typeof(Config.ShortMultiFramework))] +public class CopyBuffers { - /// - /// Compare different methods for copying native and/or managed buffers. - /// Conclusions: - /// - Span.CopyTo() has terrible performance on classic .NET Framework - /// - Buffer.MemoryCopy() performance is good enough for all sizes (but needs pinning) - /// - [Config(typeof(Config.ShortMultiFramework))] - public class CopyBuffers + private byte[] destArray; + + private MemoryHandle destHandle; + + private Memory destMemory; + + private byte[] sourceArray; + + private MemoryHandle sourceHandle; + + private Memory sourceMemory; + + [Params(10, 50, 100, 1000, 10000)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.sourceArray = new byte[this.Count]; + this.sourceMemory = new Memory(this.sourceArray); + this.sourceHandle = this.sourceMemory.Pin(); + + this.destArray = new byte[this.Count]; + this.destMemory = new Memory(this.destArray); + this.destHandle = this.destMemory.Pin(); + } + + [GlobalCleanup] + public void Cleanup() + { + this.sourceHandle.Dispose(); + this.destHandle.Dispose(); + } + + [Benchmark(Baseline = true, Description = "Array.Copy()")] + public void ArrayCopy() + { + Array.Copy(this.sourceArray, this.destArray, this.Count); + } + + [Benchmark(Description = "Buffer.BlockCopy()")] + public void BufferBlockCopy() + { + Buffer.BlockCopy(this.sourceArray, 0, this.destArray, 0, this.Count); + } + + [Benchmark(Description = "Buffer.MemoryCopy()")] + public unsafe void BufferMemoryCopy() + { + void* pinnedDestination = this.destHandle.Pointer; + void* pinnedSource = this.sourceHandle.Pointer; + Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); + } + + [Benchmark(Description = "Marshal.Copy()")] + public unsafe void MarshalCopy() + { + void* pinnedDestination = this.destHandle.Pointer; + Marshal.Copy(this.sourceArray, 0, (IntPtr)pinnedDestination, this.Count); + } + + [Benchmark(Description = "Span.CopyTo()")] + public void SpanCopyTo() + { + this.sourceMemory.Span.CopyTo(this.destMemory.Span); + } + + [Benchmark(Description = "Unsafe.CopyBlock(ref)")] + public void UnsafeCopyBlockReferences() { - private byte[] destArray; - - private MemoryHandle destHandle; - - private Memory destMemory; - - private byte[] sourceArray; - - private MemoryHandle sourceHandle; - - private Memory sourceMemory; - - [Params(10, 50, 100, 1000, 10000)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.sourceArray = new byte[this.Count]; - this.sourceMemory = new Memory(this.sourceArray); - this.sourceHandle = this.sourceMemory.Pin(); - - this.destArray = new byte[this.Count]; - this.destMemory = new Memory(this.destArray); - this.destHandle = this.destMemory.Pin(); - } - - [GlobalCleanup] - public void Cleanup() - { - this.sourceHandle.Dispose(); - this.destHandle.Dispose(); - } - - [Benchmark(Baseline = true, Description = "Array.Copy()")] - public void ArrayCopy() - { - Array.Copy(this.sourceArray, this.destArray, this.Count); - } - - [Benchmark(Description = "Buffer.BlockCopy()")] - public void BufferBlockCopy() - { - Buffer.BlockCopy(this.sourceArray, 0, this.destArray, 0, this.Count); - } - - [Benchmark(Description = "Buffer.MemoryCopy()")] - public unsafe void BufferMemoryCopy() - { - void* pinnedDestination = this.destHandle.Pointer; - void* pinnedSource = this.sourceHandle.Pointer; - Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); - } - - [Benchmark(Description = "Marshal.Copy()")] - public unsafe void MarshalCopy() - { - void* pinnedDestination = this.destHandle.Pointer; - Marshal.Copy(this.sourceArray, 0, (IntPtr)pinnedDestination, this.Count); - } - - [Benchmark(Description = "Span.CopyTo()")] - public void SpanCopyTo() - { - this.sourceMemory.Span.CopyTo(this.destMemory.Span); - } - - [Benchmark(Description = "Unsafe.CopyBlock(ref)")] - public void UnsafeCopyBlockReferences() - { - Unsafe.CopyBlock(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); - } - - [Benchmark(Description = "Unsafe.CopyBlock(ptr)")] - public unsafe void UnsafeCopyBlockPointers() - { - void* pinnedDestination = this.destHandle.Pointer; - void* pinnedSource = this.sourceHandle.Pointer; - Unsafe.CopyBlock(pinnedDestination, pinnedSource, (uint)this.Count); - } - - [Benchmark(Description = "Unsafe.CopyBlockUnaligned(ref)")] - public void UnsafeCopyBlockUnalignedReferences() - { - Unsafe.CopyBlockUnaligned(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); - } - - [Benchmark(Description = "Unsafe.CopyBlockUnaligned(ptr)")] - public unsafe void UnsafeCopyBlockUnalignedPointers() - { - void* pinnedDestination = this.destHandle.Pointer; - void* pinnedSource = this.sourceHandle.Pointer; - Unsafe.CopyBlockUnaligned(pinnedDestination, pinnedSource, (uint)this.Count); - } - - // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) - // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC - // .NET Core SDK=2.2.202 - // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0 - // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // - // IterationCount=3 LaunchCount=1 WarmupCount=3 - // - // | Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |------------------------------- |----- |-------- |------ |-----------:|-----------:|----------:|------:|--------:|------:|------:|------:|----------:| - // | Array.Copy() | Clr | Clr | 10 | 23.636 ns | 2.5299 ns | 0.1387 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Clr | Clr | 10 | 11.420 ns | 2.3341 ns | 0.1279 ns | 0.48 | 0.01 | - | - | - | - | - // | Buffer.MemoryCopy() | Clr | Clr | 10 | 2.861 ns | 0.5059 ns | 0.0277 ns | 0.12 | 0.00 | - | - | - | - | - // | Marshal.Copy() | Clr | Clr | 10 | 14.870 ns | 2.4541 ns | 0.1345 ns | 0.63 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Clr | Clr | 10 | 31.906 ns | 1.2213 ns | 0.0669 ns | 1.35 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Clr | Clr | 10 | 3.513 ns | 0.7392 ns | 0.0405 ns | 0.15 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Clr | Clr | 10 | 3.053 ns | 0.2010 ns | 0.0110 ns | 0.13 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10 | 3.497 ns | 0.4911 ns | 0.0269 ns | 0.15 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10 | 3.109 ns | 0.5958 ns | 0.0327 ns | 0.13 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Core | Core | 10 | 19.709 ns | 2.1867 ns | 0.1199 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Core | Core | 10 | 7.377 ns | 1.1582 ns | 0.0635 ns | 0.37 | 0.01 | - | - | - | - | - // | Buffer.MemoryCopy() | Core | Core | 10 | 2.581 ns | 1.1607 ns | 0.0636 ns | 0.13 | 0.00 | - | - | - | - | - // | Marshal.Copy() | Core | Core | 10 | 15.197 ns | 2.8446 ns | 0.1559 ns | 0.77 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Core | Core | 10 | 25.394 ns | 0.9782 ns | 0.0536 ns | 1.29 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Core | Core | 10 | 2.254 ns | 0.1590 ns | 0.0087 ns | 0.11 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Core | Core | 10 | 1.878 ns | 0.1035 ns | 0.0057 ns | 0.10 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10 | 2.263 ns | 0.1383 ns | 0.0076 ns | 0.11 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10 | 1.877 ns | 0.0602 ns | 0.0033 ns | 0.10 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Clr | Clr | 50 | 35.068 ns | 5.9137 ns | 0.3242 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Clr | Clr | 50 | 23.299 ns | 2.3797 ns | 0.1304 ns | 0.66 | 0.01 | - | - | - | - | - // | Buffer.MemoryCopy() | Clr | Clr | 50 | 3.598 ns | 4.8536 ns | 0.2660 ns | 0.10 | 0.01 | - | - | - | - | - // | Marshal.Copy() | Clr | Clr | 50 | 27.720 ns | 4.6602 ns | 0.2554 ns | 0.79 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Clr | Clr | 50 | 35.673 ns | 16.2972 ns | 0.8933 ns | 1.02 | 0.03 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Clr | Clr | 50 | 5.534 ns | 2.8119 ns | 0.1541 ns | 0.16 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Clr | Clr | 50 | 4.511 ns | 0.9555 ns | 0.0524 ns | 0.13 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 50 | 5.613 ns | 1.6679 ns | 0.0914 ns | 0.16 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 50 | 4.884 ns | 7.3153 ns | 0.4010 ns | 0.14 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Core | Core | 50 | 20.232 ns | 1.5720 ns | 0.0862 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Core | Core | 50 | 8.142 ns | 0.7860 ns | 0.0431 ns | 0.40 | 0.00 | - | - | - | - | - // | Buffer.MemoryCopy() | Core | Core | 50 | 2.962 ns | 0.0611 ns | 0.0033 ns | 0.15 | 0.00 | - | - | - | - | - // | Marshal.Copy() | Core | Core | 50 | 16.802 ns | 2.9686 ns | 0.1627 ns | 0.83 | 0.00 | - | - | - | - | - // | Span.CopyTo() | Core | Core | 50 | 26.571 ns | 0.9228 ns | 0.0506 ns | 1.31 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Core | Core | 50 | 2.219 ns | 0.7191 ns | 0.0394 ns | 0.11 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Core | Core | 50 | 1.751 ns | 0.1884 ns | 0.0103 ns | 0.09 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 50 | 2.177 ns | 0.4489 ns | 0.0246 ns | 0.11 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 50 | 1.806 ns | 0.1063 ns | 0.0058 ns | 0.09 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Clr | Clr | 100 | 39.158 ns | 4.3068 ns | 0.2361 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Clr | Clr | 100 | 27.623 ns | 0.4611 ns | 0.0253 ns | 0.71 | 0.00 | - | - | - | - | - // | Buffer.MemoryCopy() | Clr | Clr | 100 | 5.018 ns | 0.5689 ns | 0.0312 ns | 0.13 | 0.00 | - | - | - | - | - // | Marshal.Copy() | Clr | Clr | 100 | 33.527 ns | 1.9019 ns | 0.1042 ns | 0.86 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Clr | Clr | 100 | 35.604 ns | 2.7039 ns | 0.1482 ns | 0.91 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Clr | Clr | 100 | 7.853 ns | 0.4925 ns | 0.0270 ns | 0.20 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Clr | Clr | 100 | 7.406 ns | 1.9733 ns | 0.1082 ns | 0.19 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 100 | 7.822 ns | 0.6837 ns | 0.0375 ns | 0.20 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 100 | 7.392 ns | 1.2832 ns | 0.0703 ns | 0.19 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Core | Core | 100 | 22.909 ns | 2.9754 ns | 0.1631 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Core | Core | 100 | 10.687 ns | 1.1262 ns | 0.0617 ns | 0.47 | 0.00 | - | - | - | - | - // | Buffer.MemoryCopy() | Core | Core | 100 | 4.063 ns | 0.1607 ns | 0.0088 ns | 0.18 | 0.00 | - | - | - | - | - // | Marshal.Copy() | Core | Core | 100 | 18.067 ns | 4.0557 ns | 0.2223 ns | 0.79 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Core | Core | 100 | 28.352 ns | 1.2762 ns | 0.0700 ns | 1.24 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Core | Core | 100 | 4.130 ns | 0.2013 ns | 0.0110 ns | 0.18 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Core | Core | 100 | 4.096 ns | 0.2460 ns | 0.0135 ns | 0.18 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 100 | 4.160 ns | 0.3174 ns | 0.0174 ns | 0.18 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 100 | 3.480 ns | 1.1683 ns | 0.0640 ns | 0.15 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Clr | Clr | 1000 | 49.059 ns | 2.0729 ns | 0.1136 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Clr | Clr | 1000 | 38.270 ns | 23.6970 ns | 1.2989 ns | 0.78 | 0.03 | - | - | - | - | - // | Buffer.MemoryCopy() | Clr | Clr | 1000 | 27.599 ns | 6.8328 ns | 0.3745 ns | 0.56 | 0.01 | - | - | - | - | - // | Marshal.Copy() | Clr | Clr | 1000 | 42.752 ns | 5.1357 ns | 0.2815 ns | 0.87 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Clr | Clr | 1000 | 69.983 ns | 2.1860 ns | 0.1198 ns | 1.43 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Clr | Clr | 1000 | 44.822 ns | 0.1625 ns | 0.0089 ns | 0.91 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Clr | Clr | 1000 | 45.072 ns | 1.4053 ns | 0.0770 ns | 0.92 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 1000 | 45.306 ns | 5.2646 ns | 0.2886 ns | 0.92 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 1000 | 44.813 ns | 0.9117 ns | 0.0500 ns | 0.91 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Core | Core | 1000 | 51.907 ns | 3.1827 ns | 0.1745 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Core | Core | 1000 | 40.700 ns | 3.1488 ns | 0.1726 ns | 0.78 | 0.00 | - | - | - | - | - // | Buffer.MemoryCopy() | Core | Core | 1000 | 23.711 ns | 1.3004 ns | 0.0713 ns | 0.46 | 0.00 | - | - | - | - | - // | Marshal.Copy() | Core | Core | 1000 | 42.586 ns | 2.5390 ns | 0.1392 ns | 0.82 | 0.00 | - | - | - | - | - // | Span.CopyTo() | Core | Core | 1000 | 44.109 ns | 4.5604 ns | 0.2500 ns | 0.85 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Core | Core | 1000 | 33.926 ns | 5.1633 ns | 0.2830 ns | 0.65 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Core | Core | 1000 | 33.267 ns | 0.2708 ns | 0.0148 ns | 0.64 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 1000 | 34.018 ns | 2.3238 ns | 0.1274 ns | 0.66 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 1000 | 33.667 ns | 2.1983 ns | 0.1205 ns | 0.65 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Clr | Clr | 10000 | 153.429 ns | 6.1735 ns | 0.3384 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Clr | Clr | 10000 | 201.373 ns | 4.3670 ns | 0.2394 ns | 1.31 | 0.00 | - | - | - | - | - // | Buffer.MemoryCopy() | Clr | Clr | 10000 | 211.768 ns | 71.3510 ns | 3.9110 ns | 1.38 | 0.02 | - | - | - | - | - // | Marshal.Copy() | Clr | Clr | 10000 | 215.299 ns | 17.2677 ns | 0.9465 ns | 1.40 | 0.01 | - | - | - | - | - // | Span.CopyTo() | Clr | Clr | 10000 | 486.325 ns | 32.4445 ns | 1.7784 ns | 3.17 | 0.01 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Clr | Clr | 10000 | 452.314 ns | 33.0593 ns | 1.8121 ns | 2.95 | 0.02 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Clr | Clr | 10000 | 455.600 ns | 56.7534 ns | 3.1108 ns | 2.97 | 0.02 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10000 | 452.279 ns | 8.6457 ns | 0.4739 ns | 2.95 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10000 | 453.146 ns | 12.3776 ns | 0.6785 ns | 2.95 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | | - // | Array.Copy() | Core | Core | 10000 | 204.508 ns | 3.1652 ns | 0.1735 ns | 1.00 | 0.00 | - | - | - | - | - // | Buffer.BlockCopy() | Core | Core | 10000 | 193.345 ns | 1.3742 ns | 0.0753 ns | 0.95 | 0.00 | - | - | - | - | - // | Buffer.MemoryCopy() | Core | Core | 10000 | 196.978 ns | 18.3279 ns | 1.0046 ns | 0.96 | 0.01 | - | - | - | - | - // | Marshal.Copy() | Core | Core | 10000 | 206.878 ns | 6.9938 ns | 0.3834 ns | 1.01 | 0.00 | - | - | - | - | - // | Span.CopyTo() | Core | Core | 10000 | 215.733 ns | 15.4824 ns | 0.8486 ns | 1.05 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ref) | Core | Core | 10000 | 186.894 ns | 8.7617 ns | 0.4803 ns | 0.91 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlock(ptr) | Core | Core | 10000 | 186.662 ns | 10.6059 ns | 0.5813 ns | 0.91 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10000 | 187.489 ns | 13.1527 ns | 0.7209 ns | 0.92 | 0.00 | - | - | - | - | - // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10000 | 186.586 ns | 4.6274 ns | 0.2536 ns | 0.91 | 0.00 | - | - | - | - | + Unsafe.CopyBlock(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); } + + [Benchmark(Description = "Unsafe.CopyBlock(ptr)")] + public unsafe void UnsafeCopyBlockPointers() + { + void* pinnedDestination = this.destHandle.Pointer; + void* pinnedSource = this.sourceHandle.Pointer; + Unsafe.CopyBlock(pinnedDestination, pinnedSource, (uint)this.Count); + } + + [Benchmark(Description = "Unsafe.CopyBlockUnaligned(ref)")] + public void UnsafeCopyBlockUnalignedReferences() + { + Unsafe.CopyBlockUnaligned(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); + } + + [Benchmark(Description = "Unsafe.CopyBlockUnaligned(ptr)")] + public unsafe void UnsafeCopyBlockUnalignedPointers() + { + void* pinnedDestination = this.destHandle.Pointer; + void* pinnedSource = this.sourceHandle.Pointer; + Unsafe.CopyBlockUnaligned(pinnedDestination, pinnedSource, (uint)this.Count); + } + + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC + // .NET Core SDK=2.2.202 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // | Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + // |------------------------------- |----- |-------- |------ |-----------:|-----------:|----------:|------:|--------:|------:|------:|------:|----------:| + // | Array.Copy() | Clr | Clr | 10 | 23.636 ns | 2.5299 ns | 0.1387 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 10 | 11.420 ns | 2.3341 ns | 0.1279 ns | 0.48 | 0.01 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 10 | 2.861 ns | 0.5059 ns | 0.0277 ns | 0.12 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 10 | 14.870 ns | 2.4541 ns | 0.1345 ns | 0.63 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 10 | 31.906 ns | 1.2213 ns | 0.0669 ns | 1.35 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 10 | 3.513 ns | 0.7392 ns | 0.0405 ns | 0.15 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 10 | 3.053 ns | 0.2010 ns | 0.0110 ns | 0.13 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10 | 3.497 ns | 0.4911 ns | 0.0269 ns | 0.15 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10 | 3.109 ns | 0.5958 ns | 0.0327 ns | 0.13 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 10 | 19.709 ns | 2.1867 ns | 0.1199 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 10 | 7.377 ns | 1.1582 ns | 0.0635 ns | 0.37 | 0.01 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 10 | 2.581 ns | 1.1607 ns | 0.0636 ns | 0.13 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 10 | 15.197 ns | 2.8446 ns | 0.1559 ns | 0.77 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 10 | 25.394 ns | 0.9782 ns | 0.0536 ns | 1.29 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 10 | 2.254 ns | 0.1590 ns | 0.0087 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 10 | 1.878 ns | 0.1035 ns | 0.0057 ns | 0.10 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10 | 2.263 ns | 0.1383 ns | 0.0076 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10 | 1.877 ns | 0.0602 ns | 0.0033 ns | 0.10 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 50 | 35.068 ns | 5.9137 ns | 0.3242 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 50 | 23.299 ns | 2.3797 ns | 0.1304 ns | 0.66 | 0.01 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 50 | 3.598 ns | 4.8536 ns | 0.2660 ns | 0.10 | 0.01 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 50 | 27.720 ns | 4.6602 ns | 0.2554 ns | 0.79 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 50 | 35.673 ns | 16.2972 ns | 0.8933 ns | 1.02 | 0.03 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 50 | 5.534 ns | 2.8119 ns | 0.1541 ns | 0.16 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 50 | 4.511 ns | 0.9555 ns | 0.0524 ns | 0.13 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 50 | 5.613 ns | 1.6679 ns | 0.0914 ns | 0.16 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 50 | 4.884 ns | 7.3153 ns | 0.4010 ns | 0.14 | 0.01 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 50 | 20.232 ns | 1.5720 ns | 0.0862 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 50 | 8.142 ns | 0.7860 ns | 0.0431 ns | 0.40 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 50 | 2.962 ns | 0.0611 ns | 0.0033 ns | 0.15 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 50 | 16.802 ns | 2.9686 ns | 0.1627 ns | 0.83 | 0.00 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 50 | 26.571 ns | 0.9228 ns | 0.0506 ns | 1.31 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 50 | 2.219 ns | 0.7191 ns | 0.0394 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 50 | 1.751 ns | 0.1884 ns | 0.0103 ns | 0.09 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 50 | 2.177 ns | 0.4489 ns | 0.0246 ns | 0.11 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 50 | 1.806 ns | 0.1063 ns | 0.0058 ns | 0.09 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 100 | 39.158 ns | 4.3068 ns | 0.2361 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 100 | 27.623 ns | 0.4611 ns | 0.0253 ns | 0.71 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 100 | 5.018 ns | 0.5689 ns | 0.0312 ns | 0.13 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 100 | 33.527 ns | 1.9019 ns | 0.1042 ns | 0.86 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 100 | 35.604 ns | 2.7039 ns | 0.1482 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 100 | 7.853 ns | 0.4925 ns | 0.0270 ns | 0.20 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 100 | 7.406 ns | 1.9733 ns | 0.1082 ns | 0.19 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 100 | 7.822 ns | 0.6837 ns | 0.0375 ns | 0.20 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 100 | 7.392 ns | 1.2832 ns | 0.0703 ns | 0.19 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 100 | 22.909 ns | 2.9754 ns | 0.1631 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 100 | 10.687 ns | 1.1262 ns | 0.0617 ns | 0.47 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 100 | 4.063 ns | 0.1607 ns | 0.0088 ns | 0.18 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 100 | 18.067 ns | 4.0557 ns | 0.2223 ns | 0.79 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 100 | 28.352 ns | 1.2762 ns | 0.0700 ns | 1.24 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 100 | 4.130 ns | 0.2013 ns | 0.0110 ns | 0.18 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 100 | 4.096 ns | 0.2460 ns | 0.0135 ns | 0.18 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 100 | 4.160 ns | 0.3174 ns | 0.0174 ns | 0.18 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 100 | 3.480 ns | 1.1683 ns | 0.0640 ns | 0.15 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 1000 | 49.059 ns | 2.0729 ns | 0.1136 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 1000 | 38.270 ns | 23.6970 ns | 1.2989 ns | 0.78 | 0.03 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 1000 | 27.599 ns | 6.8328 ns | 0.3745 ns | 0.56 | 0.01 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 1000 | 42.752 ns | 5.1357 ns | 0.2815 ns | 0.87 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 1000 | 69.983 ns | 2.1860 ns | 0.1198 ns | 1.43 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 1000 | 44.822 ns | 0.1625 ns | 0.0089 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 1000 | 45.072 ns | 1.4053 ns | 0.0770 ns | 0.92 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 1000 | 45.306 ns | 5.2646 ns | 0.2886 ns | 0.92 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 1000 | 44.813 ns | 0.9117 ns | 0.0500 ns | 0.91 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 1000 | 51.907 ns | 3.1827 ns | 0.1745 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 1000 | 40.700 ns | 3.1488 ns | 0.1726 ns | 0.78 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 1000 | 23.711 ns | 1.3004 ns | 0.0713 ns | 0.46 | 0.00 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 1000 | 42.586 ns | 2.5390 ns | 0.1392 ns | 0.82 | 0.00 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 1000 | 44.109 ns | 4.5604 ns | 0.2500 ns | 0.85 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 1000 | 33.926 ns | 5.1633 ns | 0.2830 ns | 0.65 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 1000 | 33.267 ns | 0.2708 ns | 0.0148 ns | 0.64 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 1000 | 34.018 ns | 2.3238 ns | 0.1274 ns | 0.66 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 1000 | 33.667 ns | 2.1983 ns | 0.1205 ns | 0.65 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Clr | Clr | 10000 | 153.429 ns | 6.1735 ns | 0.3384 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Clr | Clr | 10000 | 201.373 ns | 4.3670 ns | 0.2394 ns | 1.31 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Clr | Clr | 10000 | 211.768 ns | 71.3510 ns | 3.9110 ns | 1.38 | 0.02 | - | - | - | - | + // | Marshal.Copy() | Clr | Clr | 10000 | 215.299 ns | 17.2677 ns | 0.9465 ns | 1.40 | 0.01 | - | - | - | - | + // | Span.CopyTo() | Clr | Clr | 10000 | 486.325 ns | 32.4445 ns | 1.7784 ns | 3.17 | 0.01 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Clr | Clr | 10000 | 452.314 ns | 33.0593 ns | 1.8121 ns | 2.95 | 0.02 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Clr | Clr | 10000 | 455.600 ns | 56.7534 ns | 3.1108 ns | 2.97 | 0.02 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10000 | 452.279 ns | 8.6457 ns | 0.4739 ns | 2.95 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10000 | 453.146 ns | 12.3776 ns | 0.6785 ns | 2.95 | 0.00 | - | - | - | - | + // | | | | | | | | | | | | | | + // | Array.Copy() | Core | Core | 10000 | 204.508 ns | 3.1652 ns | 0.1735 ns | 1.00 | 0.00 | - | - | - | - | + // | Buffer.BlockCopy() | Core | Core | 10000 | 193.345 ns | 1.3742 ns | 0.0753 ns | 0.95 | 0.00 | - | - | - | - | + // | Buffer.MemoryCopy() | Core | Core | 10000 | 196.978 ns | 18.3279 ns | 1.0046 ns | 0.96 | 0.01 | - | - | - | - | + // | Marshal.Copy() | Core | Core | 10000 | 206.878 ns | 6.9938 ns | 0.3834 ns | 1.01 | 0.00 | - | - | - | - | + // | Span.CopyTo() | Core | Core | 10000 | 215.733 ns | 15.4824 ns | 0.8486 ns | 1.05 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ref) | Core | Core | 10000 | 186.894 ns | 8.7617 ns | 0.4803 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlock(ptr) | Core | Core | 10000 | 186.662 ns | 10.6059 ns | 0.5813 ns | 0.91 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10000 | 187.489 ns | 13.1527 ns | 0.7209 ns | 0.92 | 0.00 | - | - | - | - | + // | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10000 | 186.586 ns | 4.6274 ns | 0.2536 ns | 0.91 | 0.00 | - | - | - | - | } diff --git a/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs index c64e5ed86e..fdc9e26b60 100644 --- a/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs +++ b/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs @@ -1,72 +1,70 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Compression.Zlib; using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; -namespace SixLabors.ImageSharp.Benchmarks.General -{ - [Config(typeof(Config.ShortMultiFramework))] - public class Crc32Benchmark - { - private byte[] data; - private readonly SharpCrc32 crc = new SharpCrc32(); +namespace SixLabors.ImageSharp.Benchmarks.General; - [Params(1024, 2048, 4096)] - public int Count { get; set; } +[Config(typeof(Config.ShortMultiFramework))] +public class Crc32Benchmark +{ + private byte[] data; + private readonly SharpCrc32 crc = new SharpCrc32(); - [GlobalSetup] - public void SetUp() - { - this.data = new byte[this.Count]; - new Random(1).NextBytes(this.data); - } + [Params(1024, 2048, 4096)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public long SharpZipLibCalculate() - { - this.crc.Reset(); - this.crc.Update(this.data); - return this.crc.Value; - } + [GlobalSetup] + public void SetUp() + { + this.data = new byte[this.Count]; + new Random(1).NextBytes(this.data); + } - [Benchmark] - public long SixLaborsCalculate() - { - return Crc32.Calculate(this.data); - } + [Benchmark(Baseline = true)] + public long SharpZipLibCalculate() + { + this.crc.Reset(); + this.crc.Update(this.data); + return this.crc.Value; } - // ########## 17/05/2020 ########## - // - // | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |--------------------- |-------------- |------ |-------------:|-------------:|-----------:|------:|--------:|------:|------:|------:|----------:| - // | SharpZipLibCalculate | .NET 4.7.2 | 1024 | 2,797.77 ns | 278.697 ns | 15.276 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET 4.7.2 | 1024 | 2,275.56 ns | 216.100 ns | 11.845 ns | 0.81 | 0.01 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 2.1 | 1024 | 2,923.43 ns | 2,656.882 ns | 145.633 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 2.1 | 1024 | 2,257.79 ns | 75.081 ns | 4.115 ns | 0.77 | 0.04 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 3.1 | 1024 | 2,764.14 ns | 86.281 ns | 4.729 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 3.1 | 1024 | 49.32 ns | 1.813 ns | 0.099 ns | 0.02 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET 4.7.2 | 2048 | 5,603.71 ns | 427.240 ns | 23.418 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET 4.7.2 | 2048 | 4,525.02 ns | 33.931 ns | 1.860 ns | 0.81 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 2.1 | 2048 | 5,563.32 ns | 49.337 ns | 2.704 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 2.1 | 2048 | 4,519.61 ns | 29.837 ns | 1.635 ns | 0.81 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 3.1 | 2048 | 5,543.37 ns | 518.551 ns | 28.424 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 3.1 | 2048 | 89.07 ns | 3.312 ns | 0.182 ns | 0.02 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET 4.7.2 | 4096 | 11,396.95 ns | 373.450 ns | 20.470 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET 4.7.2 | 4096 | 9,070.35 ns | 271.083 ns | 14.859 ns | 0.80 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 2.1 | 4096 | 11,127.81 ns | 239.177 ns | 13.110 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 2.1 | 4096 | 9,050.46 ns | 230.916 ns | 12.657 ns | 0.81 | 0.00 | - | - | - | - | - // | | | | | | | | | | | | | - // | SharpZipLibCalculate | .NET Core 3.1 | 4096 | 11,098.62 ns | 687.978 ns | 37.710 ns | 1.00 | 0.00 | - | - | - | - | - // | SixLaborsCalculate | .NET Core 3.1 | 4096 | 168.11 ns | 3.633 ns | 0.199 ns | 0.02 | 0.00 | - | - | - | - | + [Benchmark] + public long SixLaborsCalculate() + { + return Crc32.Calculate(this.data); + } } + +// ########## 17/05/2020 ########## +// +// | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------------------- |-------------- |------ |-------------:|-------------:|-----------:|------:|--------:|------:|------:|------:|----------:| +// | SharpZipLibCalculate | .NET 4.7.2 | 1024 | 2,797.77 ns | 278.697 ns | 15.276 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET 4.7.2 | 1024 | 2,275.56 ns | 216.100 ns | 11.845 ns | 0.81 | 0.01 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 2.1 | 1024 | 2,923.43 ns | 2,656.882 ns | 145.633 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 2.1 | 1024 | 2,257.79 ns | 75.081 ns | 4.115 ns | 0.77 | 0.04 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 3.1 | 1024 | 2,764.14 ns | 86.281 ns | 4.729 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 3.1 | 1024 | 49.32 ns | 1.813 ns | 0.099 ns | 0.02 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET 4.7.2 | 2048 | 5,603.71 ns | 427.240 ns | 23.418 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET 4.7.2 | 2048 | 4,525.02 ns | 33.931 ns | 1.860 ns | 0.81 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 2.1 | 2048 | 5,563.32 ns | 49.337 ns | 2.704 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 2.1 | 2048 | 4,519.61 ns | 29.837 ns | 1.635 ns | 0.81 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 3.1 | 2048 | 5,543.37 ns | 518.551 ns | 28.424 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 3.1 | 2048 | 89.07 ns | 3.312 ns | 0.182 ns | 0.02 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET 4.7.2 | 4096 | 11,396.95 ns | 373.450 ns | 20.470 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET 4.7.2 | 4096 | 9,070.35 ns | 271.083 ns | 14.859 ns | 0.80 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 2.1 | 4096 | 11,127.81 ns | 239.177 ns | 13.110 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 2.1 | 4096 | 9,050.46 ns | 230.916 ns | 12.657 ns | 0.81 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | +// | SharpZipLibCalculate | .NET Core 3.1 | 4096 | 11,098.62 ns | 687.978 ns | 37.710 ns | 1.00 | 0.00 | - | - | - | - | +// | SixLaborsCalculate | .NET Core 3.1 | 4096 | 168.11 ns | 3.633 ns | 0.199 ns | 0.02 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs index b58b07fb03..50a628ee3c 100644 --- a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs @@ -5,24 +5,23 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +public class GetSetPixel { - public class GetSetPixel + [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")] + public System.Drawing.Color GetSetSystemDrawing() { - [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")] - public System.Drawing.Color GetSetSystemDrawing() - { - using var source = new Bitmap(400, 400); - source.SetPixel(200, 200, System.Drawing.Color.White); - return source.GetPixel(200, 200); - } + using var source = new Bitmap(400, 400); + source.SetPixel(200, 200, System.Drawing.Color.White); + return source.GetPixel(200, 200); + } - [Benchmark(Description = "ImageSharp GetSet pixel")] - public Rgba32 GetSetImageSharp() - { - using var image = new Image(400, 400); - image[200, 200] = Color.White; - return image[200, 200]; - } + [Benchmark(Description = "ImageSharp GetSet pixel")] + public Rgba32 GetSetImageSharp() + { + using var image = new Image(400, 400); + image[200, 200] = Color.White; + return image[200, 200]; } } diff --git a/tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs index 4c3e2e6838..a193e41bd3 100644 --- a/tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs +++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs @@ -1,279 +1,276 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; -using System.IO; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Benchmarks.IO +namespace SixLabors.ImageSharp.Benchmarks.IO; + +/// +/// A readonly stream wrapper that add a secondary level buffer in addition to native stream +/// buffered reading to reduce the overhead of small incremental reads. +/// +internal sealed unsafe class BufferedReadStreamWrapper : IDisposable { /// - /// A readonly stream wrapper that add a secondary level buffer in addition to native stream - /// buffered reading to reduce the overhead of small incremental reads. + /// The length, in bytes, of the underlying buffer. /// - internal sealed unsafe class BufferedReadStreamWrapper : IDisposable - { - /// - /// The length, in bytes, of the underlying buffer. - /// - public const int BufferLength = 8192; - - private const int MaxBufferIndex = BufferLength - 1; - - private readonly Stream stream; + public const int BufferLength = 8192; - private readonly byte[] readBuffer; + private const int MaxBufferIndex = BufferLength - 1; - private MemoryHandle readBufferHandle; + private readonly Stream stream; - private readonly byte* pinnedReadBuffer; + private readonly byte[] readBuffer; - // Index within our buffer, not reader position. - private int readBufferIndex; + private MemoryHandle readBufferHandle; - // Matches what the stream position would be without buffering - private long readerPosition; + private readonly byte* pinnedReadBuffer; - private bool isDisposed; + // Index within our buffer, not reader position. + private int readBufferIndex; - /// - /// Initializes a new instance of the class. - /// - /// The input stream. - public BufferedReadStreamWrapper(Stream stream) - { - Guard.IsTrue(stream.CanRead, nameof(stream), "Stream must be readable."); - Guard.IsTrue(stream.CanSeek, nameof(stream), "Stream must be seekable."); - - // Ensure all underlying buffers have been flushed before we attempt to read the stream. - // User streams may have opted to throw from Flush if CanWrite is false - // (although the abstract Stream does not do so). - if (stream.CanWrite) - { - stream.Flush(); - } + // Matches what the stream position would be without buffering + private long readerPosition; - this.stream = stream; - this.Position = (int)stream.Position; - this.Length = stream.Length; + private bool isDisposed; - this.readBuffer = ArrayPool.Shared.Rent(BufferLength); - this.readBufferHandle = new Memory(this.readBuffer).Pin(); - this.pinnedReadBuffer = (byte*)this.readBufferHandle.Pointer; + /// + /// Initializes a new instance of the class. + /// + /// The input stream. + public BufferedReadStreamWrapper(Stream stream) + { + Guard.IsTrue(stream.CanRead, nameof(stream), "Stream must be readable."); + Guard.IsTrue(stream.CanSeek, nameof(stream), "Stream must be seekable."); - // This triggers a full read on first attempt. - this.readBufferIndex = BufferLength; + // Ensure all underlying buffers have been flushed before we attempt to read the stream. + // User streams may have opted to throw from Flush if CanWrite is false + // (although the abstract Stream does not do so). + if (stream.CanWrite) + { + stream.Flush(); } - /// - /// Gets the length, in bytes, of the stream. - /// - public long Length { get; } + this.stream = stream; + this.Position = (int)stream.Position; + this.Length = stream.Length; - /// - /// Gets or sets the current position within the stream. - /// - public long Position - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.readerPosition; + this.readBuffer = ArrayPool.Shared.Rent(BufferLength); + this.readBufferHandle = new Memory(this.readBuffer).Pin(); + this.pinnedReadBuffer = (byte*)this.readBufferHandle.Pointer; - [MethodImpl(MethodImplOptions.NoInlining)] - set - { - // Only reset readBufferIndex if we are out of bounds of our working buffer - // otherwise we should simply move the value by the diff. - if (this.IsInReadBuffer(value, out long index)) - { - this.readBufferIndex = (int)index; - this.readerPosition = value; - } - else - { - // Base stream seek will throw for us if invalid. - this.stream.Seek(value, SeekOrigin.Begin); - this.readerPosition = value; - this.readBufferIndex = BufferLength; - } - } - } + // This triggers a full read on first attempt. + this.readBufferIndex = BufferLength; + } + /// + /// Gets the length, in bytes, of the stream. + /// + public long Length { get; } + + /// + /// Gets or sets the current position within the stream. + /// + public long Position + { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReadByte() + get => this.readerPosition; + + [MethodImpl(MethodImplOptions.NoInlining)] + set { - if (this.readerPosition >= this.Length) + // Only reset readBufferIndex if we are out of bounds of our working buffer + // otherwise we should simply move the value by the diff. + if (this.IsInReadBuffer(value, out long index)) { - return -1; + this.readBufferIndex = (int)index; + this.readerPosition = value; } - - // Our buffer has been read. - // We need to refill and start again. - if (this.readBufferIndex > MaxBufferIndex) + else { - this.FillReadBuffer(); + // Base stream seek will throw for us if invalid. + this.stream.Seek(value, SeekOrigin.Begin); + this.readerPosition = value; + this.readBufferIndex = BufferLength; } + } + } - this.readerPosition++; - return this.pinnedReadBuffer[this.readBufferIndex++]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadByte() + { + if (this.readerPosition >= this.Length) + { + return -1; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(byte[] buffer, int offset, int count) + // Our buffer has been read. + // We need to refill and start again. + if (this.readBufferIndex > MaxBufferIndex) { - // Too big for our buffer. Read directly from the stream. - if (count > BufferLength) - { - return this.ReadToBufferDirectSlow(buffer, offset, count); - } + this.FillReadBuffer(); + } - // Too big for remaining buffer but less than entire buffer length - // Copy to buffer then read from there. - if (count + this.readBufferIndex > BufferLength) - { - return this.ReadToBufferViaCopySlow(buffer, offset, count); - } + this.readerPosition++; + return this.pinnedReadBuffer[this.readBufferIndex++]; + } - return this.ReadToBufferViaCopyFast(buffer, offset, count); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Read(byte[] buffer, int offset, int count) + { + // Too big for our buffer. Read directly from the stream. + if (count > BufferLength) + { + return this.ReadToBufferDirectSlow(buffer, offset, count); } - public void Flush() + // Too big for remaining buffer but less than entire buffer length + // Copy to buffer then read from there. + if (count + this.readBufferIndex > BufferLength) { - // Reset the stream position to match reader position. - if (this.readerPosition != this.stream.Position) - { - this.stream.Seek(this.readerPosition, SeekOrigin.Begin); - this.readerPosition = (int)this.stream.Position; - } - - // Reset to trigger full read on next attempt. - this.readBufferIndex = BufferLength; + return this.ReadToBufferViaCopySlow(buffer, offset, count); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long Seek(long offset, SeekOrigin origin) + return this.ReadToBufferViaCopyFast(buffer, offset, count); + } + + public void Flush() + { + // Reset the stream position to match reader position. + if (this.readerPosition != this.stream.Position) { - switch (origin) - { - case SeekOrigin.Begin: - this.Position = offset; - break; + this.stream.Seek(this.readerPosition, SeekOrigin.Begin); + this.readerPosition = (int)this.stream.Position; + } - case SeekOrigin.Current: - this.Position += offset; - break; + // Reset to trigger full read on next attempt. + this.readBufferIndex = BufferLength; + } - case SeekOrigin.End: - this.Position = this.Length - offset; - break; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + this.Position = offset; + break; - return this.readerPosition; + case SeekOrigin.Current: + this.Position += offset; + break; + + case SeekOrigin.End: + this.Position = this.Length - offset; + break; } - /// - public void Dispose() + return this.readerPosition; + } + + /// + public void Dispose() + { + if (!this.isDisposed) { - if (!this.isDisposed) - { - this.isDisposed = true; - this.readBufferHandle.Dispose(); - ArrayPool.Shared.Return(this.readBuffer); - this.Flush(); - } + this.isDisposed = true; + this.readBufferHandle.Dispose(); + ArrayPool.Shared.Return(this.readBuffer); + this.Flush(); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool IsInReadBuffer(long newPosition, out long index) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsInReadBuffer(long newPosition, out long index) + { + index = newPosition - this.readerPosition + this.readBufferIndex; + return index > -1 && index < BufferLength; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void FillReadBuffer() + { + if (this.readerPosition != this.stream.Position) { - index = newPosition - this.readerPosition + this.readBufferIndex; - return index > -1 && index < BufferLength; + this.stream.Seek(this.readerPosition, SeekOrigin.Begin); } - [MethodImpl(MethodImplOptions.NoInlining)] - private void FillReadBuffer() - { - if (this.readerPosition != this.stream.Position) - { - this.stream.Seek(this.readerPosition, SeekOrigin.Begin); - } + this.stream.Read(this.readBuffer, 0, BufferLength); + this.readBufferIndex = 0; + } - this.stream.Read(this.readBuffer, 0, BufferLength); - this.readBufferIndex = 0; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ReadToBufferViaCopyFast(byte[] buffer, int offset, int count) + { + int n = this.GetCopyCount(count); + this.CopyBytes(buffer, offset, n); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ReadToBufferViaCopyFast(byte[] buffer, int offset, int count) - { - int n = this.GetCopyCount(count); - this.CopyBytes(buffer, offset, n); + this.readerPosition += n; + this.readBufferIndex += n; - this.readerPosition += n; - this.readBufferIndex += n; + return n; + } - return n; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count) + { + // Refill our buffer then copy. + this.FillReadBuffer(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count) - { - // Refill our buffer then copy. - this.FillReadBuffer(); + return this.ReadToBufferViaCopyFast(buffer, offset, count); + } - return this.ReadToBufferViaCopyFast(buffer, offset, count); + [MethodImpl(MethodImplOptions.NoInlining)] + private int ReadToBufferDirectSlow(byte[] buffer, int offset, int count) + { + // Read to target but don't copy to our read buffer. + if (this.readerPosition != this.stream.Position) + { + this.stream.Seek(this.readerPosition, SeekOrigin.Begin); } - [MethodImpl(MethodImplOptions.NoInlining)] - private int ReadToBufferDirectSlow(byte[] buffer, int offset, int count) - { - // Read to target but don't copy to our read buffer. - if (this.readerPosition != this.stream.Position) - { - this.stream.Seek(this.readerPosition, SeekOrigin.Begin); - } + int n = this.stream.Read(buffer, offset, count); + this.Position += n; - int n = this.stream.Read(buffer, offset, count); - this.Position += n; + return n; + } - return n; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetCopyCount(int count) + { + long n = this.Length - this.readerPosition; + if (n > count) + { + return count; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetCopyCount(int count) + if (n < 0) { - long n = this.Length - this.readerPosition; - if (n > count) - { - return count; - } - - if (n < 0) - { - return 0; - } - - return (int)n; + return 0; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyBytes(byte[] buffer, int offset, int count) + return (int)n; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CopyBytes(byte[] buffer, int offset, int count) + { + // Same as MemoryStream. + if (count < 9) { - // Same as MemoryStream. - if (count < 9) - { - int byteCount = count; - int read = this.readBufferIndex; - byte* pinned = this.pinnedReadBuffer; - - while (--byteCount > -1) - { - buffer[offset + byteCount] = pinned[read + byteCount]; - } - } - else + int byteCount = count; + int read = this.readBufferIndex; + byte* pinned = this.pinnedReadBuffer; + + while (--byteCount > -1) { - Buffer.BlockCopy(this.readBuffer, this.readBufferIndex, buffer, offset, count); + buffer[offset + byteCount] = pinned[read + byteCount]; } } + else + { + Buffer.BlockCopy(this.readBuffer, this.readBufferIndex, buffer, offset, count); + } } } diff --git a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs index bf91d0f1cb..2a926d1cd8 100644 --- a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs @@ -1,261 +1,258 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.IO; -namespace SixLabors.ImageSharp.Benchmarks.IO +namespace SixLabors.ImageSharp.Benchmarks.IO; + +[Config(typeof(Config.ShortMultiFramework))] +public class BufferedStreams { - [Config(typeof(Config.ShortMultiFramework))] - public class BufferedStreams + private readonly byte[] buffer = CreateTestBytes(); + private readonly byte[] chunk1 = new byte[2]; + private readonly byte[] chunk2 = new byte[2]; + + private MemoryStream stream1; + private MemoryStream stream2; + private MemoryStream stream3; + private MemoryStream stream4; + private MemoryStream stream5; + private MemoryStream stream6; + private ChunkedMemoryStream chunkedMemoryStream1; + private ChunkedMemoryStream chunkedMemoryStream2; + private BufferedReadStream bufferedStream1; + private BufferedReadStream bufferedStream2; + private BufferedReadStream bufferedStream3; + private BufferedReadStream bufferedStream4; + private BufferedReadStreamWrapper bufferedStreamWrap1; + private BufferedReadStreamWrapper bufferedStreamWrap2; + + [GlobalSetup] + public void CreateStreams() { - private readonly byte[] buffer = CreateTestBytes(); - private readonly byte[] chunk1 = new byte[2]; - private readonly byte[] chunk2 = new byte[2]; - - private MemoryStream stream1; - private MemoryStream stream2; - private MemoryStream stream3; - private MemoryStream stream4; - private MemoryStream stream5; - private MemoryStream stream6; - private ChunkedMemoryStream chunkedMemoryStream1; - private ChunkedMemoryStream chunkedMemoryStream2; - private BufferedReadStream bufferedStream1; - private BufferedReadStream bufferedStream2; - private BufferedReadStream bufferedStream3; - private BufferedReadStream bufferedStream4; - private BufferedReadStreamWrapper bufferedStreamWrap1; - private BufferedReadStreamWrapper bufferedStreamWrap2; - - [GlobalSetup] - public void CreateStreams() - { - this.stream1 = new MemoryStream(this.buffer); - this.stream2 = new MemoryStream(this.buffer); - this.stream3 = new MemoryStream(this.buffer); - this.stream4 = new MemoryStream(this.buffer); - this.stream5 = new MemoryStream(this.buffer); - this.stream6 = new MemoryStream(this.buffer); - this.stream6 = new MemoryStream(this.buffer); - - this.chunkedMemoryStream1 = new ChunkedMemoryStream(Configuration.Default.MemoryAllocator); - this.chunkedMemoryStream1.Write(this.buffer); - this.chunkedMemoryStream1.Position = 0; - - this.chunkedMemoryStream2 = new ChunkedMemoryStream(Configuration.Default.MemoryAllocator); - this.chunkedMemoryStream2.Write(this.buffer); - this.chunkedMemoryStream2.Position = 0; - - this.bufferedStream1 = new BufferedReadStream(Configuration.Default, this.stream3); - this.bufferedStream2 = new BufferedReadStream(Configuration.Default, this.stream4); - this.bufferedStream3 = new BufferedReadStream(Configuration.Default, this.chunkedMemoryStream1); - this.bufferedStream4 = new BufferedReadStream(Configuration.Default, this.chunkedMemoryStream2); - this.bufferedStreamWrap1 = new BufferedReadStreamWrapper(this.stream5); - this.bufferedStreamWrap2 = new BufferedReadStreamWrapper(this.stream6); - } - - [GlobalCleanup] - public void DestroyStreams() - { - this.bufferedStream1?.Dispose(); - this.bufferedStream2?.Dispose(); - this.bufferedStream3?.Dispose(); - this.bufferedStream4?.Dispose(); - this.bufferedStreamWrap1?.Dispose(); - this.bufferedStreamWrap2?.Dispose(); - this.chunkedMemoryStream1?.Dispose(); - this.chunkedMemoryStream2?.Dispose(); - this.stream1?.Dispose(); - this.stream2?.Dispose(); - this.stream3?.Dispose(); - this.stream4?.Dispose(); - this.stream5?.Dispose(); - this.stream6?.Dispose(); - } - - [Benchmark] - public int StandardStreamRead() - { - int r = 0; - Stream stream = this.stream1; - byte[] b = this.chunk1; + this.stream1 = new MemoryStream(this.buffer); + this.stream2 = new MemoryStream(this.buffer); + this.stream3 = new MemoryStream(this.buffer); + this.stream4 = new MemoryStream(this.buffer); + this.stream5 = new MemoryStream(this.buffer); + this.stream6 = new MemoryStream(this.buffer); + this.stream6 = new MemoryStream(this.buffer); + + this.chunkedMemoryStream1 = new ChunkedMemoryStream(Configuration.Default.MemoryAllocator); + this.chunkedMemoryStream1.Write(this.buffer); + this.chunkedMemoryStream1.Position = 0; + + this.chunkedMemoryStream2 = new ChunkedMemoryStream(Configuration.Default.MemoryAllocator); + this.chunkedMemoryStream2.Write(this.buffer); + this.chunkedMemoryStream2.Position = 0; + + this.bufferedStream1 = new BufferedReadStream(Configuration.Default, this.stream3); + this.bufferedStream2 = new BufferedReadStream(Configuration.Default, this.stream4); + this.bufferedStream3 = new BufferedReadStream(Configuration.Default, this.chunkedMemoryStream1); + this.bufferedStream4 = new BufferedReadStream(Configuration.Default, this.chunkedMemoryStream2); + this.bufferedStreamWrap1 = new BufferedReadStreamWrapper(this.stream5); + this.bufferedStreamWrap2 = new BufferedReadStreamWrapper(this.stream6); + } - for (int i = 0; i < stream.Length / 2; i++) - { - r += stream.Read(b, 0, 2); - } + [GlobalCleanup] + public void DestroyStreams() + { + this.bufferedStream1?.Dispose(); + this.bufferedStream2?.Dispose(); + this.bufferedStream3?.Dispose(); + this.bufferedStream4?.Dispose(); + this.bufferedStreamWrap1?.Dispose(); + this.bufferedStreamWrap2?.Dispose(); + this.chunkedMemoryStream1?.Dispose(); + this.chunkedMemoryStream2?.Dispose(); + this.stream1?.Dispose(); + this.stream2?.Dispose(); + this.stream3?.Dispose(); + this.stream4?.Dispose(); + this.stream5?.Dispose(); + this.stream6?.Dispose(); + } - return r; - } + [Benchmark] + public int StandardStreamRead() + { + int r = 0; + Stream stream = this.stream1; + byte[] b = this.chunk1; - [Benchmark] - public int BufferedReadStreamRead() + for (int i = 0; i < stream.Length / 2; i++) { - int r = 0; - BufferedReadStream reader = this.bufferedStream1; - byte[] b = this.chunk2; + r += stream.Read(b, 0, 2); + } - for (int i = 0; i < reader.Length / 2; i++) - { - r += reader.Read(b, 0, 2); - } + return r; + } - return r; - } + [Benchmark] + public int BufferedReadStreamRead() + { + int r = 0; + BufferedReadStream reader = this.bufferedStream1; + byte[] b = this.chunk2; - [Benchmark] - public int BufferedReadStreamChunkedRead() + for (int i = 0; i < reader.Length / 2; i++) { - int r = 0; - BufferedReadStream reader = this.bufferedStream3; - byte[] b = this.chunk2; + r += reader.Read(b, 0, 2); + } - for (int i = 0; i < reader.Length / 2; i++) - { - r += reader.Read(b, 0, 2); - } + return r; + } - return r; - } + [Benchmark] + public int BufferedReadStreamChunkedRead() + { + int r = 0; + BufferedReadStream reader = this.bufferedStream3; + byte[] b = this.chunk2; - [Benchmark] - public int BufferedReadStreamWrapRead() + for (int i = 0; i < reader.Length / 2; i++) { - int r = 0; - BufferedReadStreamWrapper reader = this.bufferedStreamWrap1; - byte[] b = this.chunk2; + r += reader.Read(b, 0, 2); + } - for (int i = 0; i < reader.Length / 2; i++) - { - r += reader.Read(b, 0, 2); - } + return r; + } - return r; - } + [Benchmark] + public int BufferedReadStreamWrapRead() + { + int r = 0; + BufferedReadStreamWrapper reader = this.bufferedStreamWrap1; + byte[] b = this.chunk2; - [Benchmark(Baseline = true)] - public int StandardStreamReadByte() + for (int i = 0; i < reader.Length / 2; i++) { - int r = 0; - Stream stream = this.stream2; + r += reader.Read(b, 0, 2); + } - for (int i = 0; i < stream.Length; i++) - { - r += stream.ReadByte(); - } + return r; + } - return r; - } + [Benchmark(Baseline = true)] + public int StandardStreamReadByte() + { + int r = 0; + Stream stream = this.stream2; - [Benchmark] - public int BufferedReadStreamReadByte() + for (int i = 0; i < stream.Length; i++) { - int r = 0; - BufferedReadStream reader = this.bufferedStream2; + r += stream.ReadByte(); + } - for (int i = 0; i < reader.Length; i++) - { - r += reader.ReadByte(); - } + return r; + } - return r; - } + [Benchmark] + public int BufferedReadStreamReadByte() + { + int r = 0; + BufferedReadStream reader = this.bufferedStream2; - [Benchmark] - public int BufferedReadStreamChunkedReadByte() + for (int i = 0; i < reader.Length; i++) { - int r = 0; - BufferedReadStream reader = this.bufferedStream4; + r += reader.ReadByte(); + } - for (int i = 0; i < reader.Length; i++) - { - r += reader.ReadByte(); - } + return r; + } - return r; - } + [Benchmark] + public int BufferedReadStreamChunkedReadByte() + { + int r = 0; + BufferedReadStream reader = this.bufferedStream4; - [Benchmark] - public int BufferedReadStreamWrapReadByte() + for (int i = 0; i < reader.Length; i++) { - int r = 0; - BufferedReadStreamWrapper reader = this.bufferedStreamWrap2; + r += reader.ReadByte(); + } - for (int i = 0; i < reader.Length; i++) - { - r += reader.ReadByte(); - } + return r; + } - return r; - } + [Benchmark] + public int BufferedReadStreamWrapReadByte() + { + int r = 0; + BufferedReadStreamWrapper reader = this.bufferedStreamWrap2; - [Benchmark] - public int ArrayReadByte() + for (int i = 0; i < reader.Length; i++) { - byte[] b = this.buffer; - int r = 0; - for (int i = 0; i < b.Length; i++) - { - r += b[i]; - } - - return r; + r += reader.ReadByte(); } - private static byte[] CreateTestBytes() - { - var buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; - var random = new Random(); - random.NextBytes(buffer); + return r; + } - return buffer; + [Benchmark] + public int ArrayReadByte() + { + byte[] b = this.buffer; + int r = 0; + for (int i = 0; i < b.Length; i++) + { + r += b[i]; } + + return r; } - /* - BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1) - Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - .NET Core SDK=3.1.401 - [Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT - Job-OKZLUV : .NET Framework 4.8 (4.8.4084.0), X64 RyuJIT - Job-CPYMXV : .NET Core 2.1.21 (CoreCLR 4.6.29130.01, CoreFX 4.6.29130.02), X64 RyuJIT - Job-BSGVGU : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT - - IterationCount=3 LaunchCount=1 WarmupCount=3 - - | Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - |---------------------------------- |----------- |-------------- |-----------:|----------:|----------:|------:|--------:|------:|------:|------:|----------:| - | StandardStreamRead | Job-OKZLUV | .NET 4.7.2 | 66.785 us | 15.768 us | 0.8643 us | 0.83 | 0.01 | - | - | - | - | - | BufferedReadStreamRead | Job-OKZLUV | .NET 4.7.2 | 97.389 us | 17.658 us | 0.9679 us | 1.21 | 0.01 | - | - | - | - | - | BufferedReadStreamChunkedRead | Job-OKZLUV | .NET 4.7.2 | 96.006 us | 16.286 us | 0.8927 us | 1.20 | 0.02 | - | - | - | - | - | BufferedReadStreamWrapRead | Job-OKZLUV | .NET 4.7.2 | 37.064 us | 14.640 us | 0.8024 us | 0.46 | 0.02 | - | - | - | - | - | StandardStreamReadByte | Job-OKZLUV | .NET 4.7.2 | 80.315 us | 26.676 us | 1.4622 us | 1.00 | 0.00 | - | - | - | - | - | BufferedReadStreamReadByte | Job-OKZLUV | .NET 4.7.2 | 118.706 us | 38.013 us | 2.0836 us | 1.48 | 0.00 | - | - | - | - | - | BufferedReadStreamChunkedReadByte | Job-OKZLUV | .NET 4.7.2 | 115.437 us | 33.352 us | 1.8282 us | 1.44 | 0.01 | - | - | - | - | - | BufferedReadStreamWrapReadByte | Job-OKZLUV | .NET 4.7.2 | 16.449 us | 11.400 us | 0.6249 us | 0.20 | 0.00 | - | - | - | - | - | ArrayReadByte | Job-OKZLUV | .NET 4.7.2 | 10.416 us | 1.866 us | 0.1023 us | 0.13 | 0.00 | - | - | - | - | - | | | | | | | | | | | | | - | StandardStreamRead | Job-CPYMXV | .NET Core 2.1 | 71.425 us | 50.441 us | 2.7648 us | 0.82 | 0.03 | - | - | - | - | - | BufferedReadStreamRead | Job-CPYMXV | .NET Core 2.1 | 32.816 us | 6.655 us | 0.3648 us | 0.38 | 0.01 | - | - | - | - | - | BufferedReadStreamChunkedRead | Job-CPYMXV | .NET Core 2.1 | 31.995 us | 7.751 us | 0.4249 us | 0.37 | 0.01 | - | - | - | - | - | BufferedReadStreamWrapRead | Job-CPYMXV | .NET Core 2.1 | 31.970 us | 4.170 us | 0.2286 us | 0.37 | 0.01 | - | - | - | - | - | StandardStreamReadByte | Job-CPYMXV | .NET Core 2.1 | 86.909 us | 18.565 us | 1.0176 us | 1.00 | 0.00 | - | - | - | - | - | BufferedReadStreamReadByte | Job-CPYMXV | .NET Core 2.1 | 14.596 us | 10.889 us | 0.5969 us | 0.17 | 0.01 | - | - | - | - | - | BufferedReadStreamChunkedReadByte | Job-CPYMXV | .NET Core 2.1 | 13.629 us | 1.569 us | 0.0860 us | 0.16 | 0.00 | - | - | - | - | - | BufferedReadStreamWrapReadByte | Job-CPYMXV | .NET Core 2.1 | 13.566 us | 1.743 us | 0.0956 us | 0.16 | 0.00 | - | - | - | - | - | ArrayReadByte | Job-CPYMXV | .NET Core 2.1 | 9.771 us | 6.658 us | 0.3650 us | 0.11 | 0.00 | - | - | - | - | - | | | | | | | | | | | | | - | StandardStreamRead | Job-BSGVGU | .NET Core 3.1 | 53.265 us | 65.819 us | 3.6078 us | 0.81 | 0.05 | - | - | - | - | - | BufferedReadStreamRead | Job-BSGVGU | .NET Core 3.1 | 33.163 us | 9.569 us | 0.5245 us | 0.51 | 0.01 | - | - | - | - | - | BufferedReadStreamChunkedRead | Job-BSGVGU | .NET Core 3.1 | 33.001 us | 6.114 us | 0.3351 us | 0.50 | 0.01 | - | - | - | - | - | BufferedReadStreamWrapRead | Job-BSGVGU | .NET Core 3.1 | 29.448 us | 7.120 us | 0.3902 us | 0.45 | 0.01 | - | - | - | - | - | StandardStreamReadByte | Job-BSGVGU | .NET Core 3.1 | 65.619 us | 6.732 us | 0.3690 us | 1.00 | 0.00 | - | - | - | - | - | BufferedReadStreamReadByte | Job-BSGVGU | .NET Core 3.1 | 13.989 us | 3.464 us | 0.1899 us | 0.21 | 0.00 | - | - | - | - | - | BufferedReadStreamChunkedReadByte | Job-BSGVGU | .NET Core 3.1 | 13.806 us | 1.710 us | 0.0938 us | 0.21 | 0.00 | - | - | - | - | - | BufferedReadStreamWrapReadByte | Job-BSGVGU | .NET Core 3.1 | 13.690 us | 1.523 us | 0.0835 us | 0.21 | 0.00 | - | - | - | - | - | ArrayReadByte | Job-BSGVGU | .NET Core 3.1 | 10.792 us | 8.228 us | 0.4510 us | 0.16 | 0.01 | - | - | - | - | - */ + private static byte[] CreateTestBytes() + { + var buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; + var random = new Random(); + random.NextBytes(buffer); + + return buffer; + } } + +/* +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1) +Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +.NET Core SDK=3.1.401 + [Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT + Job-OKZLUV : .NET Framework 4.8 (4.8.4084.0), X64 RyuJIT + Job-CPYMXV : .NET Core 2.1.21 (CoreCLR 4.6.29130.01, CoreFX 4.6.29130.02), X64 RyuJIT + Job-BSGVGU : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT + +IterationCount=3 LaunchCount=1 WarmupCount=3 + +| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|---------------------------------- |----------- |-------------- |-----------:|----------:|----------:|------:|--------:|------:|------:|------:|----------:| +| StandardStreamRead | Job-OKZLUV | .NET 4.7.2 | 66.785 us | 15.768 us | 0.8643 us | 0.83 | 0.01 | - | - | - | - | +| BufferedReadStreamRead | Job-OKZLUV | .NET 4.7.2 | 97.389 us | 17.658 us | 0.9679 us | 1.21 | 0.01 | - | - | - | - | +| BufferedReadStreamChunkedRead | Job-OKZLUV | .NET 4.7.2 | 96.006 us | 16.286 us | 0.8927 us | 1.20 | 0.02 | - | - | - | - | +| BufferedReadStreamWrapRead | Job-OKZLUV | .NET 4.7.2 | 37.064 us | 14.640 us | 0.8024 us | 0.46 | 0.02 | - | - | - | - | +| StandardStreamReadByte | Job-OKZLUV | .NET 4.7.2 | 80.315 us | 26.676 us | 1.4622 us | 1.00 | 0.00 | - | - | - | - | +| BufferedReadStreamReadByte | Job-OKZLUV | .NET 4.7.2 | 118.706 us | 38.013 us | 2.0836 us | 1.48 | 0.00 | - | - | - | - | +| BufferedReadStreamChunkedReadByte | Job-OKZLUV | .NET 4.7.2 | 115.437 us | 33.352 us | 1.8282 us | 1.44 | 0.01 | - | - | - | - | +| BufferedReadStreamWrapReadByte | Job-OKZLUV | .NET 4.7.2 | 16.449 us | 11.400 us | 0.6249 us | 0.20 | 0.00 | - | - | - | - | +| ArrayReadByte | Job-OKZLUV | .NET 4.7.2 | 10.416 us | 1.866 us | 0.1023 us | 0.13 | 0.00 | - | - | - | - | +| | | | | | | | | | | | | +| StandardStreamRead | Job-CPYMXV | .NET Core 2.1 | 71.425 us | 50.441 us | 2.7648 us | 0.82 | 0.03 | - | - | - | - | +| BufferedReadStreamRead | Job-CPYMXV | .NET Core 2.1 | 32.816 us | 6.655 us | 0.3648 us | 0.38 | 0.01 | - | - | - | - | +| BufferedReadStreamChunkedRead | Job-CPYMXV | .NET Core 2.1 | 31.995 us | 7.751 us | 0.4249 us | 0.37 | 0.01 | - | - | - | - | +| BufferedReadStreamWrapRead | Job-CPYMXV | .NET Core 2.1 | 31.970 us | 4.170 us | 0.2286 us | 0.37 | 0.01 | - | - | - | - | +| StandardStreamReadByte | Job-CPYMXV | .NET Core 2.1 | 86.909 us | 18.565 us | 1.0176 us | 1.00 | 0.00 | - | - | - | - | +| BufferedReadStreamReadByte | Job-CPYMXV | .NET Core 2.1 | 14.596 us | 10.889 us | 0.5969 us | 0.17 | 0.01 | - | - | - | - | +| BufferedReadStreamChunkedReadByte | Job-CPYMXV | .NET Core 2.1 | 13.629 us | 1.569 us | 0.0860 us | 0.16 | 0.00 | - | - | - | - | +| BufferedReadStreamWrapReadByte | Job-CPYMXV | .NET Core 2.1 | 13.566 us | 1.743 us | 0.0956 us | 0.16 | 0.00 | - | - | - | - | +| ArrayReadByte | Job-CPYMXV | .NET Core 2.1 | 9.771 us | 6.658 us | 0.3650 us | 0.11 | 0.00 | - | - | - | - | +| | | | | | | | | | | | | +| StandardStreamRead | Job-BSGVGU | .NET Core 3.1 | 53.265 us | 65.819 us | 3.6078 us | 0.81 | 0.05 | - | - | - | - | +| BufferedReadStreamRead | Job-BSGVGU | .NET Core 3.1 | 33.163 us | 9.569 us | 0.5245 us | 0.51 | 0.01 | - | - | - | - | +| BufferedReadStreamChunkedRead | Job-BSGVGU | .NET Core 3.1 | 33.001 us | 6.114 us | 0.3351 us | 0.50 | 0.01 | - | - | - | - | +| BufferedReadStreamWrapRead | Job-BSGVGU | .NET Core 3.1 | 29.448 us | 7.120 us | 0.3902 us | 0.45 | 0.01 | - | - | - | - | +| StandardStreamReadByte | Job-BSGVGU | .NET Core 3.1 | 65.619 us | 6.732 us | 0.3690 us | 1.00 | 0.00 | - | - | - | - | +| BufferedReadStreamReadByte | Job-BSGVGU | .NET Core 3.1 | 13.989 us | 3.464 us | 0.1899 us | 0.21 | 0.00 | - | - | - | - | +| BufferedReadStreamChunkedReadByte | Job-BSGVGU | .NET Core 3.1 | 13.806 us | 1.710 us | 0.0938 us | 0.21 | 0.00 | - | - | - | - | +| BufferedReadStreamWrapReadByte | Job-BSGVGU | .NET Core 3.1 | 13.690 us | 1.523 us | 0.0835 us | 0.21 | 0.00 | - | - | - | - | +| ArrayReadByte | Job-BSGVGU | .NET Core 3.1 | 10.792 us | 8.228 us | 0.4510 us | 0.16 | 0.01 | - | - | - | - | +*/ diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs index 484eaf30d1..8820406af6 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs @@ -4,27 +4,26 @@ using System.Numerics; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public interface ITestPixel + where T : struct, ITestPixel { - public interface ITestPixel - where T : struct, ITestPixel - { - void FromRgba32(Rgba32 source); + void FromRgba32(Rgba32 source); - void FromRgba32(ref Rgba32 source); + void FromRgba32(ref Rgba32 source); - void FromBytes(byte r, byte g, byte b, byte a); + void FromBytes(byte r, byte g, byte b, byte a); - void FromVector4(Vector4 source); + void FromVector4(Vector4 source); - void FromVector4(ref Vector4 source); + void FromVector4(ref Vector4 source); - Rgba32 ToRgba32(); + Rgba32 ToRgba32(); - void CopyToRgba32(ref Rgba32 dest); + void CopyToRgba32(ref Rgba32 dest); - Vector4 ToVector4(); + Vector4 ToVector4(); - void CopyToVector4(ref Vector4 dest); - } + void CopyToVector4(ref Vector4 dest); } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index 1dcacd708e..d418c45fe3 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; @@ -10,184 +9,183 @@ using SixLabors.ImageSharp.PixelFormats.Utils; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public abstract class PixelConversion_ConvertFromRgba32 { - public abstract class PixelConversion_ConvertFromRgba32 + internal struct ConversionRunner + where T : struct, ITestPixel { - internal struct ConversionRunner - where T : struct, ITestPixel - { - public readonly T[] Dest; + public readonly T[] Dest; - public readonly Rgba32[] Source; + public readonly Rgba32[] Source; - public ConversionRunner(int count) - { - this.Dest = new T[count]; - this.Source = new Rgba32[count]; - } + public ConversionRunner(int count) + { + this.Dest = new T[count]; + this.Source = new Rgba32[count]; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByRefConversion() - { - int count = this.Dest.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByRefConversion() + { + int count = this.Dest.Length; - ref T destBaseRef = ref this.Dest[0]; - ref Rgba32 sourceBaseRef = ref this.Source[0]; + ref T destBaseRef = ref this.Dest[0]; + ref Rgba32 sourceBaseRef = ref this.Source[0]; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref destBaseRef, i).FromRgba32(ref Unsafe.Add(ref sourceBaseRef, i)); - } + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i).FromRgba32(ref Unsafe.Add(ref sourceBaseRef, i)); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByValConversion() - { - int count = this.Dest.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByValConversion() + { + int count = this.Dest.Length; - ref T destBaseRef = ref this.Dest[0]; - ref Rgba32 sourceBaseRef = ref this.Source[0]; + ref T destBaseRef = ref this.Dest[0]; + ref Rgba32 sourceBaseRef = ref this.Source[0]; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref destBaseRef, i).FromRgba32(Unsafe.Add(ref sourceBaseRef, i)); - } + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i).FromRgba32(Unsafe.Add(ref sourceBaseRef, i)); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunFromBytesConversion() - { - int count = this.Dest.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunFromBytesConversion() + { + int count = this.Dest.Length; - ref T destBaseRef = ref this.Dest[0]; - ref Rgba32 sourceBaseRef = ref this.Source[0]; + ref T destBaseRef = ref this.Dest[0]; + ref Rgba32 sourceBaseRef = ref this.Source[0]; - for (int i = 0; i < count; i++) - { - ref Rgba32 s = ref Unsafe.Add(ref sourceBaseRef, i); - Unsafe.Add(ref destBaseRef, i).FromBytes(s.R, s.G, s.B, s.A); - } + for (int i = 0; i < count; i++) + { + ref Rgba32 s = ref Unsafe.Add(ref sourceBaseRef, i); + Unsafe.Add(ref destBaseRef, i).FromBytes(s.R, s.G, s.B, s.A); } } + } - internal ConversionRunner CompatibleMemLayoutRunner; + internal ConversionRunner CompatibleMemLayoutRunner; - internal ConversionRunner PermutedRunnerRgbaToArgb; + internal ConversionRunner PermutedRunnerRgbaToArgb; - [Params(256, 2048)] - public int Count { get; set; } + [Params(256, 2048)] + public int Count { get; set; } - [GlobalSetup] - public void Setup() - { - this.CompatibleMemLayoutRunner = new ConversionRunner(this.Count); - this.PermutedRunnerRgbaToArgb = new ConversionRunner(this.Count); - } + [GlobalSetup] + public void Setup() + { + this.CompatibleMemLayoutRunner = new ConversionRunner(this.Count); + this.PermutedRunnerRgbaToArgb = new ConversionRunner(this.Count); } +} - public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_ConvertFromRgba32 +public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_ConvertFromRgba32 +{ + [Benchmark(Baseline = true)] + public void ByRef() { - [Benchmark(Baseline = true)] - public void ByRef() - { - this.CompatibleMemLayoutRunner.RunByRefConversion(); - } - - [Benchmark] - public void ByVal() - { - this.CompatibleMemLayoutRunner.RunByValConversion(); - } - - [Benchmark] - public void FromBytes() - { - this.CompatibleMemLayoutRunner.RunFromBytesConversion(); - } - - [Benchmark] - public void Inline() - { - ref Rgba32 sBase = ref this.CompatibleMemLayoutRunner.Source[0]; - ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Dest[0]); + this.CompatibleMemLayoutRunner.RunByRefConversion(); + } - for (int i = 0; i < this.Count; i++) - { - Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i); - } - } + [Benchmark] + public void ByVal() + { + this.CompatibleMemLayoutRunner.RunByValConversion(); + } - /* Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | - ---------- |------ |---------:|---------:|---------:|-------:|---------:| - ByRef | 256 | 128.5 ns | 1.217 ns | 1.138 ns | 1.00 | 0.00 | - ByVal | 256 | 196.7 ns | 2.792 ns | 2.612 ns | 1.53 | 0.02 | - FromBytes | 256 | 321.7 ns | 2.180 ns | 1.820 ns | 2.50 | 0.03 | - Inline | 256 | 129.9 ns | 2.759 ns | 2.581 ns | 1.01 | 0.02 | */ + [Benchmark] + public void FromBytes() + { + this.CompatibleMemLayoutRunner.RunFromBytesConversion(); } - public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConversion_ConvertFromRgba32 + [Benchmark] + public void Inline() { - [Benchmark(Baseline = true)] - public void ByRef() - { - this.PermutedRunnerRgbaToArgb.RunByRefConversion(); - } + ref Rgba32 sBase = ref this.CompatibleMemLayoutRunner.Source[0]; + ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Dest[0]); - [Benchmark] - public void ByVal() + for (int i = 0; i < this.Count; i++) { - this.PermutedRunnerRgbaToArgb.RunByValConversion(); + Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i); } + } - [Benchmark] - public void FromBytes() - { - this.PermutedRunnerRgbaToArgb.RunFromBytesConversion(); - } + /* Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + ---------- |------ |---------:|---------:|---------:|-------:|---------:| + ByRef | 256 | 128.5 ns | 1.217 ns | 1.138 ns | 1.00 | 0.00 | + ByVal | 256 | 196.7 ns | 2.792 ns | 2.612 ns | 1.53 | 0.02 | + FromBytes | 256 | 321.7 ns | 2.180 ns | 1.820 ns | 2.50 | 0.03 | + Inline | 256 | 129.9 ns | 2.759 ns | 2.581 ns | 1.01 | 0.02 | */ +} - [Benchmark] - public void InlineShuffle() - { - ref Rgba32 sBase = ref this.PermutedRunnerRgbaToArgb.Source[0]; - ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Dest[0]; +public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConversion_ConvertFromRgba32 +{ + [Benchmark(Baseline = true)] + public void ByRef() + { + this.PermutedRunnerRgbaToArgb.RunByRefConversion(); + } - for (int i = 0; i < this.Count; i++) - { - Rgba32 s = Unsafe.Add(ref sBase, i); - ref TestArgb d = ref Unsafe.Add(ref dBase, i); + [Benchmark] + public void ByVal() + { + this.PermutedRunnerRgbaToArgb.RunByValConversion(); + } - d.R = s.R; - d.G = s.G; - d.B = s.B; - d.A = s.A; - } - } + [Benchmark] + public void FromBytes() + { + this.PermutedRunnerRgbaToArgb.RunFromBytesConversion(); + } + + [Benchmark] + public void InlineShuffle() + { + ref Rgba32 sBase = ref this.PermutedRunnerRgbaToArgb.Source[0]; + ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Dest[0]; - [Benchmark] - public void PixelConverter_Rgba32_ToArgb32() + for (int i = 0; i < this.Count; i++) { - Span source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); - Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Dest); + Rgba32 s = Unsafe.Add(ref sBase, i); + ref TestArgb d = ref Unsafe.Add(ref dBase, i); - PixelConverter.FromRgba32.ToArgb32(source, dest); + d.R = s.R; + d.G = s.G; + d.B = s.B; + d.A = s.A; } + } - /* - RESULTS: - | Method | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | - |------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:| - | ByRef | 256 | 288.84 ns | 19.601 ns | 52.319 ns | 268.10 ns | 1.00 | 0.00 | - | ByVal | 256 | 267.97 ns | 1.831 ns | 1.713 ns | 267.85 ns | 0.77 | 0.18 | - | FromBytes | 256 | 266.81 ns | 2.427 ns | 2.270 ns | 266.47 ns | 0.76 | 0.18 | - | InlineShuffle | 256 | 291.41 ns | 5.820 ns | 5.444 ns | 290.17 ns | 0.83 | 0.19 | - | PixelConverter_Rgba32_ToArgb32 | 256 | 38.62 ns | 0.431 ns | 0.403 ns | 38.68 ns | 0.11 | 0.03 | - | | | | | | | | | - | ByRef | 2048 | 2,197.69 ns | 15.826 ns | 14.804 ns | 2,197.25 ns | 1.00 | 0.00 | - | ByVal | 2048 | 2,226.81 ns | 44.266 ns | 62.054 ns | 2,197.17 ns | 1.03 | 0.04 | - | FromBytes | 2048 | 2,181.35 ns | 18.033 ns | 16.868 ns | 2,185.97 ns | 0.99 | 0.01 | - | InlineShuffle | 2048 | 2,233.10 ns | 27.673 ns | 24.531 ns | 2,229.78 ns | 1.02 | 0.01 | - | PixelConverter_Rgba32_ToArgb32 | 2048 | 139.90 ns | 2.152 ns | 3.825 ns | 138.70 ns | 0.06 | 0.00 | - */ + [Benchmark] + public void PixelConverter_Rgba32_ToArgb32() + { + Span source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); + Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Dest); + + PixelConverter.FromRgba32.ToArgb32(source, dest); } + + /* + RESULTS: + | Method | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | + |------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:| + | ByRef | 256 | 288.84 ns | 19.601 ns | 52.319 ns | 268.10 ns | 1.00 | 0.00 | + | ByVal | 256 | 267.97 ns | 1.831 ns | 1.713 ns | 267.85 ns | 0.77 | 0.18 | + | FromBytes | 256 | 266.81 ns | 2.427 ns | 2.270 ns | 266.47 ns | 0.76 | 0.18 | + | InlineShuffle | 256 | 291.41 ns | 5.820 ns | 5.444 ns | 290.17 ns | 0.83 | 0.19 | + | PixelConverter_Rgba32_ToArgb32 | 256 | 38.62 ns | 0.431 ns | 0.403 ns | 38.68 ns | 0.11 | 0.03 | + | | | | | | | | | + | ByRef | 2048 | 2,197.69 ns | 15.826 ns | 14.804 ns | 2,197.25 ns | 1.00 | 0.00 | + | ByVal | 2048 | 2,226.81 ns | 44.266 ns | 62.054 ns | 2,197.17 ns | 1.03 | 0.04 | + | FromBytes | 2048 | 2,181.35 ns | 18.033 ns | 16.868 ns | 2,185.97 ns | 0.99 | 0.01 | + | InlineShuffle | 2048 | 2,233.10 ns | 27.673 ns | 24.531 ns | 2,229.78 ns | 1.02 | 0.01 | + | PixelConverter_Rgba32_ToArgb32 | 2048 | 139.90 ns | 2.152 ns | 3.825 ns | 138.70 ns | 0.06 | 0.00 | + */ } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs index de0e32e8b2..a167ade12b 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs @@ -9,138 +9,137 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public class PixelConversion_ConvertFromVector4 { - public class PixelConversion_ConvertFromVector4 + [StructLayout(LayoutKind.Sequential)] + private struct TestRgbaVector : ITestPixel { - [StructLayout(LayoutKind.Sequential)] - private struct TestRgbaVector : ITestPixel + private Vector4 v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 p) { - private Vector4 v; + this.v = p; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) - { - this.v = p; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 p) + { + this.v = p; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) - { - this.v = p; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() => this.v; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => this.v; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToVector4(ref Vector4 dest) + { + dest = this.v; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) - { - dest = this.v; - } + public void FromRgba32(Rgba32 source) => throw new System.NotImplementedException(); + + public void FromRgba32(ref Rgba32 source) => throw new System.NotImplementedException(); + + public void FromBytes(byte r, byte g, byte b, byte a) => throw new System.NotImplementedException(); - public void FromRgba32(Rgba32 source) => throw new System.NotImplementedException(); + public Rgba32 ToRgba32() => throw new System.NotImplementedException(); - public void FromRgba32(ref Rgba32 source) => throw new System.NotImplementedException(); + public void CopyToRgba32(ref Rgba32 dest) => throw new System.NotImplementedException(); + } - public void FromBytes(byte r, byte g, byte b, byte a) => throw new System.NotImplementedException(); + private struct ConversionRunner + where T : struct, ITestPixel + { + private T[] dest; - public Rgba32 ToRgba32() => throw new System.NotImplementedException(); + private Vector4[] source; - public void CopyToRgba32(ref Rgba32 dest) => throw new System.NotImplementedException(); + public ConversionRunner(int count) + { + this.dest = new T[count]; + this.source = new Vector4[count]; } - private struct ConversionRunner - where T : struct, ITestPixel + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByRefConversion() { - private T[] dest; + int count = this.dest.Length; - private Vector4[] source; + ref T destBaseRef = ref this.dest[0]; + ref Vector4 sourceBaseRef = ref this.source[0]; - public ConversionRunner(int count) + for (int i = 0; i < count; i++) { - this.dest = new T[count]; - this.source = new Vector4[count]; + Unsafe.Add(ref destBaseRef, i).FromVector4(ref Unsafe.Add(ref sourceBaseRef, i)); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByRefConversion() - { - int count = this.dest.Length; - - ref T destBaseRef = ref this.dest[0]; - ref Vector4 sourceBaseRef = ref this.source[0]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByValConversion() + { + int count = this.dest.Length; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref destBaseRef, i).FromVector4(ref Unsafe.Add(ref sourceBaseRef, i)); - } - } + ref T destBaseRef = ref this.dest[0]; + ref Vector4 sourceBaseRef = ref this.source[0]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByValConversion() + for (int i = 0; i < count; i++) { - int count = this.dest.Length; - - ref T destBaseRef = ref this.dest[0]; - ref Vector4 sourceBaseRef = ref this.source[0]; - - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref destBaseRef, i).FromVector4(Unsafe.Add(ref sourceBaseRef, i)); - } + Unsafe.Add(ref destBaseRef, i).FromVector4(Unsafe.Add(ref sourceBaseRef, i)); } } + } - private ConversionRunner nonVectorRunner; - - private ConversionRunner vectorRunner; + private ConversionRunner nonVectorRunner; - [Params(32)] - public int Count { get; set; } + private ConversionRunner vectorRunner; - [GlobalSetup] - public void Setup() - { - this.nonVectorRunner = new ConversionRunner(this.Count); - this.vectorRunner = new ConversionRunner(this.Count); - } + [Params(32)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public void VectorByRef() - { - this.vectorRunner.RunByRefConversion(); - } + [GlobalSetup] + public void Setup() + { + this.nonVectorRunner = new ConversionRunner(this.Count); + this.vectorRunner = new ConversionRunner(this.Count); + } - [Benchmark] - public void VectorByVal() - { - this.vectorRunner.RunByValConversion(); - } + [Benchmark(Baseline = true)] + public void VectorByRef() + { + this.vectorRunner.RunByRefConversion(); + } - [Benchmark] - public void NonVectorByRef() - { - this.nonVectorRunner.RunByRefConversion(); - } + [Benchmark] + public void VectorByVal() + { + this.vectorRunner.RunByValConversion(); + } - [Benchmark] - public void NonVectorByVal() - { - this.nonVectorRunner.RunByValConversion(); - } + [Benchmark] + public void NonVectorByRef() + { + this.nonVectorRunner.RunByRefConversion(); } - /* - * Results: - * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | - * --------------- |------ |----------- |---------- |------- |-------------- | - * VectorByRef | 32 | 23.6678 ns | 0.1141 ns | 1.00 | 0.00 | - * VectorByVal | 32 | 24.5347 ns | 0.0771 ns | 1.04 | 0.01 | - * NonVectorByRef | 32 | 59.0187 ns | 0.2114 ns | 2.49 | 0.01 | - * NonVectorByVal | 32 | 58.7529 ns | 0.2545 ns | 2.48 | 0.02 | - * - * !!! Conclusion !!! - * We do not need by-ref version of ConvertFromVector4() stuff - */ + [Benchmark] + public void NonVectorByVal() + { + this.nonVectorRunner.RunByValConversion(); + } } + +/* + * Results: + * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | + * --------------- |------ |----------- |---------- |------- |-------------- | + * VectorByRef | 32 | 23.6678 ns | 0.1141 ns | 1.00 | 0.00 | + * VectorByVal | 32 | 24.5347 ns | 0.0771 ns | 1.04 | 0.01 | + * NonVectorByRef | 32 | 59.0187 ns | 0.2114 ns | 2.49 | 0.01 | + * NonVectorByVal | 32 | 58.7529 ns | 0.2545 ns | 2.48 | 0.02 | + * + * !!! Conclusion !!! + * We do not need by-ref version of ConvertFromVector4() stuff + */ diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs index a9cd385056..cd8307614e 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs @@ -6,106 +6,105 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +/// +/// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer? +/// 1. Rgba32 ToRgba32(); +/// OR +/// 2. void CopyToRgba32(ref Rgba32 dest); +/// ? +/// +public class PixelConversion_ConvertToRgba32 { - /// - /// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer? - /// 1. Rgba32 ToRgba32(); - /// OR - /// 2. void CopyToRgba32(ref Rgba32 dest); - /// ? - /// - public class PixelConversion_ConvertToRgba32 + private struct ConversionRunner + where T : struct, ITestPixel { - private struct ConversionRunner - where T : struct, ITestPixel - { - private T[] source; + private T[] source; - private Rgba32[] dest; + private Rgba32[] dest; - public ConversionRunner(int count) - { - this.source = new T[count]; - this.dest = new Rgba32[count]; - } + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Rgba32[count]; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunRetvalConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Rgba32 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Rgba32 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); - } + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunCopyToConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Rgba32 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Rgba32 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref Unsafe.Add(ref destBaseRef, i)); - } + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref Unsafe.Add(ref destBaseRef, i)); } } + } - private ConversionRunner compatibleMemoryLayoutRunner; - - private ConversionRunner permutedRunner; + private ConversionRunner compatibleMemoryLayoutRunner; - [Params(32)] - public int Count { get; set; } + private ConversionRunner permutedRunner; - [GlobalSetup] - public void Setup() - { - this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); - this.permutedRunner = new ConversionRunner(this.Count); - } + [Params(32)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public void CompatibleRetval() - { - this.compatibleMemoryLayoutRunner.RunRetvalConversion(); - } + [GlobalSetup] + public void Setup() + { + this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); + this.permutedRunner = new ConversionRunner(this.Count); + } - [Benchmark] - public void CompatibleCopyTo() - { - this.compatibleMemoryLayoutRunner.RunCopyToConversion(); - } + [Benchmark(Baseline = true)] + public void CompatibleRetval() + { + this.compatibleMemoryLayoutRunner.RunRetvalConversion(); + } - [Benchmark] - public void PermutedRetval() - { - this.permutedRunner.RunRetvalConversion(); - } + [Benchmark] + public void CompatibleCopyTo() + { + this.compatibleMemoryLayoutRunner.RunCopyToConversion(); + } - [Benchmark] - public void PermutedCopyTo() - { - this.permutedRunner.RunCopyToConversion(); - } + [Benchmark] + public void PermutedRetval() + { + this.permutedRunner.RunRetvalConversion(); } - /* - * Results: - * - * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | - * --------------- |------ |------------ |---------- |------- |-------------- | - * CompatibleRetval | 128 | 89.7358 ns | 2.2389 ns | 1.00 | 0.00 | - * CompatibleCopyTo | 128 | 89.4112 ns | 2.2901 ns | 1.00 | 0.03 | - * PermutedRetval | 128 | 845.4038 ns | 5.6154 ns | 9.43 | 0.23 | - * PermutedCopyTo | 128 | 155.6004 ns | 3.8870 ns | 1.73 | 0.06 | - */ + [Benchmark] + public void PermutedCopyTo() + { + this.permutedRunner.RunCopyToConversion(); + } } + +/* + * Results: + * + * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | + * --------------- |------ |------------ |---------- |------- |-------------- | + * CompatibleRetval | 128 | 89.7358 ns | 2.2389 ns | 1.00 | 0.00 | + * CompatibleCopyTo | 128 | 89.4112 ns | 2.2901 ns | 1.00 | 0.03 | + * PermutedRetval | 128 | 845.4038 ns | 5.6154 ns | 9.43 | 0.23 | + * PermutedCopyTo | 128 | 155.6004 ns | 3.8870 ns | 1.73 | 0.06 | + */ diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs index 40048ec21a..9ea0c04c77 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs @@ -6,110 +6,109 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public class PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation { - public class PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation + private struct ConversionRunner + where T : struct, ITestPixel { - private struct ConversionRunner - where T : struct, ITestPixel - { - private T[] source; + private T[] source; - private Rgba32[] dest; + private Rgba32[] dest; - public ConversionRunner(int count) - { - this.source = new T[count]; - this.dest = new Rgba32[count]; - } + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Rgba32[count]; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunRetvalConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Rgba32 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Rgba32 destBaseRef = ref this.dest[0]; - Rgba32 temp; + Rgba32 temp; - for (int i = 0; i < count; i++) - { - temp = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); + for (int i = 0; i < count; i++) + { + temp = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); - // manipulate pixel before saving to dest buffer: - temp.A = 0; + // manipulate pixel before saving to dest buffer: + temp.A = 0; - Unsafe.Add(ref destBaseRef, i) = temp; - } + Unsafe.Add(ref destBaseRef, i) = temp; } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunCopyToConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Rgba32 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Rgba32 destBaseRef = ref this.dest[0]; - Rgba32 temp = default; + Rgba32 temp = default; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref temp); + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref temp); - // manipulate pixel before saving to dest buffer: - temp.A = 0; + // manipulate pixel before saving to dest buffer: + temp.A = 0; - Unsafe.Add(ref destBaseRef, i) = temp; - } + Unsafe.Add(ref destBaseRef, i) = temp; } } + } - private ConversionRunner compatibleMemoryLayoutRunner; - - private ConversionRunner permutedRunner; + private ConversionRunner compatibleMemoryLayoutRunner; - [Params(32)] - public int Count { get; set; } + private ConversionRunner permutedRunner; - [GlobalSetup] - public void Setup() - { - this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); - this.permutedRunner = new ConversionRunner(this.Count); - } + [Params(32)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public void CompatibleRetval() - { - this.compatibleMemoryLayoutRunner.RunRetvalConversion(); - } + [GlobalSetup] + public void Setup() + { + this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); + this.permutedRunner = new ConversionRunner(this.Count); + } - [Benchmark] - public void CompatibleCopyTo() - { - this.compatibleMemoryLayoutRunner.RunCopyToConversion(); - } + [Benchmark(Baseline = true)] + public void CompatibleRetval() + { + this.compatibleMemoryLayoutRunner.RunRetvalConversion(); + } - [Benchmark] - public void PermutedRetval() - { - this.permutedRunner.RunRetvalConversion(); - } + [Benchmark] + public void CompatibleCopyTo() + { + this.compatibleMemoryLayoutRunner.RunCopyToConversion(); + } - [Benchmark] - public void PermutedCopyTo() - { - this.permutedRunner.RunCopyToConversion(); - } + [Benchmark] + public void PermutedRetval() + { + this.permutedRunner.RunRetvalConversion(); } - // RESULTS: - // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | - // ----------------- |------ |----------:|----------:|----------:|-------:|---------:| - // CompatibleRetval | 32 | 53.05 ns | 0.1865 ns | 0.1557 ns | 1.00 | 0.00 | - // CompatibleCopyTo | 32 | 36.12 ns | 0.3596 ns | 0.3003 ns | 0.68 | 0.01 | - // PermutedRetval | 32 | 303.61 ns | 5.1697 ns | 4.8358 ns | 5.72 | 0.09 | - // PermutedCopyTo | 32 | 38.05 ns | 0.8053 ns | 1.2297 ns | 0.72 | 0.02 | + [Benchmark] + public void PermutedCopyTo() + { + this.permutedRunner.RunCopyToConversion(); + } } + +// RESULTS: +// Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | +// ----------------- |------ |----------:|----------:|----------:|-------:|---------:| +// CompatibleRetval | 32 | 53.05 ns | 0.1865 ns | 0.1557 ns | 1.00 | 0.00 | +// CompatibleCopyTo | 32 | 36.12 ns | 0.3596 ns | 0.3003 ns | 0.68 | 0.01 | +// PermutedRetval | 32 | 303.61 ns | 5.1697 ns | 4.8358 ns | 5.72 | 0.09 | +// PermutedCopyTo | 32 | 38.05 ns | 0.8053 ns | 1.2297 ns | 0.72 | 0.02 | diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs index 65afa2a2fe..c03560e106 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs @@ -5,79 +5,78 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public class PixelConversion_ConvertToVector4 { - public class PixelConversion_ConvertToVector4 + private struct ConversionRunner + where T : struct, ITestPixel { - private struct ConversionRunner - where T : struct, ITestPixel - { - private T[] source; + private T[] source; - private Vector4[] dest; + private Vector4[] dest; - public ConversionRunner(int count) - { - this.source = new T[count]; - this.dest = new Vector4[count]; - } + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Vector4[count]; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunRetvalConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Vector4 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); - } + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunCopyToConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Vector4 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref Unsafe.Add(ref destBaseRef, i)); - } + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref Unsafe.Add(ref destBaseRef, i)); } } + } - private ConversionRunner runner; - - [Params(32)] - public int Count { get; set; } + private ConversionRunner runner; - [GlobalSetup] - public void Setup() - { - this.runner = new ConversionRunner(this.Count); - } + [Params(32)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public void UseRetval() - { - this.runner.RunRetvalConversion(); - } + [GlobalSetup] + public void Setup() + { + this.runner = new ConversionRunner(this.Count); + } - [Benchmark] - public void UseCopyTo() - { - this.runner.RunCopyToConversion(); - } + [Benchmark(Baseline = true)] + public void UseRetval() + { + this.runner.RunRetvalConversion(); + } - // RESULTS: - // Method | Count | Mean | Error | StdDev | Scaled | - // ---------- |------ |---------:|---------:|---------:|-------:| - // UseRetval | 32 | 109.0 ns | 1.202 ns | 1.125 ns | 1.00 | - // UseCopyTo | 32 | 108.6 ns | 1.151 ns | 1.020 ns | 1.00 | + [Benchmark] + public void UseCopyTo() + { + this.runner.RunCopyToConversion(); } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | + // ---------- |------ |---------:|---------:|---------:|-------:| + // UseRetval | 32 | 109.0 ns | 1.202 ns | 1.125 ns | 1.00 | + // UseCopyTo | 32 | 108.6 ns | 1.151 ns | 1.020 ns | 1.00 | } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs index 61d5893692..462bbbf7b3 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs @@ -5,93 +5,92 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public class PixelConversion_ConvertToVector4_AsPartOfCompositeOperation { - public class PixelConversion_ConvertToVector4_AsPartOfCompositeOperation + private struct ConversionRunner + where T : struct, ITestPixel { - private struct ConversionRunner - where T : struct, ITestPixel - { - private T[] source; + private T[] source; - private Vector4[] dest; + private Vector4[] dest; - public ConversionRunner(int count) - { - this.source = new T[count]; - this.dest = new Vector4[count]; - } + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Vector4[count]; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunRetvalConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Vector4 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; - Vector4 temp; + Vector4 temp; - for (int i = 0; i < count; i++) - { - temp = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); + for (int i = 0; i < count; i++) + { + temp = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); - // manipulate pixel before saving to dest buffer: - temp.W = 0; + // manipulate pixel before saving to dest buffer: + temp.W = 0; - Unsafe.Add(ref destBaseRef, i) = temp; - } + Unsafe.Add(ref destBaseRef, i) = temp; } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunCopyToConversion() - { - int count = this.source.Length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; - ref T sourceBaseRef = ref this.source[0]; - ref Vector4 destBaseRef = ref this.dest[0]; + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; - Vector4 temp = default; + Vector4 temp = default; - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref temp); + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref temp); - // manipulate pixel before saving to dest buffer: - temp.W = 0; + // manipulate pixel before saving to dest buffer: + temp.W = 0; - Unsafe.Add(ref destBaseRef, i) = temp; - } + Unsafe.Add(ref destBaseRef, i) = temp; } } + } - private ConversionRunner runner; - - [Params(32)] - public int Count { get; set; } + private ConversionRunner runner; - [GlobalSetup] - public void Setup() - { - this.runner = new ConversionRunner(this.Count); - } + [Params(32)] + public int Count { get; set; } - [Benchmark(Baseline = true)] - public void UseRetval() - { - this.runner.RunRetvalConversion(); - } + [GlobalSetup] + public void Setup() + { + this.runner = new ConversionRunner(this.Count); + } - [Benchmark] - public void UseCopyTo() - { - this.runner.RunCopyToConversion(); - } + [Benchmark(Baseline = true)] + public void UseRetval() + { + this.runner.RunRetvalConversion(); + } - // RESULTS: - // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | - // ---------- |------ |---------:|---------:|---------:|-------:|---------:| - // UseRetval | 32 | 120.2 ns | 1.560 ns | 1.383 ns | 1.00 | 0.00 | - // UseCopyTo | 32 | 121.7 ns | 2.439 ns | 2.281 ns | 1.01 | 0.02 | + [Benchmark] + public void UseCopyTo() + { + this.runner.RunCopyToConversion(); } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + // ---------- |------ |---------:|---------:|---------:|-------:|---------:| + // UseRetval | 32 | 120.2 ns | 1.560 ns | 1.383 ns | 1.00 | 0.00 | + // UseCopyTo | 32 | 121.7 ns | 2.439 ns | 2.281 ns | 1.01 | 0.02 | } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs index 252ff90589..86ac928af9 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -9,280 +8,279 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public unsafe class PixelConversion_PackFromRgbPlanes { - public unsafe class PixelConversion_PackFromRgbPlanes - { - private byte[] rBuf; - private byte[] gBuf; - private byte[] bBuf; - private Rgb24[] rgbBuf; - private Rgba32[] rgbaBuf; + private byte[] rBuf; + private byte[] gBuf; + private byte[] bBuf; + private Rgb24[] rgbBuf; + private Rgba32[] rgbaBuf; - private float[] rFloat; - private float[] gFloat; - private float[] bFloat; + private float[] rFloat; + private float[] gFloat; + private float[] bFloat; - private float[] rgbaFloat; + private float[] rgbaFloat; - [Params(1024)] - public int Count { get; set; } + [Params(1024)] + public int Count { get; set; } - [GlobalSetup] - public void Setup() - { - this.rBuf = new byte[this.Count]; - this.gBuf = new byte[this.Count]; - this.bBuf = new byte[this.Count]; - this.rgbBuf = new Rgb24[this.Count + 3]; // padded - this.rgbaBuf = new Rgba32[this.Count]; + [GlobalSetup] + public void Setup() + { + this.rBuf = new byte[this.Count]; + this.gBuf = new byte[this.Count]; + this.bBuf = new byte[this.Count]; + this.rgbBuf = new Rgb24[this.Count + 3]; // padded + this.rgbaBuf = new Rgba32[this.Count]; - this.rFloat = new float[this.Count]; - this.gFloat = new float[this.Count]; - this.bFloat = new float[this.Count]; + this.rFloat = new float[this.Count]; + this.gFloat = new float[this.Count]; + this.bFloat = new float[this.Count]; - this.rgbaFloat = new float[this.Count * 4]; - } + this.rgbaFloat = new float[this.Count * 4]; + } - // [Benchmark] - public void Rgb24_Scalar_PerElement_Pinned() + // [Benchmark] + public void Rgb24_Scalar_PerElement_Pinned() + { + fixed (byte* r = &this.rBuf[0]) { - fixed (byte* r = &this.rBuf[0]) + fixed (byte* g = &this.gBuf[0]) { - fixed (byte* g = &this.gBuf[0]) + fixed (byte* b = &this.bBuf[0]) { - fixed (byte* b = &this.bBuf[0]) + fixed (Rgb24* rgb = &this.rgbBuf[0]) { - fixed (Rgb24* rgb = &this.rgbBuf[0]) + for (int i = 0; i < this.Count; i++) { - for (int i = 0; i < this.Count; i++) - { - Rgb24* d = rgb + i; - d->R = r[i]; - d->G = g[i]; - d->B = b[i]; - } + Rgb24* d = rgb + i; + d->R = r[i]; + d->G = g[i]; + d->B = b[i]; } } } } } + } - [Benchmark] - public void Rgb24_Scalar_PerElement_Span() - { - Span r = this.rBuf; - Span g = this.rBuf; - Span b = this.rBuf; - Span rgb = this.rgbBuf; + [Benchmark] + public void Rgb24_Scalar_PerElement_Span() + { + Span r = this.rBuf; + Span g = this.rBuf; + Span b = this.rBuf; + Span rgb = this.rgbBuf; - for (int i = 0; i < r.Length; i++) - { - ref Rgb24 d = ref rgb[i]; - d.R = r[i]; - d.G = g[i]; - d.B = b[i]; - } + for (int i = 0; i < r.Length; i++) + { + ref Rgb24 d = ref rgb[i]; + d.R = r[i]; + d.G = g[i]; + d.B = b[i]; } + } - [Benchmark] - public void Rgb24_Scalar_PerElement_Unsafe() - { - ref byte r = ref this.rBuf[0]; - ref byte g = ref this.rBuf[0]; - ref byte b = ref this.rBuf[0]; - ref Rgb24 rgb = ref this.rgbBuf[0]; + [Benchmark] + public void Rgb24_Scalar_PerElement_Unsafe() + { + ref byte r = ref this.rBuf[0]; + ref byte g = ref this.rBuf[0]; + ref byte b = ref this.rBuf[0]; + ref Rgb24 rgb = ref this.rgbBuf[0]; - for (int i = 0; i < this.Count; i++) - { - ref Rgb24 d = ref Unsafe.Add(ref rgb, i); - d.R = Unsafe.Add(ref r, i); - d.G = Unsafe.Add(ref g, i); - d.B = Unsafe.Add(ref b, i); - } + for (int i = 0; i < this.Count; i++) + { + ref Rgb24 d = ref Unsafe.Add(ref rgb, i); + d.R = Unsafe.Add(ref r, i); + d.G = Unsafe.Add(ref g, i); + d.B = Unsafe.Add(ref b, i); } + } - [Benchmark] - public void Rgb24_Scalar_PerElement_Batched8() - { - ref Byte8 r = ref Unsafe.As(ref this.rBuf[0]); - ref Byte8 g = ref Unsafe.As(ref this.rBuf[0]); - ref Byte8 b = ref Unsafe.As(ref this.rBuf[0]); - ref Rgb24 rgb = ref this.rgbBuf[0]; + [Benchmark] + public void Rgb24_Scalar_PerElement_Batched8() + { + ref Byte8 r = ref Unsafe.As(ref this.rBuf[0]); + ref Byte8 g = ref Unsafe.As(ref this.rBuf[0]); + ref Byte8 b = ref Unsafe.As(ref this.rBuf[0]); + ref Rgb24 rgb = ref this.rgbBuf[0]; - int count = this.Count / 8; - for (int i = 0; i < count; i++) - { - ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 8); - ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); - ref Rgb24 d2 = ref Unsafe.Add(ref d0, 2); - ref Rgb24 d3 = ref Unsafe.Add(ref d0, 3); - ref Rgb24 d4 = ref Unsafe.Add(ref d0, 4); - ref Rgb24 d5 = ref Unsafe.Add(ref d0, 5); - ref Rgb24 d6 = ref Unsafe.Add(ref d0, 6); - ref Rgb24 d7 = ref Unsafe.Add(ref d0, 7); - - ref Byte8 rr = ref Unsafe.Add(ref r, i); - ref Byte8 gg = ref Unsafe.Add(ref g, i); - ref Byte8 bb = ref Unsafe.Add(ref b, i); - - d0.R = rr.V0; - d0.G = gg.V0; - d0.B = bb.V0; - - d1.R = rr.V1; - d1.G = gg.V1; - d1.B = bb.V1; - - d2.R = rr.V2; - d2.G = gg.V2; - d2.B = bb.V2; - - d3.R = rr.V3; - d3.G = gg.V3; - d3.B = bb.V3; - - d4.R = rr.V4; - d4.G = gg.V4; - d4.B = bb.V4; - - d5.R = rr.V5; - d5.G = gg.V5; - d5.B = bb.V5; - - d6.R = rr.V6; - d6.G = gg.V6; - d6.B = bb.V6; - - d7.R = rr.V7; - d7.G = gg.V7; - d7.B = bb.V7; - } + int count = this.Count / 8; + for (int i = 0; i < count; i++) + { + ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 8); + ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); + ref Rgb24 d2 = ref Unsafe.Add(ref d0, 2); + ref Rgb24 d3 = ref Unsafe.Add(ref d0, 3); + ref Rgb24 d4 = ref Unsafe.Add(ref d0, 4); + ref Rgb24 d5 = ref Unsafe.Add(ref d0, 5); + ref Rgb24 d6 = ref Unsafe.Add(ref d0, 6); + ref Rgb24 d7 = ref Unsafe.Add(ref d0, 7); + + ref Byte8 rr = ref Unsafe.Add(ref r, i); + ref Byte8 gg = ref Unsafe.Add(ref g, i); + ref Byte8 bb = ref Unsafe.Add(ref b, i); + + d0.R = rr.V0; + d0.G = gg.V0; + d0.B = bb.V0; + + d1.R = rr.V1; + d1.G = gg.V1; + d1.B = bb.V1; + + d2.R = rr.V2; + d2.G = gg.V2; + d2.B = bb.V2; + + d3.R = rr.V3; + d3.G = gg.V3; + d3.B = bb.V3; + + d4.R = rr.V4; + d4.G = gg.V4; + d4.B = bb.V4; + + d5.R = rr.V5; + d5.G = gg.V5; + d5.B = bb.V5; + + d6.R = rr.V6; + d6.G = gg.V6; + d6.B = bb.V6; + + d7.R = rr.V7; + d7.G = gg.V7; + d7.B = bb.V7; } + } - [Benchmark] - public void Rgb24_Scalar_PerElement_Batched4() - { - ref Byte4 r = ref Unsafe.As(ref this.rBuf[0]); - ref Byte4 g = ref Unsafe.As(ref this.rBuf[0]); - ref Byte4 b = ref Unsafe.As(ref this.rBuf[0]); - ref Rgb24 rgb = ref this.rgbBuf[0]; + [Benchmark] + public void Rgb24_Scalar_PerElement_Batched4() + { + ref Byte4 r = ref Unsafe.As(ref this.rBuf[0]); + ref Byte4 g = ref Unsafe.As(ref this.rBuf[0]); + ref Byte4 b = ref Unsafe.As(ref this.rBuf[0]); + ref Rgb24 rgb = ref this.rgbBuf[0]; - int count = this.Count / 4; - for (int i = 0; i < count; i++) - { - ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 4); - ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); - ref Rgb24 d2 = ref Unsafe.Add(ref d0, 2); - ref Rgb24 d3 = ref Unsafe.Add(ref d0, 3); - - ref Byte4 rr = ref Unsafe.Add(ref r, i); - ref Byte4 gg = ref Unsafe.Add(ref g, i); - ref Byte4 bb = ref Unsafe.Add(ref b, i); - - d0.R = rr.V0; - d0.G = gg.V0; - d0.B = bb.V0; - - d1.R = rr.V1; - d1.G = gg.V1; - d1.B = bb.V1; - - d2.R = rr.V2; - d2.G = gg.V2; - d2.B = bb.V2; - - d3.R = rr.V3; - d3.G = gg.V3; - d3.B = bb.V3; - } + int count = this.Count / 4; + for (int i = 0; i < count; i++) + { + ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 4); + ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); + ref Rgb24 d2 = ref Unsafe.Add(ref d0, 2); + ref Rgb24 d3 = ref Unsafe.Add(ref d0, 3); + + ref Byte4 rr = ref Unsafe.Add(ref r, i); + ref Byte4 gg = ref Unsafe.Add(ref g, i); + ref Byte4 bb = ref Unsafe.Add(ref b, i); + + d0.R = rr.V0; + d0.G = gg.V0; + d0.B = bb.V0; + + d1.R = rr.V1; + d1.G = gg.V1; + d1.B = bb.V1; + + d2.R = rr.V2; + d2.G = gg.V2; + d2.B = bb.V2; + + d3.R = rr.V3; + d3.G = gg.V3; + d3.B = bb.V3; } + } - [Benchmark(Baseline = true)] - public void Rgba32_Avx2_Float() - { - ref Vector256 rBase = ref Unsafe.As>(ref this.rFloat[0]); - ref Vector256 gBase = ref Unsafe.As>(ref this.gFloat[0]); - ref Vector256 bBase = ref Unsafe.As>(ref this.bFloat[0]); - ref Vector256 resultBase = ref Unsafe.As>(ref this.rgbaFloat[0]); + [Benchmark(Baseline = true)] + public void Rgba32_Avx2_Float() + { + ref Vector256 rBase = ref Unsafe.As>(ref this.rFloat[0]); + ref Vector256 gBase = ref Unsafe.As>(ref this.gFloat[0]); + ref Vector256 bBase = ref Unsafe.As>(ref this.bFloat[0]); + ref Vector256 resultBase = ref Unsafe.As>(ref this.rgbaFloat[0]); - int count = this.Count / Vector256.Count; + int count = this.Count / Vector256.Count; - ref byte control = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32); - Vector256 vcontrol = Unsafe.As>(ref control); + ref byte control = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32); + Vector256 vcontrol = Unsafe.As>(ref control); - var va = Vector256.Create(1F); + var va = Vector256.Create(1F); - for (int i = 0; i < count; i++) - { - Vector256 r = Unsafe.Add(ref rBase, i); - Vector256 g = Unsafe.Add(ref gBase, i); - Vector256 b = Unsafe.Add(ref bBase, i); + for (int i = 0; i < count; i++) + { + Vector256 r = Unsafe.Add(ref rBase, i); + Vector256 g = Unsafe.Add(ref gBase, i); + Vector256 b = Unsafe.Add(ref bBase, i); - r = Avx2.PermuteVar8x32(r, vcontrol); - g = Avx2.PermuteVar8x32(g, vcontrol); - b = Avx2.PermuteVar8x32(b, vcontrol); + r = Avx2.PermuteVar8x32(r, vcontrol); + g = Avx2.PermuteVar8x32(g, vcontrol); + b = Avx2.PermuteVar8x32(b, vcontrol); - Vector256 vte = Avx.UnpackLow(r, b); - Vector256 vto = Avx.UnpackLow(g, va); + Vector256 vte = Avx.UnpackLow(r, b); + Vector256 vto = Avx.UnpackLow(g, va); - ref Vector256 destination = ref Unsafe.Add(ref resultBase, i * 4); + ref Vector256 destination = ref Unsafe.Add(ref resultBase, i * 4); - destination = Avx.UnpackLow(vte, vto); - Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); + destination = Avx.UnpackLow(vte, vto); + Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); - vte = Avx.UnpackHigh(r, b); - vto = Avx.UnpackHigh(g, va); + vte = Avx.UnpackHigh(r, b); + vto = Avx.UnpackHigh(g, va); - Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); - Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); - } + Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); + Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); } + } - [Benchmark] - public void Rgb24_Avx2_Bytes() - { - ReadOnlySpan r = this.rBuf; - ReadOnlySpan g = this.rBuf; - ReadOnlySpan b = this.rBuf; - Span rgb = this.rgbBuf; - SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb); - } + [Benchmark] + public void Rgb24_Avx2_Bytes() + { + ReadOnlySpan r = this.rBuf; + ReadOnlySpan g = this.rBuf; + ReadOnlySpan b = this.rBuf; + Span rgb = this.rgbBuf; + SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb); + } - [Benchmark] - public void Rgba32_Avx2_Bytes() - { - ReadOnlySpan r = this.rBuf; - ReadOnlySpan g = this.rBuf; - ReadOnlySpan b = this.rBuf; - Span rgb = this.rgbaBuf; - SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb); - } + [Benchmark] + public void Rgba32_Avx2_Bytes() + { + ReadOnlySpan r = this.rBuf; + ReadOnlySpan g = this.rBuf; + ReadOnlySpan b = this.rBuf; + Span rgb = this.rgbaBuf; + SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb); + } #pragma warning disable SA1132 - private struct Byte8 - { - public byte V0, V1, V2, V3, V4, V5, V6, V7; - } + private struct Byte8 + { + public byte V0, V1, V2, V3, V4, V5, V6, V7; + } - private struct Byte4 - { - public byte V0, V1, V2, V3; - } + private struct Byte4 + { + public byte V0, V1, V2, V3; + } #pragma warning restore - // Results @ Anton's PC, 2020 Dec 05 - // .NET Core 3.1.1 - // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // - // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | - // |--------------------------------- |------ |-----------:|---------:|---------:|------:|--------:| - // | Rgb24_Scalar_PerElement_Span | 1024 | 1,634.6 ns | 26.56 ns | 24.84 ns | 3.12 | 0.05 | - // | Rgb24_Scalar_PerElement_Unsafe | 1024 | 1,284.7 ns | 4.70 ns | 4.16 ns | 2.46 | 0.01 | - // | Rgb24_Scalar_PerElement_Batched8 | 1024 | 1,182.3 ns | 5.12 ns | 4.27 ns | 2.26 | 0.01 | - // | Rgb24_Scalar_PerElement_Batched4 | 1024 | 1,146.2 ns | 16.38 ns | 14.52 ns | 2.19 | 0.02 | - // | Rgba32_Avx2_Float | 1024 | 522.7 ns | 1.78 ns | 1.39 ns | 1.00 | 0.00 | - // | Rgb24_Avx2_Bytes | 1024 | 243.3 ns | 1.56 ns | 1.30 ns | 0.47 | 0.00 | - // | Rgba32_Avx2_Bytes | 1024 | 146.0 ns | 2.48 ns | 2.32 ns | 0.28 | 0.01 | - } + // Results @ Anton's PC, 2020 Dec 05 + // .NET Core 3.1.1 + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // + // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | + // |--------------------------------- |------ |-----------:|---------:|---------:|------:|--------:| + // | Rgb24_Scalar_PerElement_Span | 1024 | 1,634.6 ns | 26.56 ns | 24.84 ns | 3.12 | 0.05 | + // | Rgb24_Scalar_PerElement_Unsafe | 1024 | 1,284.7 ns | 4.70 ns | 4.16 ns | 2.46 | 0.01 | + // | Rgb24_Scalar_PerElement_Batched8 | 1024 | 1,182.3 ns | 5.12 ns | 4.27 ns | 2.26 | 0.01 | + // | Rgb24_Scalar_PerElement_Batched4 | 1024 | 1,146.2 ns | 16.38 ns | 14.52 ns | 2.19 | 0.02 | + // | Rgba32_Avx2_Float | 1024 | 522.7 ns | 1.78 ns | 1.39 ns | 1.00 | 0.00 | + // | Rgb24_Avx2_Bytes | 1024 | 243.3 ns | 1.56 ns | 1.30 ns | 0.47 | 0.00 | + // | Rgba32_Avx2_Bytes | 1024 | 146.0 ns | 2.48 ns | 2.32 ns | 0.28 | 0.01 | } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs index 98802e5331..9ae36cc8b7 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs @@ -1,179 +1,177 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +public class PixelConversion_Rgba32_To_Argb32 { - public class PixelConversion_Rgba32_To_Argb32 - { - private Rgba32[] source; + private Rgba32[] source; - private Argb32[] dest; + private Argb32[] dest; - [Params(64)] - public int Count { get; set; } + [Params(64)] + public int Count { get; set; } - [GlobalSetup] - public void Setup() - { - this.source = new Rgba32[this.Count]; - this.dest = new Argb32[this.Count]; - } + [GlobalSetup] + public void Setup() + { + this.source = new Rgba32[this.Count]; + this.dest = new Argb32[this.Count]; + } - [Benchmark(Baseline = true)] - public void Default() - { - ref Rgba32 sBase = ref this.source[0]; - ref Argb32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count; i++) - { - Rgba32 s = Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); - } - } + [Benchmark(Baseline = true)] + public void Default() + { + ref Rgba32 sBase = ref this.source[0]; + ref Argb32 dBase = ref this.dest[0]; - [MethodImpl(MethodImplOptions.NoInlining)] - private static void Default_GenericImpl(ReadOnlySpan source, Span dest) - where TPixel : unmanaged, IPixel + for (int i = 0; i < this.Count; i++) { - ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); - ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < source.Length; i++) - { - Rgba32 s = Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); - } + Rgba32 s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Default_GenericImpl(ReadOnlySpan source, Span dest) + where TPixel : unmanaged, IPixel + { + ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); + ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - [Benchmark] - public void Default_Generic() + for (int i = 0; i < source.Length; i++) { - Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + Rgba32 s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); } + } + + [Benchmark] + public void Default_Generic() + { + Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + } + + [Benchmark] + public void Default_Group2() + { + ref Rgba32 sBase = ref this.source[0]; + ref Argb32 dBase = ref this.dest[0]; - [Benchmark] - public void Default_Group2() + for (int i = 0; i < this.Count; i += 2) { - ref Rgba32 sBase = ref this.source[0]; - ref Argb32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count; i += 2) - { - ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); - Rgba32 s1 = Unsafe.Add(ref s0, 1); - - ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); - d0.FromRgba32(s0); - Unsafe.Add(ref d0, 1).FromRgba32(s1); - } + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + Rgba32 s1 = Unsafe.Add(ref s0, 1); + + ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); + d0.FromRgba32(s0); + Unsafe.Add(ref d0, 1).FromRgba32(s1); } + } + + [Benchmark] + public void Default_Group4() + { + ref Rgba32 sBase = ref this.source[0]; + ref Argb32 dBase = ref this.dest[0]; - [Benchmark] - public void Default_Group4() + for (int i = 0; i < this.Count; i += 4) { - ref Rgba32 sBase = ref this.source[0]; - ref Argb32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count; i += 4) - { - ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); - ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); - ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); - Rgba32 s3 = Unsafe.Add(ref s2, 1); - - ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); - ref Argb32 d1 = ref Unsafe.Add(ref d0, 1); - ref Argb32 d2 = ref Unsafe.Add(ref d1, 1); - - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - Unsafe.Add(ref d2, 1).FromRgba32(s3); - } + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + Rgba32 s3 = Unsafe.Add(ref s2, 1); + + ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); + ref Argb32 d1 = ref Unsafe.Add(ref d0, 1); + ref Argb32 d2 = ref Unsafe.Add(ref d1, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + Unsafe.Add(ref d2, 1).FromRgba32(s3); } + } + + [Benchmark] + public void BitOps() + { + ref uint sBase = ref Unsafe.As(ref this.source[0]); + ref uint dBase = ref Unsafe.As(ref this.dest[0]); - [Benchmark] - public void BitOps() + for (int i = 0; i < this.Count; i++) { - ref uint sBase = ref Unsafe.As(ref this.source[0]); - ref uint dBase = ref Unsafe.As(ref this.dest[0]); - - for (int i = 0; i < this.Count; i++) - { - uint s = Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i) = FromRgba32.ToArgb32(s); - } + uint s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i) = FromRgba32.ToArgb32(s); } + } - [Benchmark] - public void BitOps_GroupAsULong() - { - ref ulong sBase = ref Unsafe.As(ref this.source[0]); - ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + [Benchmark] + public void BitOps_GroupAsULong() + { + ref ulong sBase = ref Unsafe.As(ref this.source[0]); + ref ulong dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 2; i++) - { - ulong s = Unsafe.Add(ref sBase, i); - uint lo = (uint)s; - uint hi = (uint)(s >> 32); - lo = FromRgba32.ToArgb32(lo); - hi = FromRgba32.ToArgb32(hi); + for (int i = 0; i < this.Count / 2; i++) + { + ulong s = Unsafe.Add(ref sBase, i); + uint lo = (uint)s; + uint hi = (uint)(s >> 32); + lo = FromRgba32.ToArgb32(lo); + hi = FromRgba32.ToArgb32(hi); - s = (ulong)(hi << 32) | lo; + s = (ulong)(hi << 32) | lo; - Unsafe.Add(ref dBase, i) = s; - } + Unsafe.Add(ref dBase, i) = s; } + } - public static class FromRgba32 + public static class FromRgba32 + { + /// + /// Converts a packed to . + /// + /// The argb value. + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToArgb32(uint packedRgba) { - /// - /// Converts a packed to . - /// - /// The argb value. - [MethodImpl(InliningOptions.ShortMethod)] - public static uint ToArgb32(uint packedRgba) - { - // packedRgba = [aa bb gg rr] - // ROL(8, packedRgba) = [bb gg rr aa] - return (packedRgba << 8) | (packedRgba >> 24); - } - - /// - /// Converts a packed to . - /// - /// The bgra value. - [MethodImpl(InliningOptions.ShortMethod)] - public static uint ToBgra32(uint packedRgba) - { - // packedRgba = [aa bb gg rr] - // tmp1 = [aa 00 gg 00] - // tmp2 = [00 bb 00 rr] - // tmp3=ROL(16, tmp2) = [00 rr 00 bb] - // tmp1 + tmp3 = [aa rr gg bb] - uint tmp1 = packedRgba & 0xFF00FF00; - uint tmp2 = packedRgba & 0x00FF00FF; - uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); - return tmp1 + tmp3; - } + // packedRgba = [aa bb gg rr] + // ROL(8, packedRgba) = [bb gg rr aa] + return (packedRgba << 8) | (packedRgba >> 24); } - // RESULTS: - // Method | Count | Mean | Error | StdDev | Scaled | - // -------------------- |------ |----------:|----------:|----------:|-------:| - // Default | 64 | 107.33 ns | 1.0633 ns | 0.9426 ns | 1.00 | - // Default_Generic | 64 | 111.15 ns | 0.3789 ns | 0.3544 ns | 1.04 | - // Default_Group2 | 64 | 90.36 ns | 0.7779 ns | 0.6896 ns | 0.84 | - // Default_Group4 | 64 | 82.39 ns | 0.2726 ns | 0.2550 ns | 0.77 | - // BitOps | 64 | 39.25 ns | 0.3266 ns | 0.2895 ns | 0.37 | - // BitOps_GroupAsULong | 64 | 41.80 ns | 0.2227 ns | 0.2083 ns | 0.39 | + /// + /// Converts a packed to . + /// + /// The bgra value. + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToBgra32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // tmp1 = [aa 00 gg 00] + // tmp2 = [00 bb 00 rr] + // tmp3=ROL(16, tmp2) = [00 rr 00 bb] + // tmp1 + tmp3 = [aa rr gg bb] + uint tmp1 = packedRgba & 0xFF00FF00; + uint tmp2 = packedRgba & 0x00FF00FF; + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + return tmp1 + tmp3; + } } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | + // -------------------- |------ |----------:|----------:|----------:|-------:| + // Default | 64 | 107.33 ns | 1.0633 ns | 0.9426 ns | 1.00 | + // Default_Generic | 64 | 111.15 ns | 0.3789 ns | 0.3544 ns | 1.04 | + // Default_Group2 | 64 | 90.36 ns | 0.7779 ns | 0.6896 ns | 0.84 | + // Default_Group4 | 64 | 82.39 ns | 0.2726 ns | 0.2550 ns | 0.77 | + // BitOps | 64 | 39.25 ns | 0.3266 ns | 0.2895 ns | 0.37 | + // BitOps_GroupAsULong | 64 | 41.80 ns | 0.2227 ns | 0.2083 ns | 0.39 | } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs index 580a0fd906..b7b9392379 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -10,389 +9,388 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tuples; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +// [MonoJob] +// [RyuJitX64Job] +public class PixelConversion_Rgba32_To_Bgra32 { - // [MonoJob] - // [RyuJitX64Job] - public class PixelConversion_Rgba32_To_Bgra32 - { - private Rgba32[] source; + private Rgba32[] source; - private Bgra32[] dest; + private Bgra32[] dest; - [StructLayout(LayoutKind.Sequential)] - private struct Tuple4OfUInt32 + [StructLayout(LayoutKind.Sequential)] + private struct Tuple4OfUInt32 + { + private uint v0; + private uint v1; + private uint v2; + private uint v3; + + public void ConvertMe() { - private uint v0; - private uint v1; - private uint v2; - private uint v3; - - public void ConvertMe() - { - this.v0 = FromRgba32.ToBgra32(this.v0); - this.v1 = FromRgba32.ToBgra32(this.v1); - this.v2 = FromRgba32.ToBgra32(this.v2); - this.v3 = FromRgba32.ToBgra32(this.v3); - } + this.v0 = FromRgba32.ToBgra32(this.v0); + this.v1 = FromRgba32.ToBgra32(this.v1); + this.v2 = FromRgba32.ToBgra32(this.v2); + this.v3 = FromRgba32.ToBgra32(this.v3); } + } - [Params(64)] - public int Count { get; set; } + [Params(64)] + public int Count { get; set; } - [GlobalSetup] - public void Setup() - { - this.source = new Rgba32[this.Count]; - this.dest = new Bgra32[this.Count]; - } + [GlobalSetup] + public void Setup() + { + this.source = new Rgba32[this.Count]; + this.dest = new Bgra32[this.Count]; + } - [Benchmark(Baseline = true)] - public void Default() - { - ref Rgba32 sBase = ref this.source[0]; - ref Bgra32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count; i++) - { - ref Rgba32 s = ref Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); - } - } + [Benchmark(Baseline = true)] + public void Default() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; - [MethodImpl(MethodImplOptions.NoInlining)] - private static void Default_GenericImpl(ReadOnlySpan source, Span dest) - where TPixel : unmanaged, IPixel + for (int i = 0; i < this.Count; i++) { - ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); - ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < source.Length; i++) - { - ref Rgba32 s = ref Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); - } + ref Rgba32 s = ref Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); } + } - [Benchmark] - public void Default_Generic() - { - Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); - } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Default_GenericImpl(ReadOnlySpan source, Span dest) + where TPixel : unmanaged, IPixel + { + ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); + ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - [Benchmark] - public void Default_Group2() + for (int i = 0; i < source.Length; i++) { - ref Rgba32 sBase = ref this.source[0]; - ref Bgra32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count; i += 2) - { - ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); - Rgba32 s1 = Unsafe.Add(ref s0, 1); - - ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); - d0.FromRgba32(s0); - Unsafe.Add(ref d0, 1).FromRgba32(s1); - } + ref Rgba32 s = ref Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); } + } + + [Benchmark] + public void Default_Generic() + { + Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + } - [Benchmark] - public void Default_Group4() + [Benchmark] + public void Default_Group2() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i += 2) { - ref Rgba32 sBase = ref this.source[0]; - ref Bgra32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count; i += 4) - { - ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); - ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); - ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); - Rgba32 s3 = Unsafe.Add(ref s2, 1); - - ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); - ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); - ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); - - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - Unsafe.Add(ref d2, 1).FromRgba32(s3); - } + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + Rgba32 s1 = Unsafe.Add(ref s0, 1); + + ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); + d0.FromRgba32(s0); + Unsafe.Add(ref d0, 1).FromRgba32(s1); } + } + + [Benchmark] + public void Default_Group4() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; - [MethodImpl(MethodImplOptions.NoInlining)] - private static void Group4GenericImpl(ReadOnlySpan source, Span dest) - where TPixel : unmanaged, IPixel + for (int i = 0; i < this.Count; i += 4) { - ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); - ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < source.Length; i += 4) - { - ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); - ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); - ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); - Rgba32 s3 = Unsafe.Add(ref s2, 1); - - ref TPixel d0 = ref Unsafe.Add(ref dBase, i); - ref TPixel d1 = ref Unsafe.Add(ref d0, 1); - ref TPixel d2 = ref Unsafe.Add(ref d1, 1); - - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - Unsafe.Add(ref d2, 1).FromRgba32(s3); - } + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + Rgba32 s3 = Unsafe.Add(ref s2, 1); + + ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); + ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); + ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + Unsafe.Add(ref d2, 1).FromRgba32(s3); } + } - // [Benchmark] - public void Default_Group4_Generic() + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Group4GenericImpl(ReadOnlySpan source, Span dest) + where TPixel : unmanaged, IPixel + { + ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); + ref TPixel dBase = ref MemoryMarshal.GetReference(dest); + + for (int i = 0; i < source.Length; i += 4) { - Group4GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + Rgba32 s3 = Unsafe.Add(ref s2, 1); + + ref TPixel d0 = ref Unsafe.Add(ref dBase, i); + ref TPixel d1 = ref Unsafe.Add(ref d0, 1); + ref TPixel d2 = ref Unsafe.Add(ref d1, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + Unsafe.Add(ref d2, 1).FromRgba32(s3); } + } - // [Benchmark] - public void Default_Group8() + // [Benchmark] + public void Default_Group4_Generic() + { + Group4GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + } + + // [Benchmark] + public void Default_Group8() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count / 4; i += 4) { - ref Rgba32 sBase = ref this.source[0]; - ref Bgra32 dBase = ref this.dest[0]; - - for (int i = 0; i < this.Count / 4; i += 4) - { - ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); - ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); - ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); - ref Rgba32 s3 = ref Unsafe.Add(ref s1, 1); - - ref Rgba32 s4 = ref Unsafe.Add(ref s3, 1); - ref Rgba32 s5 = ref Unsafe.Add(ref s4, 1); - ref Rgba32 s6 = ref Unsafe.Add(ref s5, 1); - Rgba32 s7 = Unsafe.Add(ref s6, 1); - - ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); - ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); - ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); - ref Bgra32 d3 = ref Unsafe.Add(ref d2, 1); - ref Bgra32 d4 = ref Unsafe.Add(ref d3, 1); - - ref Bgra32 d5 = ref Unsafe.Add(ref d4, 1); - ref Bgra32 d6 = ref Unsafe.Add(ref d5, 1); - - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - d3.FromRgba32(s3); - - d4.FromRgba32(s4); - d5.FromRgba32(s5); - d6.FromRgba32(s6); - Unsafe.Add(ref d6, 1).FromRgba32(s7); - } + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + ref Rgba32 s3 = ref Unsafe.Add(ref s1, 1); + + ref Rgba32 s4 = ref Unsafe.Add(ref s3, 1); + ref Rgba32 s5 = ref Unsafe.Add(ref s4, 1); + ref Rgba32 s6 = ref Unsafe.Add(ref s5, 1); + Rgba32 s7 = Unsafe.Add(ref s6, 1); + + ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); + ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); + ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); + ref Bgra32 d3 = ref Unsafe.Add(ref d2, 1); + ref Bgra32 d4 = ref Unsafe.Add(ref d3, 1); + + ref Bgra32 d5 = ref Unsafe.Add(ref d4, 1); + ref Bgra32 d6 = ref Unsafe.Add(ref d5, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + d3.FromRgba32(s3); + + d4.FromRgba32(s4); + d5.FromRgba32(s5); + d6.FromRgba32(s6); + Unsafe.Add(ref d6, 1).FromRgba32(s7); } + } + + [Benchmark] + public void BitOps() + { + ref uint sBase = ref Unsafe.As(ref this.source[0]); + ref uint dBase = ref Unsafe.As(ref this.dest[0]); - [Benchmark] - public void BitOps() + for (int i = 0; i < this.Count; i++) { - ref uint sBase = ref Unsafe.As(ref this.source[0]); - ref uint dBase = ref Unsafe.As(ref this.dest[0]); - - for (int i = 0; i < this.Count; i++) - { - uint s = Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i) = FromRgba32.ToBgra32(s); - } + uint s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i) = FromRgba32.ToBgra32(s); } + } - [Benchmark] - public void Bitops_Tuple() + [Benchmark] + public void Bitops_Tuple() + { + ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); + ref Tuple4OfUInt32 dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count / 4; i++) { - ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); - ref Tuple4OfUInt32 dBase = ref Unsafe.As(ref this.dest[0]); - - for (int i = 0; i < this.Count / 4; i++) - { - ref Tuple4OfUInt32 d = ref Unsafe.Add(ref dBase, i); - d = Unsafe.Add(ref sBase, i); - d.ConvertMe(); - } + ref Tuple4OfUInt32 d = ref Unsafe.Add(ref dBase, i); + d = Unsafe.Add(ref sBase, i); + d.ConvertMe(); } + } - // [Benchmark] - public void Bitops_SingleTuple() - { - ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); + // [Benchmark] + public void Bitops_SingleTuple() + { + ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); - for (int i = 0; i < this.Count / 4; i++) - { - Unsafe.Add(ref sBase, i).ConvertMe(); - } + for (int i = 0; i < this.Count / 4; i++) + { + Unsafe.Add(ref sBase, i).ConvertMe(); } + } - // [Benchmark] - public void Bitops_Simd() - { - ref Octet sBase = ref Unsafe.As>(ref this.source[0]); - ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); + // [Benchmark] + public void Bitops_Simd() + { + ref Octet sBase = ref Unsafe.As>(ref this.source[0]); + ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); - for (int i = 0; i < this.Count / 8; i++) - { - BitopsSimdImpl(ref Unsafe.Add(ref sBase, i), ref Unsafe.Add(ref dBase, i)); - } + for (int i = 0; i < this.Count / 8; i++) + { + BitopsSimdImpl(ref Unsafe.Add(ref sBase, i), ref Unsafe.Add(ref dBase, i)); } + } #pragma warning disable SA1132 // Do not combine fields - [StructLayout(LayoutKind.Sequential)] - private struct B - { - public uint Tmp2, Tmp5, Tmp8, Tmp11, Tmp14, Tmp17, Tmp20, Tmp23; - } + [StructLayout(LayoutKind.Sequential)] + private struct B + { + public uint Tmp2, Tmp5, Tmp8, Tmp11, Tmp14, Tmp17, Tmp20, Tmp23; + } - [StructLayout(LayoutKind.Sequential)] - private struct C - { - public uint Tmp3, Tmp6, Tmp9, Tmp12, Tmp15, Tmp18, Tmp21, Tmp24; - } + [StructLayout(LayoutKind.Sequential)] + private struct C + { + public uint Tmp3, Tmp6, Tmp9, Tmp12, Tmp15, Tmp18, Tmp21, Tmp24; + } #pragma warning restore SA1132 // Do not combine fields - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void BitopsSimdImpl(ref Octet s, ref Octet d) - { - Vector sVec = Unsafe.As, Vector>(ref s); - var aMask = new Vector(0xFF00FF00); - var bMask = new Vector(0x00FF00FF); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BitopsSimdImpl(ref Octet s, ref Octet d) + { + Vector sVec = Unsafe.As, Vector>(ref s); + var aMask = new Vector(0xFF00FF00); + var bMask = new Vector(0x00FF00FF); - Vector aa = sVec & aMask; - Vector bb = sVec & bMask; + Vector aa = sVec & aMask; + Vector bb = sVec & bMask; - B b = Unsafe.As, B>(ref bb); + B b = Unsafe.As, B>(ref bb); - C c = default; + C c = default; - c.Tmp3 = (b.Tmp2 << 16) | (b.Tmp2 >> 16); - c.Tmp6 = (b.Tmp5 << 16) | (b.Tmp5 >> 16); - c.Tmp9 = (b.Tmp8 << 16) | (b.Tmp8 >> 16); - c.Tmp12 = (b.Tmp11 << 16) | (b.Tmp11 >> 16); - c.Tmp15 = (b.Tmp14 << 16) | (b.Tmp14 >> 16); - c.Tmp18 = (b.Tmp17 << 16) | (b.Tmp17 >> 16); - c.Tmp21 = (b.Tmp20 << 16) | (b.Tmp20 >> 16); - c.Tmp24 = (b.Tmp23 << 16) | (b.Tmp23 >> 16); + c.Tmp3 = (b.Tmp2 << 16) | (b.Tmp2 >> 16); + c.Tmp6 = (b.Tmp5 << 16) | (b.Tmp5 >> 16); + c.Tmp9 = (b.Tmp8 << 16) | (b.Tmp8 >> 16); + c.Tmp12 = (b.Tmp11 << 16) | (b.Tmp11 >> 16); + c.Tmp15 = (b.Tmp14 << 16) | (b.Tmp14 >> 16); + c.Tmp18 = (b.Tmp17 << 16) | (b.Tmp17 >> 16); + c.Tmp21 = (b.Tmp20 << 16) | (b.Tmp20 >> 16); + c.Tmp24 = (b.Tmp23 << 16) | (b.Tmp23 >> 16); - Vector cc = Unsafe.As>(ref c); - Vector dd = aa + cc; + Vector cc = Unsafe.As>(ref c); + Vector dd = aa + cc; - d = Unsafe.As, Octet>(ref dd); - } + d = Unsafe.As, Octet>(ref dd); + } + + // [Benchmark] + public void BitOps_Group2() + { + ref uint sBase = ref Unsafe.As(ref this.source[0]); + ref uint dBase = ref Unsafe.As(ref this.dest[0]); - // [Benchmark] - public void BitOps_Group2() + for (int i = 0; i < this.Count; i++) { - ref uint sBase = ref Unsafe.As(ref this.source[0]); - ref uint dBase = ref Unsafe.As(ref this.dest[0]); - - for (int i = 0; i < this.Count; i++) - { - ref uint s0 = ref Unsafe.Add(ref sBase, i); - uint s1 = Unsafe.Add(ref s0, 1); - - ref uint d0 = ref Unsafe.Add(ref dBase, i); - d0 = FromRgba32.ToBgra32(s0); - Unsafe.Add(ref d0, 1) = FromRgba32.ToBgra32(s1); - } + ref uint s0 = ref Unsafe.Add(ref sBase, i); + uint s1 = Unsafe.Add(ref s0, 1); + + ref uint d0 = ref Unsafe.Add(ref dBase, i); + d0 = FromRgba32.ToBgra32(s0); + Unsafe.Add(ref d0, 1) = FromRgba32.ToBgra32(s1); } + } - [Benchmark] - public void BitOps_GroupAsULong() - { - ref ulong sBase = ref Unsafe.As(ref this.source[0]); - ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + [Benchmark] + public void BitOps_GroupAsULong() + { + ref ulong sBase = ref Unsafe.As(ref this.source[0]); + ref ulong dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 2; i++) - { - ulong s = Unsafe.Add(ref sBase, i); - uint lo = (uint)s; - uint hi = (uint)(s >> 32); - lo = FromRgba32.ToBgra32(lo); - hi = FromRgba32.ToBgra32(hi); + for (int i = 0; i < this.Count / 2; i++) + { + ulong s = Unsafe.Add(ref sBase, i); + uint lo = (uint)s; + uint hi = (uint)(s >> 32); + lo = FromRgba32.ToBgra32(lo); + hi = FromRgba32.ToBgra32(hi); - s = (ulong)(hi << 32) | lo; + s = (ulong)(hi << 32) | lo; - Unsafe.Add(ref dBase, i) = s; - } + Unsafe.Add(ref dBase, i) = s; } + } - // [Benchmark] - public void BitOps_GroupAsULong_V2() - { - ref ulong sBase = ref Unsafe.As(ref this.source[0]); - ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + // [Benchmark] + public void BitOps_GroupAsULong_V2() + { + ref ulong sBase = ref Unsafe.As(ref this.source[0]); + ref ulong dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 2; i++) - { - ulong s = Unsafe.Add(ref sBase, i); - uint lo = (uint)s; - uint hi = (uint)(s >> 32); + for (int i = 0; i < this.Count / 2; i++) + { + ulong s = Unsafe.Add(ref sBase, i); + uint lo = (uint)s; + uint hi = (uint)(s >> 32); - uint tmp1 = lo & 0xFF00FF00; - uint tmp4 = hi & 0xFF00FF00; + uint tmp1 = lo & 0xFF00FF00; + uint tmp4 = hi & 0xFF00FF00; - uint tmp2 = lo & 0x00FF00FF; - uint tmp5 = hi & 0x00FF00FF; + uint tmp2 = lo & 0x00FF00FF; + uint tmp5 = hi & 0x00FF00FF; - uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); - uint tmp6 = (tmp5 << 16) | (tmp5 >> 16); + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + uint tmp6 = (tmp5 << 16) | (tmp5 >> 16); - lo = tmp1 + tmp3; - hi = tmp4 + tmp6; + lo = tmp1 + tmp3; + hi = tmp4 + tmp6; - s = (ulong)(hi << 32) | lo; + s = (ulong)(hi << 32) | lo; - Unsafe.Add(ref dBase, i) = s; - } + Unsafe.Add(ref dBase, i) = s; } + } - public static class FromRgba32 + public static class FromRgba32 + { + /// + /// Converts a packed to . + /// + /// The argb value. + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToArgb32(uint packedRgba) { - /// - /// Converts a packed to . - /// - /// The argb value. - [MethodImpl(InliningOptions.ShortMethod)] - public static uint ToArgb32(uint packedRgba) - { - // packedRgba = [aa bb gg rr] - // ROL(8, packedRgba) = [bb gg rr aa] - return (packedRgba << 8) | (packedRgba >> 24); - } - - /// - /// Converts a packed to . - /// - /// The bgra value. - [MethodImpl(InliningOptions.ShortMethod)] - public static uint ToBgra32(uint packedRgba) - { - // packedRgba = [aa bb gg rr] - // tmp1 = [aa 00 gg 00] - // tmp2 = [00 bb 00 rr] - // tmp3=ROL(16, tmp2) = [00 rr 00 bb] - // tmp1 + tmp3 = [aa rr gg bb] - uint tmp1 = packedRgba & 0xFF00FF00; - uint tmp2 = packedRgba & 0x00FF00FF; - uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); - return tmp1 + tmp3; - } + // packedRgba = [aa bb gg rr] + // ROL(8, packedRgba) = [bb gg rr aa] + return (packedRgba << 8) | (packedRgba >> 24); } - // RESULTS: - // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | - // -------------------- |------ |---------:|----------:|----------:|-------:|---------:| - // Default | 64 | 82.67 ns | 0.6737 ns | 0.5625 ns | 1.00 | 0.00 | - // Default_Generic | 64 | 88.73 ns | 1.7959 ns | 1.7638 ns | 1.07 | 0.02 | - // Default_Group2 | 64 | 91.03 ns | 1.5237 ns | 1.3508 ns | 1.10 | 0.02 | - // Default_Group4 | 64 | 86.62 ns | 1.5737 ns | 1.4720 ns | 1.05 | 0.02 | - // BitOps | 64 | 57.45 ns | 0.6067 ns | 0.5066 ns | 0.69 | 0.01 | - // Bitops_Tuple | 64 | 75.47 ns | 1.1824 ns | 1.1060 ns | 0.91 | 0.01 | - // BitOps_GroupAsULong | 64 | 65.42 ns | 0.7157 ns | 0.6695 ns | 0.79 | 0.01 | + /// + /// Converts a packed to . + /// + /// The bgra value. + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToBgra32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // tmp1 = [aa 00 gg 00] + // tmp2 = [00 bb 00 rr] + // tmp3=ROL(16, tmp2) = [00 rr 00 bb] + // tmp1 + tmp3 = [aa rr gg bb] + uint tmp1 = packedRgba & 0xFF00FF00; + uint tmp2 = packedRgba & 0x00FF00FF; + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + return tmp1 + tmp3; + } } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + // -------------------- |------ |---------:|----------:|----------:|-------:|---------:| + // Default | 64 | 82.67 ns | 0.6737 ns | 0.5625 ns | 1.00 | 0.00 | + // Default_Generic | 64 | 88.73 ns | 1.7959 ns | 1.7638 ns | 1.07 | 0.02 | + // Default_Group2 | 64 | 91.03 ns | 1.5237 ns | 1.3508 ns | 1.10 | 0.02 | + // Default_Group4 | 64 | 86.62 ns | 1.5737 ns | 1.4720 ns | 1.05 | 0.02 | + // BitOps | 64 | 57.45 ns | 0.6067 ns | 0.5066 ns | 0.69 | 0.01 | + // Bitops_Tuple | 64 | 75.47 ns | 1.1824 ns | 1.1060 ns | 0.91 | 0.01 | + // BitOps_GroupAsULong | 64 | 65.42 ns | 0.7157 ns | 0.6695 ns | 0.79 | 0.01 | } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs index 6800788031..84698a0e19 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs @@ -6,89 +6,88 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +[StructLayout(LayoutKind.Sequential)] +public struct TestArgb : ITestPixel { - [StructLayout(LayoutKind.Sequential)] - public struct TestArgb : ITestPixel - { - public byte A; - public byte R; - public byte G; - public byte B; + public byte A; + public byte R; + public byte G; + public byte B; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 p) - { - this.R = p.R; - this.G = p.G; - this.B = p.B; - this.A = p.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 p) + { + this.R = p.R; + this.G = p.G; + this.B = p.B; + this.A = p.A; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(ref Rgba32 p) - { - this.R = p.R; - this.G = p.G; - this.B = p.B; - this.A = p.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 p) + { + this.R = p.R; + this.G = p.G; + this.B = p.B; + this.A = p.A; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBytes(byte r, byte g, byte b, byte a) - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBytes(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) - { - this.R = (byte)p.X; - this.G = (byte)p.Y; - this.B = (byte)p.Z; - this.A = (byte)p.W; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 p) + { + this.R = (byte)p.X; + this.G = (byte)p.Y; + this.B = (byte)p.Z; + this.A = (byte)p.W; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) - { - this.R = (byte)p.X; - this.G = (byte)p.Y; - this.B = (byte)p.Z; - this.A = (byte)p.W; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 p) + { + this.R = (byte)p.X; + this.G = (byte)p.Y; + this.B = (byte)p.Z; + this.A = (byte)p.W; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() - { - return new Rgba32(this.R, this.G, this.B, this.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() + { + return new Rgba32(this.R, this.G, this.B, this.A); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToRgba32(ref Rgba32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToRgba32(ref Rgba32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = this.A; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R, this.G, this.B, this.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return new Vector4(this.R, this.G, this.B, this.A); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) - { - dest.X = this.R; - dest.Y = this.G; - dest.Z = this.B; - dest.W = this.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToVector4(ref Vector4 dest) + { + dest.X = this.R; + dest.Y = this.G; + dest.Z = this.B; + dest.W = this.A; } } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs index a739635899..76449c9d95 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs @@ -6,71 +6,70 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +[StructLayout(LayoutKind.Sequential)] +public struct TestRgba : ITestPixel { - [StructLayout(LayoutKind.Sequential)] - public struct TestRgba : ITestPixel - { - public byte R; - public byte G; - public byte B; - public byte A; + public byte R; + public byte G; + public byte B; + public byte A; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) - { - this = Unsafe.As(ref source); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) + { + this = Unsafe.As(ref source); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(ref Rgba32 source) - { - this = Unsafe.As(ref source); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 source) + { + this = Unsafe.As(ref source); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBytes(byte r, byte g, byte b, byte a) - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBytes(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } - public void FromVector4(Vector4 source) - { - throw new System.NotImplementedException(); - } + public void FromVector4(Vector4 source) + { + throw new System.NotImplementedException(); + } - public void FromVector4(ref Vector4 source) - { - throw new System.NotImplementedException(); - } + public void FromVector4(ref Vector4 source) + { + throw new System.NotImplementedException(); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() - { - return Unsafe.As(ref this); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() + { + return Unsafe.As(ref this); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToRgba32(ref Rgba32 dest) - { - dest = Unsafe.As(ref this); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToRgba32(ref Rgba32 dest) + { + dest = Unsafe.As(ref this); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R, this.G, this.B, this.A) * new Vector4(1f / 255f); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return new Vector4(this.R, this.G, this.B, this.A) * new Vector4(1f / 255f); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) - { - var tmp = new Vector4(this.R, this.G, this.B, this.A); - tmp *= new Vector4(1f / 255f); - dest = tmp; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToVector4(ref Vector4 dest) + { + var tmp = new Vector4(this.R, this.G, this.B, this.A); + tmp *= new Vector4(1f / 255f); + dest = tmp; } } diff --git a/tests/ImageSharp.Benchmarks/General/StructCasting.cs b/tests/ImageSharp.Benchmarks/General/StructCasting.cs index fbb5cd566c..f8432112e3 100644 --- a/tests/ImageSharp.Benchmarks/General/StructCasting.cs +++ b/tests/ImageSharp.Benchmarks/General/StructCasting.cs @@ -4,28 +4,27 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General; + +public class StructCasting { - public class StructCasting + [Benchmark(Baseline = true)] + public short ExplicitCast() { - [Benchmark(Baseline = true)] - public short ExplicitCast() - { - int x = 5 * 2; - return (short)x; - } + int x = 5 * 2; + return (short)x; + } - [Benchmark] - public short UnsafeCast() - { - int x = 5 * 2; - return Unsafe.As(ref x); - } + [Benchmark] + public short UnsafeCast() + { + int x = 5 * 2; + return Unsafe.As(ref x); + } - [Benchmark] - public short UnsafeCastRef() - { - return Unsafe.As(ref Unsafe.AsRef(5 * 2)); - } + [Benchmark] + public short UnsafeCastRef() + { + return Unsafe.As(ref Unsafe.AsRef(5 * 2)); } } diff --git a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs index 921625bbd6..2cd6a5a52a 100644 --- a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs +++ b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs @@ -1,62 +1,60 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General; + +/// +/// Has it any effect on performance to store SIMD constants as static readonly fields? Is it OK to always inline them? +/// Spoiler: the difference seems to be statistically insignificant! +/// +public class Vector4Constants { - /// - /// Has it any effect on performance to store SIMD constants as static readonly fields? Is it OK to always inline them? - /// Spoiler: the difference seems to be statistically insignificant! - /// - public class Vector4Constants + private static readonly Vector4 A = new Vector4(1.2f); + private static readonly Vector4 B = new Vector4(3.4f); + private static readonly Vector4 C = new Vector4(5.6f); + private static readonly Vector4 D = new Vector4(7.8f); + + private Random random; + + private Vector4 parameter; + + [GlobalSetup] + public void Setup() { - private static readonly Vector4 A = new Vector4(1.2f); - private static readonly Vector4 B = new Vector4(3.4f); - private static readonly Vector4 C = new Vector4(5.6f); - private static readonly Vector4 D = new Vector4(7.8f); - - private Random random; - - private Vector4 parameter; - - [GlobalSetup] - public void Setup() - { - this.random = new Random(42); - this.parameter = new Vector4( - this.GetRandomFloat(), - this.GetRandomFloat(), - this.GetRandomFloat(), - this.GetRandomFloat()); - } - - [Benchmark(Baseline = true)] - public Vector4 Static() - { - Vector4 p = this.parameter; - - Vector4 x = (p * A / B) + (p * C / D); - Vector4 y = (p / A * B) + (p / C * D); - var z = Vector4.Min(p, A); - var w = Vector4.Max(p, B); - return x + y + z + w; - } - - [Benchmark] - public Vector4 Inlined() - { - Vector4 p = this.parameter; - - Vector4 x = (p * new Vector4(1.2f) / new Vector4(2.3f)) + (p * new Vector4(4.5f) / new Vector4(6.7f)); - Vector4 y = (p / new Vector4(1.2f) * new Vector4(2.3f)) + (p / new Vector4(4.5f) * new Vector4(6.7f)); - var z = Vector4.Min(p, new Vector4(1.2f)); - var w = Vector4.Max(p, new Vector4(2.3f)); - return x + y + z + w; - } - - private float GetRandomFloat() => (float)this.random.NextDouble(); + this.random = new Random(42); + this.parameter = new Vector4( + this.GetRandomFloat(), + this.GetRandomFloat(), + this.GetRandomFloat(), + this.GetRandomFloat()); } + + [Benchmark(Baseline = true)] + public Vector4 Static() + { + Vector4 p = this.parameter; + + Vector4 x = (p * A / B) + (p * C / D); + Vector4 y = (p / A * B) + (p / C * D); + var z = Vector4.Min(p, A); + var w = Vector4.Max(p, B); + return x + y + z + w; + } + + [Benchmark] + public Vector4 Inlined() + { + Vector4 p = this.parameter; + + Vector4 x = (p * new Vector4(1.2f) / new Vector4(2.3f)) + (p * new Vector4(4.5f) / new Vector4(6.7f)); + Vector4 y = (p / new Vector4(1.2f) * new Vector4(2.3f)) + (p / new Vector4(4.5f) * new Vector4(6.7f)); + var z = Vector4.Min(p, new Vector4(1.2f)); + var w = Vector4.Max(p, new Vector4(2.3f)); + return x + y + z + w; + } + + private float GetRandomFloat() => (float)this.random.NextDouble(); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs index 7b74f346b9..4d8d9b1ed6 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -4,53 +4,52 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class BitwiseOrUInt32 { - public class BitwiseOrUInt32 - { - private uint[] input; + private uint[] input; - private uint[] result; + private uint[] result; - [Params(32)] - public int InputSize { get; set; } + [Params(32)] + public int InputSize { get; set; } - private uint testValue; + private uint testValue; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new uint[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) { - this.input = new uint[this.InputSize]; - this.result = new uint[this.InputSize]; - this.testValue = 42; - - for (int i = 0; i < this.InputSize; i++) - { - this.input[i] = (uint)i; - } + this.input[i] = (uint)i; } + } - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] | v; - } + this.result[i] = this.input[i] | v; } + } + + [Benchmark] + public void Simd() + { + var v = new Vector(this.testValue); - [Benchmark] - public void Simd() + for (int i = 0; i < this.input.Length; i += Vector.Count) { - var v = new Vector(this.testValue); - - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); - a = Vector.BitwiseOr(a, v); - a.CopyTo(this.result, i); - } + var a = new Vector(this.input, i); + a = Vector.BitwiseOr(a, v); + a.CopyTo(this.result, i); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs index 4abeac0d73..1b2c56ab97 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -4,53 +4,52 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class DivFloat { - public class DivFloat - { - private float[] input; + private float[] input; - private float[] result; + private float[] result; - [Params(32)] - public int InputSize { get; set; } + [Params(32)] + public int InputSize { get; set; } - private float testValue; + private float testValue; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + this.input = new float[this.InputSize]; + this.result = new float[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) { - this.input = new float[this.InputSize]; - this.result = new float[this.InputSize]; - this.testValue = 42; - - for (int i = 0; i < this.InputSize; i++) - { - this.input[i] = (uint)i; - } + this.input[i] = (uint)i; } + } - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - float v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] / v; - } + this.result[i] = this.input[i] / v; } + } + + [Benchmark] + public void Simd() + { + var v = new Vector(this.testValue); - [Benchmark] - public void Simd() + for (int i = 0; i < this.input.Length; i += Vector.Count) { - var v = new Vector(this.testValue); - - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); - a = a / v; - a.CopyTo(this.result, i); - } + var a = new Vector(this.input, i); + a = a / v; + a.CopyTo(this.result, i); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs index 83b56199ea..d102164e2c 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -4,55 +4,54 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class DivUInt32 { - public class DivUInt32 - { - private uint[] input; + private uint[] input; + + private uint[] result; - private uint[] result; + [Params(32)] + public int InputSize { get; set; } - [Params(32)] - public int InputSize { get; set; } + private uint testValue; - private uint testValue; + [GlobalSetup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new uint[this.InputSize]; + this.testValue = 42; - [GlobalSetup] - public void Setup() + for (int i = 0; i < this.InputSize; i++) { - this.input = new uint[this.InputSize]; - this.result = new uint[this.InputSize]; - this.testValue = 42; - - for (int i = 0; i < this.InputSize; i++) - { - this.input[i] = (uint)i; - } + this.input[i] = (uint)i; } + } - [Benchmark(Baseline = true)] - public void Standard() - { - uint v = this.testValue; + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] / v; - } + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; } + } - [Benchmark] - public void Simd() - { - var v = new Vector(this.testValue); + [Benchmark] + public void Simd() + { + var v = new Vector(this.testValue); - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + var a = new Vector(this.input, i); - a = a / v; - a.CopyTo(this.result, i); - } + a = a / v; + a.CopyTo(this.result, i); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs index 5b5904a773..9c95c22e0f 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs @@ -4,69 +4,68 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace ImageSharp.Benchmarks.General.Vectorization -{ +namespace ImageSharp.Benchmarks.General.Vectorization; + #pragma warning disable SA1649 // File name should match first type name - public class DivFloat : SIMDBenchmarkBase.Divide +public class DivFloat : SIMDBenchmarkBase.Divide #pragma warning restore SA1649 // File name should match first type name - { - protected override float GetTestValue() => 42; +{ + protected override float GetTestValue() => 42; - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - float v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] / v; - } + this.result[i] = this.input[i] / v; } } +} - public class Divide : SIMDBenchmarkBase.Divide - { - protected override uint GetTestValue() => 42; +public class Divide : SIMDBenchmarkBase.Divide +{ + protected override uint GetTestValue() => 42; - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] / v; - } + this.result[i] = this.input[i] / v; } } +} - public class DivInt32 : SIMDBenchmarkBase.Divide - { - protected override int GetTestValue() => 42; +public class DivInt32 : SIMDBenchmarkBase.Divide +{ + protected override int GetTestValue() => 42; - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + int v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - int v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] / v; - } + this.result[i] = this.input[i] / v; } } +} - public class DivInt16 : SIMDBenchmarkBase.Divide - { - protected override short GetTestValue() => 42; +public class DivInt16 : SIMDBenchmarkBase.Divide +{ + protected override short GetTestValue() => 42; - protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); + protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + short v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - short v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = (short)(this.input[i] / v); - } + this.result[i] = (short)(this.input[i] / v); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index 7c24c717eb..a2eb8d417a 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -4,66 +4,65 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class MulFloat { - public class MulFloat - { - private float[] input; + private float[] input; - private float[] result; + private float[] result; - [Params(32)] - public int InputSize { get; set; } + [Params(32)] + public int InputSize { get; set; } - private float testValue; + private float testValue; - [GlobalSetup] - public void Setup() - { - this.input = new float[this.InputSize]; - this.result = new float[this.InputSize]; - this.testValue = 42; + [GlobalSetup] + public void Setup() + { + this.input = new float[this.InputSize]; + this.result = new float[this.InputSize]; + this.testValue = 42; - for (int i = 0; i < this.InputSize; i++) - { - this.input[i] = i; - } + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = i; } + } - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - float v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] * v; - } + this.result[i] = this.input[i] * v; } + } - [Benchmark] - public void SimdMultiplyByVector() - { - var v = new Vector(this.testValue); + [Benchmark] + public void SimdMultiplyByVector() + { + var v = new Vector(this.testValue); - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); - a = a * v; - a.CopyTo(this.result, i); - } + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + var a = new Vector(this.input, i); + a = a * v; + a.CopyTo(this.result, i); } + } - [Benchmark] - public void SimdMultiplyByScalar() - { - float v = this.testValue; + [Benchmark] + public void SimdMultiplyByScalar() + { + float v = this.testValue; - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); - a = a * v; - a.CopyTo(this.result, i); - } + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + var a = new Vector(this.input, i); + a = a * v; + a.CopyTo(this.result, i); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs index eebfc1ab7b..a234970a51 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -4,53 +4,52 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class MulUInt32 { - public class MulUInt32 - { - private uint[] input; + private uint[] input; - private uint[] result; + private uint[] result; - [Params(32)] - public int InputSize { get; set; } + [Params(32)] + public int InputSize { get; set; } - private uint testValue; + private uint testValue; - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new uint[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) { - this.input = new uint[this.InputSize]; - this.result = new uint[this.InputSize]; - this.testValue = 42; - - for (int i = 0; i < this.InputSize; i++) - { - this.input[i] = (uint)i; - } + this.input[i] = (uint)i; } + } - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] * v; - } + this.result[i] = this.input[i] * v; } + } + + [Benchmark] + public void Simd() + { + var v = new Vector(this.testValue); - [Benchmark] - public void Simd() + for (int i = 0; i < this.input.Length; i += Vector.Count) { - var v = new Vector(this.testValue); - - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); - a = a * v; - a.CopyTo(this.result, i); - } + var a = new Vector(this.input, i); + a = a * v; + a.CopyTo(this.result, i); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs index 423a62bc6c..fe48c3301b 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs @@ -4,52 +4,51 @@ using System.Numerics; using BenchmarkDotNet.Attributes; -namespace ImageSharp.Benchmarks.General.Vectorization -{ +namespace ImageSharp.Benchmarks.General.Vectorization; + #pragma warning disable SA1649 // File name should match first type name - public class MulUInt32 : SIMDBenchmarkBase.Multiply +public class MulUInt32 : SIMDBenchmarkBase.Multiply #pragma warning restore SA1649 // File name should match first type name - { - protected override uint GetTestValue() => 42u; +{ + protected override uint GetTestValue() => 42u; - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] * v; - } + this.result[i] = this.input[i] * v; } } +} - public class MulInt32 : SIMDBenchmarkBase.Multiply +public class MulInt32 : SIMDBenchmarkBase.Multiply +{ + [Benchmark(Baseline = true)] + public void Standard() { - [Benchmark(Baseline = true)] - public void Standard() + int v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - int v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = this.input[i] * v; - } + this.result[i] = this.input[i] * v; } } +} - public class MulInt16 : SIMDBenchmarkBase.Multiply - { - protected override short GetTestValue() => 42; +public class MulInt16 : SIMDBenchmarkBase.Multiply +{ + protected override short GetTestValue() => 42; - protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); + protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + short v = this.testValue; + for (int i = 0; i < this.input.Length; i++) { - short v = this.testValue; - for (int i = 0; i < this.input.Length; i++) - { - this.result[i] = (short)(this.input[i] * v); - } + this.result[i] = (short)(this.input[i] * v); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs index bbb0b6bde9..29b90accc5 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs @@ -5,58 +5,57 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class Premultiply { - public class Premultiply + [Benchmark(Baseline = true)] + public Vector4 PremultiplyByVal() + { + var input = new Vector4(.5F); + return Vector4Utils.Premultiply(input); + } + + [Benchmark] + public Vector4 PremultiplyByRef() + { + var input = new Vector4(.5F); + Vector4Utils.PremultiplyRef(ref input); + return input; + } + + [Benchmark] + public Vector4 PremultiplyRefWithPropertyAssign() + { + var input = new Vector4(.5F); + Vector4Utils.PremultiplyRefWithPropertyAssign(ref input); + return input; + } +} + +internal static class Vector4Utils +{ + [MethodImpl(InliningOptions.ShortMethod)] + public static Vector4 Premultiply(Vector4 source) + { + float w = source.W; + Vector4 premultiplied = source * w; + premultiplied.W = w; + return premultiplied; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void PremultiplyRef(ref Vector4 source) { - [Benchmark(Baseline = true)] - public Vector4 PremultiplyByVal() - { - var input = new Vector4(.5F); - return Vector4Utils.Premultiply(input); - } - - [Benchmark] - public Vector4 PremultiplyByRef() - { - var input = new Vector4(.5F); - Vector4Utils.PremultiplyRef(ref input); - return input; - } - - [Benchmark] - public Vector4 PremultiplyRefWithPropertyAssign() - { - var input = new Vector4(.5F); - Vector4Utils.PremultiplyRefWithPropertyAssign(ref input); - return input; - } + float w = source.W; + source *= w; + source.W = w; } - internal static class Vector4Utils + [MethodImpl(InliningOptions.ShortMethod)] + public static void PremultiplyRefWithPropertyAssign(ref Vector4 source) { - [MethodImpl(InliningOptions.ShortMethod)] - public static Vector4 Premultiply(Vector4 source) - { - float w = source.W; - Vector4 premultiplied = source * w; - premultiplied.W = w; - return premultiplied; - } - - [MethodImpl(InliningOptions.ShortMethod)] - public static void PremultiplyRef(ref Vector4 source) - { - float w = source.W; - source *= w; - source.W = w; - } - - [MethodImpl(InliningOptions.ShortMethod)] - public static void PremultiplyRefWithPropertyAssign(ref Vector4 source) - { - float w = source.W; - source *= new Vector4(w) { W = 1 }; - } + float w = source.W; + source *= new Vector4(w) { W = 1 }; } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs index 584e29df27..7d626d7856 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -5,58 +5,57 @@ using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +public class ReinterpretUInt32AsFloat { - public class ReinterpretUInt32AsFloat - { - private uint[] input; + private uint[] input; - private float[] result; + private float[] result; - [Params(32)] - public int InputSize { get; set; } + [Params(32)] + public int InputSize { get; set; } - [StructLayout(LayoutKind.Explicit)] - private struct UIntFloatUnion - { - [FieldOffset(0)] - public float F; + [StructLayout(LayoutKind.Explicit)] + private struct UIntFloatUnion + { + [FieldOffset(0)] + public float F; - [FieldOffset(0)] - public uint I; - } + [FieldOffset(0)] + public uint I; + } - [GlobalSetup] - public void Setup() + [GlobalSetup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new float[this.InputSize]; + for (int i = 0; i < this.InputSize; i++) { - this.input = new uint[this.InputSize]; - this.result = new float[this.InputSize]; - for (int i = 0; i < this.InputSize; i++) - { - this.input[i] = (uint)i; - } + this.input[i] = (uint)i; } + } - [Benchmark(Baseline = true)] - public void Standard() + [Benchmark(Baseline = true)] + public void Standard() + { + UIntFloatUnion u = default; + for (int i = 0; i < this.input.Length; i++) { - UIntFloatUnion u = default; - for (int i = 0; i < this.input.Length; i++) - { - u.I = this.input[i]; - this.result[i] = u.F; - } + u.I = this.input[i]; + this.result[i] = u.F; } + } - [Benchmark] - public void Simd() + [Benchmark] + public void Simd() + { + for (int i = 0; i < this.input.Length; i += Vector.Count) { - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - var a = new Vector(this.input, i); - var b = Vector.AsVectorSingle(a); - b.CopyTo(this.result, i); - } + var a = new Vector(this.input, i); + var b = Vector.AsVectorSingle(a); + b.CopyTo(this.result, i); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs index 4c92895e96..90d81a0583 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs @@ -5,64 +5,63 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace ImageSharp.Benchmarks.General.Vectorization +namespace ImageSharp.Benchmarks.General.Vectorization; + +public abstract class SIMDBenchmarkBase + where T : struct { - public abstract class SIMDBenchmarkBase - where T : struct - { - protected T[] input; + protected T[] input; - protected T[] result; + protected T[] result; - protected T testValue; + protected T testValue; - protected Vector testVector; + protected Vector testVector; - protected virtual T GetTestValue() => default; + protected virtual T GetTestValue() => default; - protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); + protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); - [Params(32)] - public int InputSize { get; set; } + [Params(32)] + public int InputSize { get; set; } - [GlobalSetup] - public virtual void Setup() - { - this.input = new T[this.InputSize]; - this.result = new T[this.InputSize]; - this.testValue = this.GetTestValue(); - this.testVector = this.GetTestVector(); - } + [GlobalSetup] + public virtual void Setup() + { + this.input = new T[this.InputSize]; + this.result = new T[this.InputSize]; + this.testValue = this.GetTestValue(); + this.testVector = this.GetTestVector(); + } - public abstract class Multiply : SIMDBenchmarkBase + public abstract class Multiply : SIMDBenchmarkBase + { + [Benchmark] + public void Simd() { - [Benchmark] - public void Simd() - { - Vector v = this.testVector; + Vector v = this.testVector; - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - Vector a = Unsafe.As>(ref this.input[i]); - a = a * v; - Unsafe.As>(ref this.result[i]) = a; - } + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = Unsafe.As>(ref this.input[i]); + a = a * v; + Unsafe.As>(ref this.result[i]) = a; } } + } - public abstract class Divide : SIMDBenchmarkBase + public abstract class Divide : SIMDBenchmarkBase + { + [Benchmark] + public void Simd() { - [Benchmark] - public void Simd() - { - Vector v = this.testVector; + Vector v = this.testVector; - for (int i = 0; i < this.input.Length; i += Vector.Count) - { - Vector a = Unsafe.As>(ref this.input[i]); - a = a / v; - Unsafe.As>(ref this.result[i]) = a; - } + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = Unsafe.As>(ref this.input[i]); + a = a / v; + Unsafe.As>(ref this.result[i]) = a; } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs index 42c8bd25ce..63d363c688 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs @@ -5,104 +5,103 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +[Config(typeof(Config.ShortMultiFramework))] +public class UInt32ToSingle { - [Config(typeof(Config.ShortMultiFramework))] - public class UInt32ToSingle - { - private float[] data; + private float[] data; - private const int Count = 32; + private const int Count = 32; - [GlobalSetup] - public void Setup() - { - this.data = new float[Count]; - } + [GlobalSetup] + public void Setup() + { + this.data = new float[Count]; + } - [Benchmark(Baseline = true)] - public void MagicMethod() - { - ref Vector b = ref Unsafe.As>(ref this.data[0]); + [Benchmark(Baseline = true)] + public void MagicMethod() + { + ref Vector b = ref Unsafe.As>(ref this.data[0]); - int n = Count / Vector.Count; + int n = Count / Vector.Count; - var bVec = new Vector(256.0f / 255.0f); - var magicFloat = new Vector(32768.0f); - var magicInt = new Vector(1191182336); // reinterpreted value of 32768.0f - var mask = new Vector(255); + var bVec = new Vector(256.0f / 255.0f); + var magicFloat = new Vector(32768.0f); + var magicInt = new Vector(1191182336); // reinterpreted value of 32768.0f + var mask = new Vector(255); - for (int i = 0; i < n; i++) - { - ref Vector df = ref Unsafe.Add(ref b, i); + for (int i = 0; i < n; i++) + { + ref Vector df = ref Unsafe.Add(ref b, i); - var vi = Vector.AsVectorUInt32(df); - vi &= mask; - vi |= magicInt; + var vi = Vector.AsVectorUInt32(df); + vi &= mask; + vi |= magicInt; - var vf = Vector.AsVectorSingle(vi); - vf = (vf - magicFloat) * bVec; + var vf = Vector.AsVectorSingle(vi); + vf = (vf - magicFloat) * bVec; - df = vf; - } + df = vf; } + } - [Benchmark] - public void StandardSimd() - { - int n = Count / Vector.Count; + [Benchmark] + public void StandardSimd() + { + int n = Count / Vector.Count; - ref Vector bf = ref Unsafe.As>(ref this.data[0]); - ref Vector bu = ref Unsafe.As, Vector>(ref bf); + ref Vector bf = ref Unsafe.As>(ref this.data[0]); + ref Vector bu = ref Unsafe.As, Vector>(ref bf); - var scale = new Vector(1f / 255f); + var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) - { - Vector u = Unsafe.Add(ref bu, i); - Vector v = Vector.ConvertToSingle(u); - v *= scale; - Unsafe.Add(ref bf, i) = v; - } + for (int i = 0; i < n; i++) + { + Vector u = Unsafe.Add(ref bu, i); + Vector v = Vector.ConvertToSingle(u); + v *= scale; + Unsafe.Add(ref bf, i) = v; } + } - [Benchmark] - public void StandardSimdFromInt() - { - int n = Count / Vector.Count; + [Benchmark] + public void StandardSimdFromInt() + { + int n = Count / Vector.Count; - ref Vector bf = ref Unsafe.As>(ref this.data[0]); - ref Vector bu = ref Unsafe.As, Vector>(ref bf); + ref Vector bf = ref Unsafe.As>(ref this.data[0]); + ref Vector bu = ref Unsafe.As, Vector>(ref bf); - var scale = new Vector(1f / 255f); + var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) - { - Vector u = Unsafe.Add(ref bu, i); - Vector v = Vector.ConvertToSingle(u); - v *= scale; - Unsafe.Add(ref bf, i) = v; - } + for (int i = 0; i < n; i++) + { + Vector u = Unsafe.Add(ref bu, i); + Vector v = Vector.ConvertToSingle(u); + v *= scale; + Unsafe.Add(ref bf, i) = v; } + } - [Benchmark] - public void StandardSimdFromInt_RefCast() - { - int n = Count / Vector.Count; + [Benchmark] + public void StandardSimdFromInt_RefCast() + { + int n = Count / Vector.Count; - ref Vector bf = ref Unsafe.As>(ref this.data[0]); - var scale = new Vector(1f / 255f); + ref Vector bf = ref Unsafe.As>(ref this.data[0]); + var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) - { - ref Vector fRef = ref Unsafe.Add(ref bf, i); + for (int i = 0; i < n; i++) + { + ref Vector fRef = ref Unsafe.Add(ref bf, i); - Vector du = Vector.AsVectorInt32(fRef); - Vector v = Vector.ConvertToSingle(du); - v *= scale; + Vector du = Vector.AsVectorInt32(fRef); + Vector v = Vector.ConvertToSingle(du); + v *= scale; - fRef = v; - } + fRef = v; } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 8a733c156b..07ace06686 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -1,112 +1,111 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; + +/// +/// This benchmark compares different methods for fetching memory data into +/// checking if JIT has limitations. Normally SIMD acceleration should be here for all methods. +/// +public class VectorFetching { - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using BenchmarkDotNet.Attributes; - - /// - /// This benchmark compares different methods for fetching memory data into - /// checking if JIT has limitations. Normally SIMD acceleration should be here for all methods. - /// - public class VectorFetching - { - private float testValue; + private float testValue; - private float[] data; + private float[] data; - [Params(64)] - public int InputSize { get; set; } + [Params(64)] + public int InputSize { get; set; } - [GlobalSetup] - public void Setup() - { - this.data = new float[this.InputSize]; - this.testValue = 42; + [GlobalSetup] + public void Setup() + { + this.data = new float[this.InputSize]; + this.testValue = 42; - for (int i = 0; i < this.InputSize; i++) - { - this.data[i] = i; - } + for (int i = 0; i < this.InputSize; i++) + { + this.data[i] = i; } + } - [Benchmark(Baseline = true)] - public void Baseline() + [Benchmark(Baseline = true)] + public void Baseline() + { + float v = this.testValue; + for (int i = 0; i < this.data.Length; i++) { - float v = this.testValue; - for (int i = 0; i < this.data.Length; i++) - { - this.data[i] = this.data[i] * v; - } + this.data[i] = this.data[i] * v; } + } + + [Benchmark] + public void FetchWithVectorConstructor() + { + var v = new Vector(this.testValue); - [Benchmark] - public void FetchWithVectorConstructor() + for (int i = 0; i < this.data.Length; i += Vector.Count) { - var v = new Vector(this.testValue); - - for (int i = 0; i < this.data.Length; i += Vector.Count) - { - var a = new Vector(this.data, i); - a = a * v; - a.CopyTo(this.data, i); - } + var a = new Vector(this.data, i); + a = a * v; + a.CopyTo(this.data, i); } + } - [Benchmark] - public void FetchWithUnsafeCast() - { - var v = new Vector(this.testValue); - ref Vector start = ref Unsafe.As>(ref this.data[0]); + [Benchmark] + public void FetchWithUnsafeCast() + { + var v = new Vector(this.testValue); + ref Vector start = ref Unsafe.As>(ref this.data[0]); - int n = this.InputSize / Vector.Count; + int n = this.InputSize / Vector.Count; - for (int i = 0; i < n; i++) - { - ref Vector p = ref Unsafe.Add(ref start, i); + for (int i = 0; i < n; i++) + { + ref Vector p = ref Unsafe.Add(ref start, i); - Vector a = p; - a = a * v; + Vector a = p; + a = a * v; - p = a; - } + p = a; } + } - [Benchmark] - public void FetchWithUnsafeCastNoTempVector() - { - var v = new Vector(this.testValue); - ref Vector start = ref Unsafe.As>(ref this.data[0]); + [Benchmark] + public void FetchWithUnsafeCastNoTempVector() + { + var v = new Vector(this.testValue); + ref Vector start = ref Unsafe.As>(ref this.data[0]); - int n = this.InputSize / Vector.Count; + int n = this.InputSize / Vector.Count; - for (int i = 0; i < n; i++) - { - ref Vector a = ref Unsafe.Add(ref start, i); - a = a * v; - } + for (int i = 0; i < n; i++) + { + ref Vector a = ref Unsafe.Add(ref start, i); + a = a * v; } + } - [Benchmark] - public void FetchWithUnsafeCastFromReference() - { - var v = new Vector(this.testValue); + [Benchmark] + public void FetchWithUnsafeCastFromReference() + { + var v = new Vector(this.testValue); - var span = new Span(this.data); + var span = new Span(this.data); - ref Vector start = ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); + ref Vector start = ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); - int n = this.InputSize / Vector.Count; + int n = this.InputSize / Vector.Count; - for (int i = 0; i < n; i++) - { - ref Vector a = ref Unsafe.Add(ref start, i); - a = a * v; - } + for (int i = 0; i < n; i++) + { + ref Vector a = ref Unsafe.Add(ref start, i); + a = a * v; } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs index c4a28085f0..429475ffd3 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs @@ -7,60 +7,59 @@ using SixLabors.ImageSharp.Tuples; -namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; + +[Config(typeof(Config.ShortMultiFramework))] +public class WidenBytesToUInt32 { - [Config(typeof(Config.ShortMultiFramework))] - public class WidenBytesToUInt32 - { - private byte[] source; + private byte[] source; - private uint[] dest; + private uint[] dest; - private const int Count = 64; + private const int Count = 64; - [GlobalSetup] - public void Setup() - { - this.source = new byte[Count]; - this.dest = new uint[Count]; - } + [GlobalSetup] + public void Setup() + { + this.source = new byte[Count]; + this.dest = new uint[Count]; + } - [Benchmark(Baseline = true)] - public void Standard() - { - const int N = Count / 8; + [Benchmark(Baseline = true)] + public void Standard() + { + const int N = Count / 8; - ref Octet sBase = ref Unsafe.As>(ref this.source[0]); - ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); + ref Octet sBase = ref Unsafe.As>(ref this.source[0]); + ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); - for (int i = 0; i < N; i++) - { - Unsafe.Add(ref dBase, i).LoadFrom(ref Unsafe.Add(ref sBase, i)); - } + for (int i = 0; i < N; i++) + { + Unsafe.Add(ref dBase, i).LoadFrom(ref Unsafe.Add(ref sBase, i)); } + } - [Benchmark] - public void Simd() - { - int n = Count / Vector.Count; + [Benchmark] + public void Simd() + { + int n = Count / Vector.Count; - ref Vector sBase = ref Unsafe.As>(ref this.source[0]); - ref Vector dBase = ref Unsafe.As>(ref this.dest[0]); + ref Vector sBase = ref Unsafe.As>(ref this.source[0]); + ref Vector dBase = ref Unsafe.As>(ref this.dest[0]); - for (int i = 0; i < n; i++) - { - Vector b = Unsafe.Add(ref sBase, i); + for (int i = 0; i < n; i++) + { + Vector b = Unsafe.Add(ref sBase, i); - Vector.Widen(b, out Vector s0, out Vector s1); - Vector.Widen(s0, out Vector w0, out Vector w1); - Vector.Widen(s1, out Vector w2, out Vector w3); + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); - ref Vector d = ref Unsafe.Add(ref dBase, i * 4); - d = w0; - Unsafe.Add(ref d, 1) = w1; - Unsafe.Add(ref d, 2) = w2; - Unsafe.Add(ref d, 3) = w3; - } + ref Vector d = ref Unsafe.Add(ref dBase, i * 4); + d = w0; + Unsafe.Add(ref d, 1) = w1; + Unsafe.Add(ref d, 2) = w2; + Unsafe.Add(ref d, 3) = w3; } } } diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs index dd9d05cd46..04621695c6 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs @@ -1,83 +1,81 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave -{ - // See README.md for instructions about initialization. - [MemoryDiagnoser] - [ShortRunJob] - public class LoadResizeSaveStressBenchmarks - { - private LoadResizeSaveStressRunner runner; +namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave; - // private const JpegKind Filter = JpegKind.Progressive; - private const JpegKind Filter = JpegKind.Any; +// See README.md for instructions about initialization. +[MemoryDiagnoser] +[ShortRunJob] +public class LoadResizeSaveStressBenchmarks +{ + private LoadResizeSaveStressRunner runner; - [GlobalSetup] - public void Setup() - { - this.runner = new LoadResizeSaveStressRunner() - { - ImageCount = Environment.ProcessorCount, - Filter = Filter - }; - Console.WriteLine($"ImageCount: {this.runner.ImageCount} Filter: {Filter}"); - this.runner.Init(); - } - - private void ForEachImage(Action action, int maxDegreeOfParallelism) - { - this.runner.MaxDegreeOfParallelism = maxDegreeOfParallelism; - this.runner.ForEachImageParallel(action); - } + // private const JpegKind Filter = JpegKind.Progressive; + private const JpegKind Filter = JpegKind.Any; - public int[] ParallelismValues { get; } = + [GlobalSetup] + public void Setup() + { + this.runner = new LoadResizeSaveStressRunner() { - // Environment.ProcessorCount, - // Environment.ProcessorCount / 2, - // Environment.ProcessorCount / 4, - 1 + ImageCount = Environment.ProcessorCount, + Filter = Filter }; + Console.WriteLine($"ImageCount: {this.runner.ImageCount} Filter: {Filter}"); + this.runner.Init(); + } - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void SystemDrawing(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.SystemDrawingResize, maxDegreeOfParallelism); - - [Benchmark(Baseline = true)] - [ArgumentsSource(nameof(ParallelismValues))] - public void ImageSharp(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.ImageSharpResize, maxDegreeOfParallelism); - - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void Magick(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.MagickResize, maxDegreeOfParallelism); - - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void MagicScaler(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.MagicScalerResize, maxDegreeOfParallelism); - - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void SkiaBitmap(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.SkiaBitmapResize, maxDegreeOfParallelism); - - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void SkiaBitmapDecodeToTargetSize(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.SkiaBitmapDecodeToTargetSize, maxDegreeOfParallelism); - - [Benchmark] - [ArgumentsSource(nameof(ParallelismValues))] - public void NetVips(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.NetVipsResize, maxDegreeOfParallelism); + private void ForEachImage(Action action, int maxDegreeOfParallelism) + { + this.runner.MaxDegreeOfParallelism = maxDegreeOfParallelism; + this.runner.ForEachImageParallel(action); } + + public int[] ParallelismValues { get; } = + { + // Environment.ProcessorCount, + // Environment.ProcessorCount / 2, + // Environment.ProcessorCount / 4, + 1 + }; + + [Benchmark] + [ArgumentsSource(nameof(ParallelismValues))] + public void SystemDrawing(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.SystemDrawingResize, maxDegreeOfParallelism); + + [Benchmark(Baseline = true)] + [ArgumentsSource(nameof(ParallelismValues))] + public void ImageSharp(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.ImageSharpResize, maxDegreeOfParallelism); + + [Benchmark] + [ArgumentsSource(nameof(ParallelismValues))] + public void Magick(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.MagickResize, maxDegreeOfParallelism); + + [Benchmark] + [ArgumentsSource(nameof(ParallelismValues))] + public void MagicScaler(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.MagicScalerResize, maxDegreeOfParallelism); + + [Benchmark] + [ArgumentsSource(nameof(ParallelismValues))] + public void SkiaBitmap(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.SkiaBitmapResize, maxDegreeOfParallelism); + + [Benchmark] + [ArgumentsSource(nameof(ParallelismValues))] + public void SkiaBitmapDecodeToTargetSize(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.SkiaBitmapDecodeToTargetSize, maxDegreeOfParallelism); + + [Benchmark] + [ArgumentsSource(nameof(ParallelismValues))] + public void NetVips(int maxDegreeOfParallelism) => this.ForEachImage(this.runner.NetVipsResize, maxDegreeOfParallelism); } /* 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.300 - [Host] : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT - ShortRun : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT +[Host] : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT +ShortRun : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT Job=ShortRun IterationCount=3 LaunchCount=1 WarmupCount=3 diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index da71ce4dac..ce2d1625f6 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -1,23 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.IO; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; using ImageMagick; using PhotoSauce.MagicScaler; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests; using SkiaSharp; using ImageSharpImage = SixLabors.ImageSharp.Image; @@ -25,334 +17,333 @@ using NetVipsImage = NetVips.Image; using SystemDrawingImage = System.Drawing.Image; -namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave +namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave; + +public enum JpegKind { - public enum JpegKind - { - Baseline = 1, - Progressive = 2, - Any = Baseline | Progressive - } + Baseline = 1, + Progressive = 2, + Any = Baseline | Progressive +} - public class LoadResizeSaveStressRunner - { - private const int Quality = 75; +public class LoadResizeSaveStressRunner +{ + private const int Quality = 75; - // Set the quality for ImagSharp - private readonly JpegEncoder imageSharpJpegEncoder = new() { Quality = Quality }; - private readonly ImageCodecInfo systemDrawingJpegCodec = - ImageCodecInfo.GetImageEncoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid); + // Set the quality for ImagSharp + private readonly JpegEncoder imageSharpJpegEncoder = new() { Quality = Quality }; + private readonly ImageCodecInfo systemDrawingJpegCodec = + ImageCodecInfo.GetImageEncoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid); - public string[] Images { get; private set; } + public string[] Images { get; private set; } - public double TotalProcessedMegapixels { get; private set; } + public double TotalProcessedMegapixels { get; private set; } - public Size LastProcessedImageSize { get; private set; } + public Size LastProcessedImageSize { get; private set; } - private string outputDirectory; + private string outputDirectory; - public int ImageCount { get; set; } = int.MaxValue; + public int ImageCount { get; set; } = int.MaxValue; - public int MaxDegreeOfParallelism { get; set; } = -1; + public int MaxDegreeOfParallelism { get; set; } = -1; - public JpegKind Filter { get; set; } = JpegKind.Any; + public JpegKind Filter { get; set; } = JpegKind.Any; - public int ThumbnailSize { get; set; } = 150; + public int ThumbnailSize { get; set; } = 150; - private static readonly string[] ProgressiveFiles = + private static readonly string[] ProgressiveFiles = + { + "ancyloscelis-apiformis-m-paraguay-face_2014-08-08-095255-zs-pmax_15046500892_o.jpg", + "acanthopus-excellens-f-face-brasil_2014-08-06-132105-zs-pmax_14792513890_o.jpg", + "bee-ceratina-monster-f-ukraine-face_2014-08-09-123342-zs-pmax_15068816101_o.jpg", + "bombus-eximias-f-tawain-face_2014-08-10-094449-zs-pmax_15155452565_o.jpg", + "ceratina-14507h1-m-vietnam-face_2014-08-09-163218-zs-pmax_15096718245_o.jpg", + "ceratina-buscki-f-panama-face_2014-11-25-140413-zs-pmax_15923736081_o.jpg", + "ceratina-tricolor-f-panama-face2_2014-08-29-160402-zs-pmax_14906318297_o.jpg", + "ceratina-tricolor-f-panama-face_2014-08-29-160001-zs-pmax_14906300608_o.jpg", + "ceratina-tricolor-m-panama-face_2014-08-29-162821-zs-pmax_15069878876_o.jpg", + "coelioxys-cayennensis-f-argentina-face_2014-08-09-171932-zs-pmax_14914109737_o.jpg", + "ctenocolletes-smaragdinus-f-australia-face_2014-08-08-134825-zs-pmax_14865269708_o.jpg", + "diphaglossa-gayi-f-face-chile_2014-08-04-180547-zs-pmax_14918891472_o.jpg", + "hylaeus-nubilosus-f-australia-face_2014-08-14-121100-zs-pmax_15049602149_o.jpg", + "hypanthidioides-arenaria-f-face-brazil_2014-08-06-061201-zs-pmax_14770371360_o.jpg", + "megachile-chalicodoma-species-f-morocco-face_2014-08-14-124840-zs-pmax_15217084686_o.jpg", + "megachile-species-f-15266b06-face-kenya_2014-08-06-161044-zs-pmax_14994381392_o.jpg", + "megalopta-genalis-m-face-panama-barocolorado_2014-09-19-164939-zs-pmax_15121397069_o.jpg", + "melitta-haemorrhoidalis-m--england-face_2014-11-02-014026-zs-pmax-recovered_15782113675_o.jpg", + "nomia-heart-antennae-m-15266b02-face-kenya_2014-08-04-195216-zs-pmax_14922843736_o.jpg", + "nomia-species-m-oman-face_2014-08-09-192602-zs-pmax_15128732411_o.jpg", + "nomia-spiney-m-vietnam-face_2014-08-09-213126-zs-pmax_15191389705_o.jpg", + "ochreriades-fasciata-m-face-israel_2014-08-06-084407-zs-pmax_14965515571_o.jpg", + "osmia-brevicornisf-jaw-kyrgystan_2014-08-08-103333-zs-pmax_14865267787_o.jpg", + "pachyanthidium-aff-benguelense-f-6711f07-face_2014-08-07-112830-zs-pmax_15018069042_o.jpg", + "pachymelus-bicolor-m-face-madagascar_2014-08-06-134930-zs-pmax_14801667477_o.jpg", + "psaenythia-species-m-argentina-face_2014-08-07-163754-zs-pmax_15007018976_o.jpg", + "stingless-bee-1-f-face-peru_2014-07-30-123322-zs-pmax_15633797167_o.jpg", + "triepeolus-simplex-m-face-md-kent-county_2014-07-22-100937-zs-pmax_14805405233_o.jpg", + "washed-megachile-f-face-chile_2014-08-06-103414-zs-pmax_14977843152_o.jpg", + "xylocopa-balck-violetwing-f-kyrgystan-angle_2014-08-09-182433-zs-pmax_15123416061_o.jpg", + "xylocopa-india-yellow-m-india-face_2014-08-10-111701-zs-pmax_15166559172_o.jpg", + }; + + public void Init() + { + if (RuntimeInformation.OSArchitecture is Architecture.X86 or Architecture.X64) { - "ancyloscelis-apiformis-m-paraguay-face_2014-08-08-095255-zs-pmax_15046500892_o.jpg", - "acanthopus-excellens-f-face-brasil_2014-08-06-132105-zs-pmax_14792513890_o.jpg", - "bee-ceratina-monster-f-ukraine-face_2014-08-09-123342-zs-pmax_15068816101_o.jpg", - "bombus-eximias-f-tawain-face_2014-08-10-094449-zs-pmax_15155452565_o.jpg", - "ceratina-14507h1-m-vietnam-face_2014-08-09-163218-zs-pmax_15096718245_o.jpg", - "ceratina-buscki-f-panama-face_2014-11-25-140413-zs-pmax_15923736081_o.jpg", - "ceratina-tricolor-f-panama-face2_2014-08-29-160402-zs-pmax_14906318297_o.jpg", - "ceratina-tricolor-f-panama-face_2014-08-29-160001-zs-pmax_14906300608_o.jpg", - "ceratina-tricolor-m-panama-face_2014-08-29-162821-zs-pmax_15069878876_o.jpg", - "coelioxys-cayennensis-f-argentina-face_2014-08-09-171932-zs-pmax_14914109737_o.jpg", - "ctenocolletes-smaragdinus-f-australia-face_2014-08-08-134825-zs-pmax_14865269708_o.jpg", - "diphaglossa-gayi-f-face-chile_2014-08-04-180547-zs-pmax_14918891472_o.jpg", - "hylaeus-nubilosus-f-australia-face_2014-08-14-121100-zs-pmax_15049602149_o.jpg", - "hypanthidioides-arenaria-f-face-brazil_2014-08-06-061201-zs-pmax_14770371360_o.jpg", - "megachile-chalicodoma-species-f-morocco-face_2014-08-14-124840-zs-pmax_15217084686_o.jpg", - "megachile-species-f-15266b06-face-kenya_2014-08-06-161044-zs-pmax_14994381392_o.jpg", - "megalopta-genalis-m-face-panama-barocolorado_2014-09-19-164939-zs-pmax_15121397069_o.jpg", - "melitta-haemorrhoidalis-m--england-face_2014-11-02-014026-zs-pmax-recovered_15782113675_o.jpg", - "nomia-heart-antennae-m-15266b02-face-kenya_2014-08-04-195216-zs-pmax_14922843736_o.jpg", - "nomia-species-m-oman-face_2014-08-09-192602-zs-pmax_15128732411_o.jpg", - "nomia-spiney-m-vietnam-face_2014-08-09-213126-zs-pmax_15191389705_o.jpg", - "ochreriades-fasciata-m-face-israel_2014-08-06-084407-zs-pmax_14965515571_o.jpg", - "osmia-brevicornisf-jaw-kyrgystan_2014-08-08-103333-zs-pmax_14865267787_o.jpg", - "pachyanthidium-aff-benguelense-f-6711f07-face_2014-08-07-112830-zs-pmax_15018069042_o.jpg", - "pachymelus-bicolor-m-face-madagascar_2014-08-06-134930-zs-pmax_14801667477_o.jpg", - "psaenythia-species-m-argentina-face_2014-08-07-163754-zs-pmax_15007018976_o.jpg", - "stingless-bee-1-f-face-peru_2014-07-30-123322-zs-pmax_15633797167_o.jpg", - "triepeolus-simplex-m-face-md-kent-county_2014-07-22-100937-zs-pmax_14805405233_o.jpg", - "washed-megachile-f-face-chile_2014-08-06-103414-zs-pmax_14977843152_o.jpg", - "xylocopa-balck-violetwing-f-kyrgystan-angle_2014-08-09-182433-zs-pmax_15123416061_o.jpg", - "xylocopa-india-yellow-m-india-face_2014-08-10-111701-zs-pmax_15166559172_o.jpg", - }; + // Workaround ImageMagick issue + OpenCL.IsEnabled = false; + } - public void Init() + string imageDirectory = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, "MemoryStress"); + if (!Directory.Exists(imageDirectory) || !Directory.EnumerateFiles(imageDirectory).Any()) { - if (RuntimeInformation.OSArchitecture is Architecture.X86 or Architecture.X64) - { - // Workaround ImageMagick issue - OpenCL.IsEnabled = false; - } + throw new DirectoryNotFoundException($"Copy stress images to: {imageDirectory}"); + } - string imageDirectory = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, "MemoryStress"); - if (!Directory.Exists(imageDirectory) || !Directory.EnumerateFiles(imageDirectory).Any()) - { - throw new DirectoryNotFoundException($"Copy stress images to: {imageDirectory}"); - } + // Get at most this.ImageCount images from there + bool FilterFunc(string f) => this.Filter.HasFlag(GetJpegType(f)); - // Get at most this.ImageCount images from there - bool FilterFunc(string f) => this.Filter.HasFlag(GetJpegType(f)); + this.Images = Directory.EnumerateFiles(imageDirectory).Where(FilterFunc).Take(this.ImageCount).ToArray(); - this.Images = Directory.EnumerateFiles(imageDirectory).Where(FilterFunc).Take(this.ImageCount).ToArray(); + // Create the output directory next to the images directory + this.outputDirectory = TestEnvironment.CreateOutputDirectory("MemoryStress"); - // Create the output directory next to the images directory - this.outputDirectory = TestEnvironment.CreateOutputDirectory("MemoryStress"); + static JpegKind GetJpegType(string f) => + ProgressiveFiles.Any(p => f.EndsWith(p, StringComparison.OrdinalIgnoreCase)) + ? JpegKind.Progressive + : JpegKind.Baseline; + } - static JpegKind GetJpegType(string f) => - ProgressiveFiles.Any(p => f.EndsWith(p, StringComparison.OrdinalIgnoreCase)) - ? JpegKind.Progressive - : JpegKind.Baseline; - } + public void ForEachImageParallel(Action action) => Parallel.ForEach( + this.Images, + new ParallelOptions { MaxDegreeOfParallelism = this.MaxDegreeOfParallelism }, + action); - public void ForEachImageParallel(Action action) => Parallel.ForEach( - this.Images, - new ParallelOptions { MaxDegreeOfParallelism = this.MaxDegreeOfParallelism }, - action); + public Task ForEachImageParallelAsync(Func action) + { + int maxDegreeOfParallelism = this.MaxDegreeOfParallelism > 0 + ? this.MaxDegreeOfParallelism + : Environment.ProcessorCount; + int partitionSize = (int)Math.Ceiling((double)this.Images.Length / maxDegreeOfParallelism); - public Task ForEachImageParallelAsync(Func action) + List tasks = new(); + for (int i = 0; i < this.Images.Length; i += partitionSize) { - int maxDegreeOfParallelism = this.MaxDegreeOfParallelism > 0 - ? this.MaxDegreeOfParallelism - : Environment.ProcessorCount; - int partitionSize = (int)Math.Ceiling((double)this.Images.Length / maxDegreeOfParallelism); - - List tasks = new(); - for (int i = 0; i < this.Images.Length; i += partitionSize) - { - int end = Math.Min(i + partitionSize, this.Images.Length); - Task task = RunPartition(i, end); - tasks.Add(task); - } + int end = Math.Min(i + partitionSize, this.Images.Length); + Task task = RunPartition(i, end); + tasks.Add(task); + } - return Task.WhenAll(tasks); + return Task.WhenAll(tasks); - Task RunPartition(int start, int end) => Task.Run(async () => + Task RunPartition(int start, int end) => Task.Run(async () => + { + for (int i = start; i < end; i++) { - for (int i = start; i < end; i++) - { - await action(this.Images[i]); - } - }); - } + await action(this.Images[i]); + } + }); + } - private void LogImageProcessed(int width, int height) - { - this.LastProcessedImageSize = new Size(width, height); - double pixels = width * (double)height; - this.TotalProcessedMegapixels += pixels / 1_000_000.0; - } + private void LogImageProcessed(int width, int height) + { + this.LastProcessedImageSize = new Size(width, height); + double pixels = width * (double)height; + this.TotalProcessedMegapixels += pixels / 1_000_000.0; + } - private string OutputPath(string inputPath, [CallerMemberName] string postfix = null) => - Path.Combine( - this.outputDirectory, - Path.GetFileNameWithoutExtension(inputPath) + "-" + postfix + Path.GetExtension(inputPath)); + private string OutputPath(string inputPath, [CallerMemberName] string postfix = null) => + Path.Combine( + this.outputDirectory, + Path.GetFileNameWithoutExtension(inputPath) + "-" + postfix + Path.GetExtension(inputPath)); - private (int Width, int Height) ScaledSize(int inWidth, int inHeight, int outSize) + private (int Width, int Height) ScaledSize(int inWidth, int inHeight, int outSize) + { + int width, height; + if (inWidth > inHeight) { - int width, height; - if (inWidth > inHeight) - { - width = outSize; - height = (int)Math.Round(inHeight * outSize / (double)inWidth); - } - else - { - width = (int)Math.Round(inWidth * outSize / (double)inHeight); - height = outSize; - } - - return (width, height); + width = outSize; + height = (int)Math.Round(inHeight * outSize / (double)inWidth); } - - public void SystemDrawingResize(string input) + else { - using var image = SystemDrawingImage.FromFile(input, true); - this.LogImageProcessed(image.Width, image.Height); - - (int Width, int Height) scaled = this.ScaledSize(image.Width, image.Height, this.ThumbnailSize); - var resized = new Bitmap(scaled.Width, scaled.Height); - using var graphics = Graphics.FromImage(resized); - using var attributes = new ImageAttributes(); - attributes.SetWrapMode(WrapMode.TileFlipXY); - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingMode = CompositingMode.SourceCopy; - graphics.CompositingQuality = CompositingQuality.AssumeLinear; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.DrawImage(image, System.Drawing.Rectangle.FromLTRB(0, 0, resized.Width, resized.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); - - // Save the results - using var encoderParams = new EncoderParameters(1); - using var qualityParam = new EncoderParameter(Encoder.Quality, (long)Quality); - encoderParams.Param[0] = qualityParam; - resized.Save(this.OutputPath(input), this.systemDrawingJpegCodec, encoderParams); + width = (int)Math.Round(inWidth * outSize / (double)inHeight); + height = outSize; } - public void ImageSharpResize(string input) - { - using FileStream inputStream = File.Open(input, FileMode.Open); - using FileStream outputStream = File.Open(this.OutputPath(input), FileMode.Create); - - // Resize it to fit a 150x150 square - DecoderOptions options = new() - { - TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) - }; - - var decoder = new JpegDecoder(); - using ImageSharpImage image = decoder.Decode(options, inputStream); - this.LogImageProcessed(image.Width, image.Height); + return (width, height); + } - // Reduce the size of the file - image.Metadata.ExifProfile = null; - image.Metadata.XmpProfile = null; - image.Metadata.IccProfile = null; - image.Metadata.IptcProfile = null; + public void SystemDrawingResize(string input) + { + using var image = SystemDrawingImage.FromFile(input, true); + this.LogImageProcessed(image.Width, image.Height); + + (int Width, int Height) scaled = this.ScaledSize(image.Width, image.Height, this.ThumbnailSize); + var resized = new Bitmap(scaled.Width, scaled.Height); + using var graphics = Graphics.FromImage(resized); + using var attributes = new ImageAttributes(); + attributes.SetWrapMode(WrapMode.TileFlipXY); + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingMode = CompositingMode.SourceCopy; + graphics.CompositingQuality = CompositingQuality.AssumeLinear; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.DrawImage(image, System.Drawing.Rectangle.FromLTRB(0, 0, resized.Width, resized.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + + // Save the results + using var encoderParams = new EncoderParameters(1); + using var qualityParam = new EncoderParameter(Encoder.Quality, (long)Quality); + encoderParams.Param[0] = qualityParam; + resized.Save(this.OutputPath(input), this.systemDrawingJpegCodec, encoderParams); + } - // Save the results - image.Save(outputStream, this.imageSharpJpegEncoder); - } + public void ImageSharpResize(string input) + { + using FileStream inputStream = File.Open(input, FileMode.Open); + using FileStream outputStream = File.Open(this.OutputPath(input), FileMode.Create); - public async Task ImageSharpResizeAsync(string input) + // Resize it to fit a 150x150 square + DecoderOptions options = new() { - using FileStream output = File.Open(this.OutputPath(input), FileMode.Create); + TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) + }; - // Resize it to fit a 150x150 square. - DecoderOptions options = new() - { - TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) - }; + var decoder = new JpegDecoder(); + using ImageSharpImage image = decoder.Decode(options, inputStream); + this.LogImageProcessed(image.Width, image.Height); - using ImageSharpImage image = await ImageSharpImage.LoadAsync(options, input); - this.LogImageProcessed(image.Width, image.Height); + // Reduce the size of the file + image.Metadata.ExifProfile = null; + image.Metadata.XmpProfile = null; + image.Metadata.IccProfile = null; + image.Metadata.IptcProfile = null; - // Reduce the size of the file - image.Metadata.ExifProfile = null; - image.Metadata.XmpProfile = null; - image.Metadata.IccProfile = null; - image.Metadata.IptcProfile = null; + // Save the results + image.Save(outputStream, this.imageSharpJpegEncoder); + } - // Save the results - await image.SaveAsync(output, this.imageSharpJpegEncoder); - } + public async Task ImageSharpResizeAsync(string input) + { + using FileStream output = File.Open(this.OutputPath(input), FileMode.Create); - public void MagickResize(string input) + // Resize it to fit a 150x150 square. + DecoderOptions options = new() { - using var image = new MagickImage(input); - this.LogImageProcessed(image.Width, image.Height); + TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) + }; - // Resize it to fit a 150x150 square - image.Resize(this.ThumbnailSize, this.ThumbnailSize); + using ImageSharpImage image = await ImageSharpImage.LoadAsync(options, input); + this.LogImageProcessed(image.Width, image.Height); - // Reduce the size of the file - image.Strip(); + // Reduce the size of the file + image.Metadata.ExifProfile = null; + image.Metadata.XmpProfile = null; + image.Metadata.IccProfile = null; + image.Metadata.IptcProfile = null; - // Set the quality - image.Quality = Quality; + // Save the results + await image.SaveAsync(output, this.imageSharpJpegEncoder); + } - // Save the results - image.Write(this.OutputPath(input)); - } + public void MagickResize(string input) + { + using var image = new MagickImage(input); + this.LogImageProcessed(image.Width, image.Height); - public void MagicScalerResize(string input) - { - var settings = new ProcessImageSettings() - { - Width = this.ThumbnailSize, - Height = this.ThumbnailSize, - ResizeMode = CropScaleMode.Max, - SaveFormat = FileFormat.Jpeg, - JpegQuality = Quality, - JpegSubsampleMode = ChromaSubsampleMode.Subsample420 - }; - - // TODO: Is there a way to capture input dimensions for IncreaseTotalMegapixels? - using var output = new FileStream(this.OutputPath(input), FileMode.Create); - MagicImageProcessor.ProcessImage(input, output, settings); - } + // Resize it to fit a 150x150 square + image.Resize(this.ThumbnailSize, this.ThumbnailSize); - public void SkiaCanvasResize(string input) - { - using var original = SKBitmap.Decode(input); - this.LogImageProcessed(original.Width, original.Height); - (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize); - using var surface = SKSurface.Create(new SKImageInfo(scaled.Width, scaled.Height, original.ColorType, original.AlphaType)); - using var paint = new SKPaint() { FilterQuality = SKFilterQuality.High }; - SKCanvas canvas = surface.Canvas; - canvas.Scale((float)scaled.Width / original.Width); - canvas.DrawBitmap(original, 0, 0, paint); - canvas.Flush(); - - using FileStream output = File.OpenWrite(this.OutputPath(input)); - surface.Snapshot() - .Encode(SKEncodedImageFormat.Jpeg, Quality) - .SaveTo(output); - } + // Reduce the size of the file + image.Strip(); - public void SkiaBitmapResize(string input) + // Set the quality + image.Quality = Quality; + + // Save the results + image.Write(this.OutputPath(input)); + } + + public void MagicScalerResize(string input) + { + var settings = new ProcessImageSettings() { - using var original = SKBitmap.Decode(input); - this.LogImageProcessed(original.Width, original.Height); - (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize); - using var resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High); - if (resized == null) - { - return; - } + Width = this.ThumbnailSize, + Height = this.ThumbnailSize, + ResizeMode = CropScaleMode.Max, + SaveFormat = FileFormat.Jpeg, + JpegQuality = Quality, + JpegSubsampleMode = ChromaSubsampleMode.Subsample420 + }; - using var image = SKImage.FromBitmap(resized); - using FileStream output = File.OpenWrite(this.OutputPath(input)); - image.Encode(SKEncodedImageFormat.Jpeg, Quality) - .SaveTo(output); - } + // TODO: Is there a way to capture input dimensions for IncreaseTotalMegapixels? + using var output = new FileStream(this.OutputPath(input), FileMode.Create); + MagicImageProcessor.ProcessImage(input, output, settings); + } + + public void SkiaCanvasResize(string input) + { + using var original = SKBitmap.Decode(input); + this.LogImageProcessed(original.Width, original.Height); + (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize); + using var surface = SKSurface.Create(new SKImageInfo(scaled.Width, scaled.Height, original.ColorType, original.AlphaType)); + using var paint = new SKPaint() { FilterQuality = SKFilterQuality.High }; + SKCanvas canvas = surface.Canvas; + canvas.Scale((float)scaled.Width / original.Width); + canvas.DrawBitmap(original, 0, 0, paint); + canvas.Flush(); + + using FileStream output = File.OpenWrite(this.OutputPath(input)); + surface.Snapshot() + .Encode(SKEncodedImageFormat.Jpeg, Quality) + .SaveTo(output); + } - public void SkiaBitmapDecodeToTargetSize(string input) + public void SkiaBitmapResize(string input) + { + using var original = SKBitmap.Decode(input); + this.LogImageProcessed(original.Width, original.Height); + (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize); + using var resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High); + if (resized == null) { - using var codec = SKCodec.Create(input); + return; + } - SKImageInfo info = codec.Info; - this.LogImageProcessed(info.Width, info.Height); - (int Width, int Height) scaled = this.ScaledSize(info.Width, info.Height, this.ThumbnailSize); - SKSizeI supportedScale = codec.GetScaledDimensions((float)scaled.Width / info.Width); + using var image = SKImage.FromBitmap(resized); + using FileStream output = File.OpenWrite(this.OutputPath(input)); + image.Encode(SKEncodedImageFormat.Jpeg, Quality) + .SaveTo(output); + } - using var original = SKBitmap.Decode(codec, new SKImageInfo(supportedScale.Width, supportedScale.Height)); - using SKBitmap resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High); - if (resized == null) - { - return; - } + public void SkiaBitmapDecodeToTargetSize(string input) + { + using var codec = SKCodec.Create(input); - using var image = SKImage.FromBitmap(resized); + SKImageInfo info = codec.Info; + this.LogImageProcessed(info.Width, info.Height); + (int Width, int Height) scaled = this.ScaledSize(info.Width, info.Height, this.ThumbnailSize); + SKSizeI supportedScale = codec.GetScaledDimensions((float)scaled.Width / info.Width); - using FileStream output = File.OpenWrite(this.OutputPath(input, nameof(this.SkiaBitmapDecodeToTargetSize))); - image.Encode(SKEncodedImageFormat.Jpeg, Quality) - .SaveTo(output); + using var original = SKBitmap.Decode(codec, new SKImageInfo(supportedScale.Width, supportedScale.Height)); + using SKBitmap resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High); + if (resized == null) + { + return; } - public void NetVipsResize(string input) - { - // Thumbnail to fit a 150x150 square - using var thumb = NetVipsImage.Thumbnail(input, this.ThumbnailSize, this.ThumbnailSize); + using var image = SKImage.FromBitmap(resized); - // Save the results - thumb.Jpegsave(this.OutputPath(input), q: Quality, strip: true); - } + using FileStream output = File.OpenWrite(this.OutputPath(input, nameof(this.SkiaBitmapDecodeToTargetSize))); + image.Encode(SKEncodedImageFormat.Jpeg, Quality) + .SaveTo(output); + } + + public void NetVipsResize(string input) + { + // Thumbnail to fit a 150x150 square + using var thumb = NetVipsImage.Thumbnail(input, this.ThumbnailSize, this.ThumbnailSize); + + // Save the results + thumb.Jpegsave(this.OutputPath(input), q: Quality, strip: true); } } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 6c06b5e40c..68956c880f 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; using System.Numerics; using BenchmarkDotNet.Attributes; @@ -9,88 +8,87 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; -namespace SixLabors.ImageSharp.Benchmarks -{ - public class PorterDuffBulkVsPixel - { - private Configuration Configuration => Configuration.Default; +namespace SixLabors.ImageSharp.Benchmarks; - private void BulkVectorConvert( - Span destination, - Span background, - Span source, - Span amount) - where TPixel : unmanaged, IPixel - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); +public class PorterDuffBulkVsPixel +{ + private Configuration Configuration => Configuration.Default; - using IMemoryOwner buffer = - Configuration.Default.MemoryAllocator.Allocate(destination.Length * 3); - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + private void BulkVectorConvert( + Span destination, + Span background, + Span source, + Span amount) + where TPixel : unmanaged, IPixel + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - PixelOperations.Instance.ToVector4(this.Configuration, background, backgroundSpan); - PixelOperations.Instance.ToVector4(this.Configuration, source, sourceSpan); + using IMemoryOwner buffer = + Configuration.Default.MemoryAllocator.Allocate(destination.Length * 3); + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]); - } + PixelOperations.Instance.ToVector4(this.Configuration, background, backgroundSpan); + PixelOperations.Instance.ToVector4(this.Configuration, source, sourceSpan); - PixelOperations.Instance.FromVector4Destructive(this.Configuration, destinationSpan, destination); + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]); } - private void BulkPixelConvert( - Span destination, - Span background, - Span source, - Span amount) - where TPixel : unmanaged, IPixel - { - Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, destinationSpan, destination); + } - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(destination[i], source[i], amount[i]); - } - } + private void BulkPixelConvert( + Span destination, + Span background, + Span source, + Span amount) + where TPixel : unmanaged, IPixel + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); - [Benchmark(Description = "ImageSharp BulkVectorConvert")] - public Size BulkVectorConvert() + for (int i = 0; i < destination.Length; i++) { - using var image = new Image(800, 800); - using IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width); - amounts.GetSpan().Fill(1); + destination[i] = PorterDuffFunctions.NormalSrcOver(destination[i], source[i], amount[i]); + } + } - Buffer2D pixels = image.GetRootFramePixelBuffer(); - for (int y = 0; y < image.Height; y++) - { - Span span = pixels.DangerousGetRowSpan(y); - this.BulkVectorConvert(span, span, span, amounts.GetSpan()); - } + [Benchmark(Description = "ImageSharp BulkVectorConvert")] + public Size BulkVectorConvert() + { + using var image = new Image(800, 800); + using IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width); + amounts.GetSpan().Fill(1); - return new Size(image.Width, image.Height); + Buffer2D pixels = image.GetRootFramePixelBuffer(); + for (int y = 0; y < image.Height; y++) + { + Span span = pixels.DangerousGetRowSpan(y); + this.BulkVectorConvert(span, span, span, amounts.GetSpan()); } - [Benchmark(Description = "ImageSharp BulkPixelConvert")] - public Size BulkPixelConvert() - { - using var image = new Image(800, 800); - using IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width); - amounts.GetSpan().Fill(1); - Buffer2D pixels = image.GetRootFramePixelBuffer(); - for (int y = 0; y < image.Height; y++) - { - Span span = pixels.DangerousGetRowSpan(y); - this.BulkPixelConvert(span, span, span, amounts.GetSpan()); - } + return new Size(image.Width, image.Height); + } - return new Size(image.Width, image.Height); + [Benchmark(Description = "ImageSharp BulkPixelConvert")] + public Size BulkPixelConvert() + { + using var image = new Image(800, 800); + using IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width); + amounts.GetSpan().Fill(1); + Buffer2D pixels = image.GetRootFramePixelBuffer(); + for (int y = 0; y < image.Height; y++) + { + Span span = pixels.DangerousGetRowSpan(y); + this.BulkPixelConvert(span, span, span, amounts.GetSpan()); } + + return new Size(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs index a1732fa8b3..2bfc437582 100644 --- a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs @@ -5,16 +5,15 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -namespace SixLabors.ImageSharp.Benchmarks.Processing +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.MultiFramework))] +public class BokehBlur { - [Config(typeof(Config.MultiFramework))] - public class BokehBlur + [Benchmark] + public void Blur() { - [Benchmark] - public void Blur() - { - using var image = new Image(Configuration.Default, 400, 400, Color.White); - image.Mutate(c => c.BokehBlur()); - } + using var image = new Image(Configuration.Default, 400, 400, Color.White); + image.Mutate(c => c.BokehBlur()); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/Crop.cs b/tests/ImageSharp.Benchmarks/Processing/Crop.cs index 8631b97d35..d802d3293a 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Crop.cs @@ -9,32 +9,31 @@ using SDRectangle = System.Drawing.Rectangle; using SDSize = System.Drawing.Size; -namespace SixLabors.ImageSharp.Benchmarks.Processing +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.MultiFramework))] +public class Crop { - [Config(typeof(Config.MultiFramework))] - public class Crop + [Benchmark(Baseline = true, Description = "System.Drawing Crop")] + public SDSize CropSystemDrawing() { - [Benchmark(Baseline = true, Description = "System.Drawing Crop")] - public SDSize CropSystemDrawing() - { - using var source = new Bitmap(800, 800); - using var destination = new Bitmap(100, 100); - using var graphics = Graphics.FromImage(destination); + using var source = new Bitmap(800, 800); + using var destination = new Bitmap(100, 100); + using var graphics = Graphics.FromImage(destination); - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.DrawImage(source, new SDRectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.DrawImage(source, new SDRectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); - return destination.Size; - } + return destination.Size; + } - [Benchmark(Description = "ImageSharp Crop")] - public Size CropImageSharp() - { - using var image = new Image(800, 800); - image.Mutate(x => x.Crop(100, 100)); - return new Size(image.Width, image.Height); - } + [Benchmark(Description = "ImageSharp Crop")] + public Size CropImageSharp() + { + using var image = new Image(800, 800); + image.Mutate(x => x.Crop(100, 100)); + return new Size(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs index 68984005e9..9371906965 100644 --- a/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs @@ -1,49 +1,47 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +[Config(typeof(Config.MultiFramework))] +public class DetectEdges { - [Config(typeof(Config.MultiFramework))] - public class DetectEdges - { - private Image image; + private Image image; - [GlobalSetup] - public void ReadImage() + [GlobalSetup] + public void ReadImage() + { + if (this.image == null) { - if (this.image == null) - { - this.image = Image.Load(File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car))); - } + this.image = Image.Load(File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car))); } + } - [GlobalCleanup] - public void Cleanup() - { - this.image.Dispose(); - this.image = null; - } + [GlobalCleanup] + public void Cleanup() + { + this.image.Dispose(); + this.image = null; + } - [Benchmark(Description = "ImageSharp DetectEdges")] - public void ImageProcessorCoreDetectEdges() - { - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kayyali)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kayyali)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kirsch)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Laplacian3x3)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Laplacian5x5)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.LaplacianOfGaussian)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Prewitt)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.RobertsCross)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Robinson)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Scharr)); - this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Sobel)); - } + [Benchmark(Description = "ImageSharp DetectEdges")] + public void ImageProcessorCoreDetectEdges() + { + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kayyali)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kayyali)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kirsch)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Laplacian3x3)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Laplacian5x5)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.LaplacianOfGaussian)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Prewitt)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.RobertsCross)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Robinson)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Scharr)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Sobel)); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs index acb5a03590..7d6a52af25 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs @@ -5,28 +5,27 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -namespace SixLabors.ImageSharp.Benchmarks.Processing +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.MultiFramework))] +public class Diffuse { - [Config(typeof(Config.MultiFramework))] - public class Diffuse + [Benchmark] + public Size DoDiffuse() { - [Benchmark] - public Size DoDiffuse() - { - using var image = new Image(Configuration.Default, 800, 800, Color.BlanchedAlmond); - image.Mutate(x => x.Dither(KnownDitherings.FloydSteinberg)); + using var image = new Image(Configuration.Default, 800, 800, Color.BlanchedAlmond); + image.Mutate(x => x.Dither(KnownDitherings.FloydSteinberg)); - return image.Size(); - } + return image.Size(); + } - [Benchmark] - public Size DoDither() - { - using var image = new Image(Configuration.Default, 800, 800, Color.BlanchedAlmond); - image.Mutate(x => x.Dither()); + [Benchmark] + public Size DoDither() + { + using var image = new Image(Configuration.Default, 800, 800, Color.BlanchedAlmond); + image.Mutate(x => x.Dither()); - return image.Size(); - } + return image.Size(); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs index da6ba005af..0123f4d3b8 100644 --- a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs @@ -5,16 +5,15 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -namespace SixLabors.ImageSharp.Benchmarks.Samplers +namespace SixLabors.ImageSharp.Benchmarks.Samplers; + +[Config(typeof(Config.MultiFramework))] +public class GaussianBlur { - [Config(typeof(Config.MultiFramework))] - public class GaussianBlur + [Benchmark] + public void Blur() { - [Benchmark] - public void Blur() - { - using var image = new Image(Configuration.Default, 400, 400, Color.White); - image.Mutate(c => c.GaussianBlur()); - } + using var image = new Image(Configuration.Default, 400, 400, Color.White); + image.Mutate(c => c.GaussianBlur()); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs b/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs index 299ac9b2f3..6292b793ec 100644 --- a/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs +++ b/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs @@ -1,52 +1,50 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Normalization; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.Processing +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.MultiFramework))] +public class HistogramEqualization { - [Config(typeof(Config.MultiFramework))] - public class HistogramEqualization - { - private Image image; + private Image image; - [GlobalSetup] - public void ReadImages() + [GlobalSetup] + public void ReadImages() + { + if (this.image == null) { - if (this.image == null) - { - this.image = Image.Load(File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.HistogramEqImage))); - } + this.image = Image.Load(File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.HistogramEqImage))); } + } - [GlobalCleanup] - public void Cleanup() - { - this.image.Dispose(); - this.image = null; - } + [GlobalCleanup] + public void Cleanup() + { + this.image.Dispose(); + this.image = null; + } - [Benchmark(Description = "Global Histogram Equalization")] - public void GlobalHistogramEqualization() - => this.image.Mutate(img => img.HistogramEqualization( - new HistogramEqualizationOptions() - { - LuminanceLevels = 256, - Method = HistogramEqualizationMethod.Global - })); + [Benchmark(Description = "Global Histogram Equalization")] + public void GlobalHistogramEqualization() + => this.image.Mutate(img => img.HistogramEqualization( + new HistogramEqualizationOptions() + { + LuminanceLevels = 256, + Method = HistogramEqualizationMethod.Global + })); - [Benchmark(Description = "AdaptiveHistogramEqualization (Tile interpolation)")] - public void AdaptiveHistogramEqualization() - => this.image.Mutate(img => img.HistogramEqualization( - new HistogramEqualizationOptions() - { - LuminanceLevels = 256, - Method = HistogramEqualizationMethod.AdaptiveTileInterpolation - })); - } + [Benchmark(Description = "AdaptiveHistogramEqualization (Tile interpolation)")] + public void AdaptiveHistogramEqualization() + => this.image.Mutate(img => img.HistogramEqualization( + new HistogramEqualizationOptions() + { + LuminanceLevels = 256, + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation + })); } diff --git a/tests/ImageSharp.Benchmarks/Processing/Resize.cs b/tests/ImageSharp.Benchmarks/Processing/Resize.cs index 0f9e95819a..ae993cd25b 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Resize.cs @@ -3,8 +3,6 @@ using System.Drawing; using System.Drawing.Drawing2D; -using System.Globalization; -using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; @@ -12,238 +10,237 @@ using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +[Config(typeof(Config.MultiFramework))] +public abstract class Resize + where TPixel : unmanaged, IPixel { - [Config(typeof(Config.MultiFramework))] - public abstract class Resize - where TPixel : unmanaged, IPixel - { - private byte[] bytes = null; + private byte[] bytes = null; - private Image sourceImage; + private Image sourceImage; - private SDImage sourceBitmap; + private SDImage sourceBitmap; - protected Configuration Configuration { get; } = new Configuration(new JpegConfigurationModule()); + protected Configuration Configuration { get; } = new Configuration(new JpegConfigurationModule()); - protected int DestSize { get; private set; } + protected int DestSize { get; private set; } - [GlobalSetup] - public virtual void Setup() + [GlobalSetup] + public virtual void Setup() + { + if (this.bytes is null) { - if (this.bytes is null) - { - this.bytes = File.ReadAllBytes(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Snake)); + this.bytes = File.ReadAllBytes(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Snake)); - this.sourceImage = Image.Load(this.bytes); + this.sourceImage = Image.Load(this.bytes); - var ms1 = new MemoryStream(this.bytes); - this.sourceBitmap = SDImage.FromStream(ms1); - this.DestSize = this.sourceBitmap.Width / 2; - } + var ms1 = new MemoryStream(this.bytes); + this.sourceBitmap = SDImage.FromStream(ms1); + this.DestSize = this.sourceBitmap.Width / 2; } + } - [GlobalCleanup] - public void Cleanup() - { - this.bytes = null; - this.sourceImage.Dispose(); - this.sourceBitmap.Dispose(); - } + [GlobalCleanup] + public void Cleanup() + { + this.bytes = null; + this.sourceImage.Dispose(); + this.sourceBitmap.Dispose(); + } - [Benchmark(Baseline = true)] - public int SystemDrawing() + [Benchmark(Baseline = true)] + public int SystemDrawing() + { + using (var destination = new Bitmap(this.DestSize, this.DestSize)) { - using (var destination = new Bitmap(this.DestSize, this.DestSize)) + using (var g = Graphics.FromImage(destination)) { - using (var g = Graphics.FromImage(destination)) - { - g.CompositingMode = CompositingMode.SourceCopy; - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; - g.CompositingQuality = CompositingQuality.HighQuality; - g.SmoothingMode = SmoothingMode.HighQuality; - - g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); - } - - return destination.Width; + g.CompositingMode = CompositingMode.SourceCopy; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.SmoothingMode = SmoothingMode.HighQuality; + + g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); } + + return destination.Width; } + } - [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] - public int ImageSharp_P1() => this.RunImageSharpResize(1); + [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] + public int ImageSharp_P1() => this.RunImageSharpResize(1); - // Parallel cases have been disabled for fast benchmark execution. - // Uncomment, if you are interested in parallel speedup + // Parallel cases have been disabled for fast benchmark execution. + // Uncomment, if you are interested in parallel speedup - /* - [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")] - public int ImageSharp_P4() => this.RunImageSharpResize(4); + /* + [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")] + public int ImageSharp_P4() => this.RunImageSharpResize(4); - [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")] - public int ImageSharp_P8() => this.RunImageSharpResize(8); - */ + [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")] + public int ImageSharp_P8() => this.RunImageSharpResize(8); + */ - protected int RunImageSharpResize(int maxDegreeOfParallelism) - { - this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism; + protected int RunImageSharpResize(int maxDegreeOfParallelism) + { + this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism; - using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) - { - return clone.Width; - } + using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) + { + return clone.Width; } - - protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); } - public class Resize_Bicubic_Rgba32 : Resize - { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) - => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); - - // RESULTS - 2019 April - ResizeWorker: - // - // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) - // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC - // .NET Core SDK=2.2.202 - // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0 - // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // - // IterationCount=3 LaunchCount=1 WarmupCount=3 - // - // Method | Job | Runtime | SourceToDest | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | - // ----------------------------------------- |----- |-------- |------------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| - // SystemDrawing | Clr | Clr | 3032-400 | 120.11 ms | 1.435 ms | 0.0786 ms | 1.00 | 0.00 | - | - | - | 1638 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032-400 | 75.32 ms | 34.143 ms | 1.8715 ms | 0.63 | 0.02 | - | - | - | 16384 B | - // | | | | | | | | | | | | | - // SystemDrawing | Core | Core | 3032-400 | 120.33 ms | 6.669 ms | 0.3656 ms | 1.00 | 0.00 | - | - | - | 96 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032-400 | 88.56 ms | 1.864 ms | 0.1022 ms | 0.74 | 0.00 | - | - | - | 15568 B | - } + protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); +} - /// - /// Is it worth to set a larger working buffer limit for resize? - /// Conclusion: It doesn't really have an effect. - /// - public class Resize_Bicubic_Rgba32_CompareWorkBufferSizes : Resize_Bicubic_Rgba32 - { - [Params(128, 512, 1024, 8 * 1024)] - public int WorkingBufferSizeHintInKilobytes { get; set; } +public class Resize_Bicubic_Rgba32 : Resize +{ + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + + // RESULTS - 2019 April - ResizeWorker: + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC + // .NET Core SDK=2.2.202 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | SourceToDest | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |------------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032-400 | 120.11 ms | 1.435 ms | 0.0786 ms | 1.00 | 0.00 | - | - | - | 1638 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032-400 | 75.32 ms | 34.143 ms | 1.8715 ms | 0.63 | 0.02 | - | - | - | 16384 B | + // | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032-400 | 120.33 ms | 6.669 ms | 0.3656 ms | 1.00 | 0.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032-400 | 88.56 ms | 1.864 ms | 0.1022 ms | 0.74 | 0.00 | - | - | - | 15568 B | +} - public override void Setup() - { - this.Configuration.WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInKilobytes * 1024; - base.Setup(); - } - } +/// +/// Is it worth to set a larger working buffer limit for resize? +/// Conclusion: It doesn't really have an effect. +/// +public class Resize_Bicubic_Rgba32_CompareWorkBufferSizes : Resize_Bicubic_Rgba32 +{ + [Params(128, 512, 1024, 8 * 1024)] + public int WorkingBufferSizeHintInKilobytes { get; set; } - public class Resize_Bicubic_Bgra32 : Resize + public override void Setup() { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) - => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); - - // RESULTS (2019 April): - // - // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) - // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC - // .NET Core SDK=2.1.602 - // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 - // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // - // IterationCount=3 LaunchCount=1 WarmupCount=3 - // - // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | - // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:| - // SystemDrawing | Clr | Clr | 3032 | 400 | 119.01 ms | 18.513 ms | 1.0147 ms | 1.00 | - | - | - | 1638 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 104.71 ms | 16.078 ms | 0.8813 ms | 0.88 | - | - | - | 45056 B | - // | | | | | | | | | | | | | - // SystemDrawing | Core | Core | 3032 | 400 | 121.58 ms | 50.084 ms | 2.7453 ms | 1.00 | - | - | - | 96 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 96.96 ms | 7.899 ms | 0.4329 ms | 0.80 | - | - | - | 44512 B | + this.Configuration.WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInKilobytes * 1024; + base.Setup(); } +} - public class Resize_Bicubic_Rgb24 : Resize - { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) - => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); - - // RESULTS (2019 April): - // - // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) - // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC - // .NET Core SDK=2.1.602 - // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 - // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // - // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | - // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| - // SystemDrawing | Clr | Clr | 3032 | 400 | 121.37 ms | 48.580 ms | 2.6628 ms | 1.00 | 0.00 | - | - | - | 2048 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 99.36 ms | 11.356 ms | 0.6224 ms | 0.82 | 0.02 | - | - | - | 45056 B | - // | | | | | | | | | | | | | | - // SystemDrawing | Core | Core | 3032 | 400 | 118.06 ms | 15.667 ms | 0.8587 ms | 1.00 | 0.00 | - | - | - | 96 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 92.47 ms | 5.683 ms | 0.3115 ms | 0.78 | 0.01 | - | - | - | 44512 B | - } +public class Resize_Bicubic_Bgra32 : Resize +{ + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + + // RESULTS (2019 April): + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 119.01 ms | 18.513 ms | 1.0147 ms | 1.00 | - | - | - | 1638 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 104.71 ms | 16.078 ms | 0.8813 ms | 0.88 | - | - | - | 45056 B | + // | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 121.58 ms | 50.084 ms | 2.7453 ms | 1.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 96.96 ms | 7.899 ms | 0.4329 ms | 0.80 | - | - | - | 44512 B | +} + +public class Resize_Bicubic_Rgb24 : Resize +{ + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + + // RESULTS (2019 April): + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 121.37 ms | 48.580 ms | 2.6628 ms | 1.00 | 0.00 | - | - | - | 2048 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 99.36 ms | 11.356 ms | 0.6224 ms | 0.82 | 0.02 | - | - | - | 45056 B | + // | | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 118.06 ms | 15.667 ms | 0.8587 ms | 1.00 | 0.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 92.47 ms | 5.683 ms | 0.3115 ms | 0.78 | 0.01 | - | - | - | 44512 B | +} - public class Resize_BicubicCompand_Rgba32 : Resize +public class Resize_BicubicCompand_Rgba32 : Resize +{ + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); + + // RESULTS (2019 April): + // + // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC + // .NET Core SDK=2.1.602 + // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 + // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT + // + // IterationCount=3 LaunchCount=1 WarmupCount=3 + // + // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + // ----------------------------------------- |----- |-------- |----------- |--------- |---------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| + // SystemDrawing | Clr | Clr | 3032 | 400 | 120.7 ms | 68.985 ms | 3.7813 ms | 1.00 | 0.00 | - | - | - | 1638 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 132.2 ms | 15.976 ms | 0.8757 ms | 1.10 | 0.04 | - | - | - | 16384 B | + // | | | | | | | | | | | | | | + // SystemDrawing | Core | Core | 3032 | 400 | 118.3 ms | 6.899 ms | 0.3781 ms | 1.00 | 0.00 | - | - | - | 96 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 B | +} + +public class Resize_Bicubic_Compare_Rgba32_Rgb24 +{ + private Resize_Bicubic_Rgb24 rgb24; + private Resize_Bicubic_Rgba32 rgba32; + + [GlobalSetup] + public void Setup() { - protected override void ExecuteResizeOperation(IImageProcessingContext ctx) - => ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); - - // RESULTS (2019 April): - // - // BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4) - // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores - // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC - // .NET Core SDK=2.1.602 - // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0 - // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT - // - // IterationCount=3 LaunchCount=1 WarmupCount=3 - // - // Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | - // ----------------------------------------- |----- |-------- |----------- |--------- |---------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:| - // SystemDrawing | Clr | Clr | 3032 | 400 | 120.7 ms | 68.985 ms | 3.7813 ms | 1.00 | 0.00 | - | - | - | 1638 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 132.2 ms | 15.976 ms | 0.8757 ms | 1.10 | 0.04 | - | - | - | 16384 B | - // | | | | | | | | | | | | | | - // SystemDrawing | Core | Core | 3032 | 400 | 118.3 ms | 6.899 ms | 0.3781 ms | 1.00 | 0.00 | - | - | - | 96 B | - // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 B | + this.rgb24 = new Resize_Bicubic_Rgb24(); + this.rgb24.Setup(); + this.rgba32 = new Resize_Bicubic_Rgba32(); + this.rgba32.Setup(); } - public class Resize_Bicubic_Compare_Rgba32_Rgb24 + [GlobalCleanup] + public void Cleanup() { - private Resize_Bicubic_Rgb24 rgb24; - private Resize_Bicubic_Rgba32 rgba32; - - [GlobalSetup] - public void Setup() - { - this.rgb24 = new Resize_Bicubic_Rgb24(); - this.rgb24.Setup(); - this.rgba32 = new Resize_Bicubic_Rgba32(); - this.rgba32.Setup(); - } - - [GlobalCleanup] - public void Cleanup() - { - this.rgb24.Cleanup(); - this.rgba32.Cleanup(); - } + this.rgb24.Cleanup(); + this.rgba32.Cleanup(); + } - [Benchmark] - public void SystemDrawing() => this.rgba32.SystemDrawing(); + [Benchmark] + public void SystemDrawing() => this.rgba32.SystemDrawing(); - [Benchmark(Baseline = true)] - public void Rgba32() => this.rgba32.ImageSharp_P1(); + [Benchmark(Baseline = true)] + public void Rgba32() => this.rgba32.ImageSharp_P1(); - [Benchmark] - public void Rgb24() => this.rgb24.ImageSharp_P1(); - } + [Benchmark] + public void Rgb24() => this.rgb24.ImageSharp_P1(); } diff --git a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs index 313c5ba961..c392d99644 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs @@ -5,19 +5,18 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -namespace SixLabors.ImageSharp.Benchmarks.Processing +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.MultiFramework))] +public class Rotate { - [Config(typeof(Config.MultiFramework))] - public class Rotate + [Benchmark] + public Size DoRotate() { - [Benchmark] - public Size DoRotate() - { - using var image = new Image(Configuration.Default, 400, 400, Color.BlanchedAlmond); - image.Mutate(x => x.Rotate(37.5F)); + using var image = new Image(Configuration.Default, 400, 400, Color.BlanchedAlmond); + image.Mutate(x => x.Rotate(37.5F)); - return image.Size(); - } + return image.Size(); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/Skew.cs b/tests/ImageSharp.Benchmarks/Processing/Skew.cs index 7a7d38def9..bc64382c19 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Skew.cs @@ -5,19 +5,18 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -namespace SixLabors.ImageSharp.Benchmarks.Processing +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.MultiFramework))] +public class Skew { - [Config(typeof(Config.MultiFramework))] - public class Skew + [Benchmark] + public Size DoSkew() { - [Benchmark] - public Size DoSkew() - { - using var image = new Image(Configuration.Default, 400, 400, Color.BlanchedAlmond); - image.Mutate(x => x.Skew(20, 10)); + using var image = new Image(Configuration.Default, 400, 400, Color.BlanchedAlmond); + image.Mutate(x => x.Skew(20, 10)); - return image.Size(); - } + return image.Size(); } } diff --git a/tests/ImageSharp.Benchmarks/Program.cs b/tests/ImageSharp.Benchmarks/Program.cs index 153e12d4b8..75e5a8233e 100644 --- a/tests/ImageSharp.Benchmarks/Program.cs +++ b/tests/ImageSharp.Benchmarks/Program.cs @@ -3,18 +3,17 @@ using BenchmarkDotNet.Running; -namespace SixLabors.ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks; + +public class Program { - public class Program - { - /// - /// The main. - /// - /// - /// The arguments to pass to the program. - /// - public static void Main(string[] args) => BenchmarkSwitcher - .FromAssembly(typeof(Program).Assembly) - .Run(args); - } + /// + /// The main. + /// + /// + /// The arguments to pass to the program. + /// + public static void Main(string[] args) => BenchmarkSwitcher + .FromAssembly(typeof(Program).Assembly) + .Run(args); } diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs index 9932990930..248912b14f 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs @@ -1,144 +1,141 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Diagnostics; using System.Globalization; -using System.IO; using System.Text; -using System.Threading; using CommandLine; using CommandLine.Text; using SixLabors.ImageSharp.Benchmarks.LoadResizeSave; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory.Internals; -namespace SixLabors.ImageSharp.Tests.ProfilingSandbox +namespace SixLabors.ImageSharp.Tests.ProfilingSandbox; + +// See ImageSharp.Benchmarks/LoadResizeSave/README.md +internal class LoadResizeSaveParallelMemoryStress { - // See ImageSharp.Benchmarks/LoadResizeSave/README.md - internal class LoadResizeSaveParallelMemoryStress + private LoadResizeSaveParallelMemoryStress() { - private LoadResizeSaveParallelMemoryStress() + this.Benchmarks = new LoadResizeSaveStressRunner() { - this.Benchmarks = new LoadResizeSaveStressRunner() - { - Filter = JpegKind.Baseline, - }; - this.Benchmarks.Init(); - } + Filter = JpegKind.Baseline, + }; + this.Benchmarks.Init(); + } - private int gcFrequency; + private int gcFrequency; - private int leakFrequency; + private int leakFrequency; - private int imageCounter; + private int imageCounter; - public LoadResizeSaveStressRunner Benchmarks { get; } + public LoadResizeSaveStressRunner Benchmarks { get; } - public static void Run(string[] args) + public static void Run(string[] args) + { + Console.WriteLine($"Running: {typeof(LoadResizeSaveParallelMemoryStress).Assembly.Location}"); + Console.WriteLine($"64 bit: {Environment.Is64BitProcess}"); + CommandLineOptions options = args.Length > 0 ? CommandLineOptions.Parse(args) : null; + + var lrs = new LoadResizeSaveParallelMemoryStress(); + if (options != null) { - Console.WriteLine($"Running: {typeof(LoadResizeSaveParallelMemoryStress).Assembly.Location}"); - Console.WriteLine($"64 bit: {Environment.Is64BitProcess}"); - CommandLineOptions options = args.Length > 0 ? CommandLineOptions.Parse(args) : null; + lrs.Benchmarks.MaxDegreeOfParallelism = options.MaxDegreeOfParallelism; + } - var lrs = new LoadResizeSaveParallelMemoryStress(); - if (options != null) - { - lrs.Benchmarks.MaxDegreeOfParallelism = options.MaxDegreeOfParallelism; - } + Console.WriteLine($"\nEnvironment.ProcessorCount={Environment.ProcessorCount}"); + Stopwatch timer; - Console.WriteLine($"\nEnvironment.ProcessorCount={Environment.ProcessorCount}"); - Stopwatch timer; + if (options == null || !(options.ImageSharp || options.AsyncImageSharp)) + { + RunBenchmarkSwitcher(lrs, out timer); + } + else + { + Console.WriteLine("Running ImageSharp with options:"); + Console.WriteLine(options.ToString()); - if (options == null || !(options.ImageSharp || options.AsyncImageSharp)) + if (!options.KeepDefaultAllocator) { - RunBenchmarkSwitcher(lrs, out timer); + Configuration.Default.MemoryAllocator = options.CreateMemoryAllocator(); } - else - { - Console.WriteLine("Running ImageSharp with options:"); - Console.WriteLine(options.ToString()); - - if (!options.KeepDefaultAllocator) - { - Configuration.Default.MemoryAllocator = options.CreateMemoryAllocator(); - } - lrs.leakFrequency = options.LeakFrequency; - lrs.gcFrequency = options.GcFrequency; + lrs.leakFrequency = options.LeakFrequency; + lrs.gcFrequency = options.GcFrequency; - timer = Stopwatch.StartNew(); - try + timer = Stopwatch.StartNew(); + try + { + for (int i = 0; i < options.RepeatCount; i++) { - for (int i = 0; i < options.RepeatCount; i++) + if (options.AsyncImageSharp) { - if (options.AsyncImageSharp) - { - lrs.ImageSharpBenchmarkParallelAsync(); - } - else - { - lrs.ImageSharpBenchmarkParallel(); - } - - Console.WriteLine("OK"); + lrs.ImageSharpBenchmarkParallelAsync(); } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - - timer.Stop(); - - if (options.ReleaseRetainedResourcesAtEnd) - { - Configuration.Default.MemoryAllocator.ReleaseRetainedResources(); - } - - int finalGcCount = -Math.Min(0, options.GcFrequency); - - if (finalGcCount > 0) - { - Console.WriteLine($"TotalOutstandingHandles: {UnmanagedMemoryHandle.TotalOutstandingHandles}"); - Console.WriteLine($"GC x {finalGcCount}, with 3 seconds wait."); - for (int i = 0; i < finalGcCount; i++) + else { - Thread.Sleep(3000); - GC.Collect(); - GC.WaitForPendingFinalizers(); + lrs.ImageSharpBenchmarkParallel(); } + + Console.WriteLine("OK"); } } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } - var stats = new Stats(timer, lrs.Benchmarks.TotalProcessedMegapixels); - Console.WriteLine($"Total Megapixels: {stats.TotalMegapixels}, TotalOomRetries: {UnmanagedMemoryHandle.TotalOomRetries}, TotalOutstandingHandles: {UnmanagedMemoryHandle.TotalOutstandingHandles}, Total Gen2 GC count: {GC.CollectionCount(2)}"); - Console.WriteLine(stats.GetMarkdown()); - if (options?.FileOutput != null) + timer.Stop(); + + if (options.ReleaseRetainedResourcesAtEnd) { - PrintFileOutput(options.FileOutput, stats); + Configuration.Default.MemoryAllocator.ReleaseRetainedResources(); } - if (options != null && options.PauseAtEnd) + int finalGcCount = -Math.Min(0, options.GcFrequency); + + if (finalGcCount > 0) { - Console.WriteLine("Press ENTER"); - Console.ReadLine(); + Console.WriteLine($"TotalOutstandingHandles: {UnmanagedMemoryHandle.TotalOutstandingHandles}"); + Console.WriteLine($"GC x {finalGcCount}, with 3 seconds wait."); + for (int i = 0; i < finalGcCount; i++) + { + Thread.Sleep(3000); + GC.Collect(); + GC.WaitForPendingFinalizers(); + } } } - private static void PrintFileOutput(string fileOutput, Stats stats) + var stats = new Stats(timer, lrs.Benchmarks.TotalProcessedMegapixels); + Console.WriteLine($"Total Megapixels: {stats.TotalMegapixels}, TotalOomRetries: {UnmanagedMemoryHandle.TotalOomRetries}, TotalOutstandingHandles: {UnmanagedMemoryHandle.TotalOutstandingHandles}, Total Gen2 GC count: {GC.CollectionCount(2)}"); + Console.WriteLine(stats.GetMarkdown()); + if (options?.FileOutput != null) { - string[] ss = fileOutput.Split(';'); - string fileName = ss[0]; - string content = ss[1] - .Replace("TotalSeconds", stats.TotalSeconds.ToString(CultureInfo.InvariantCulture)) - .Replace("EOL", Environment.NewLine); - File.AppendAllText(fileName, content); + PrintFileOutput(options.FileOutput, stats); } - private static void RunBenchmarkSwitcher(LoadResizeSaveParallelMemoryStress lrs, out Stopwatch timer) + if (options != null && options.PauseAtEnd) { - Console.WriteLine(@"Choose a library for image resizing stress test: + Console.WriteLine("Press ENTER"); + Console.ReadLine(); + } + } + + private static void PrintFileOutput(string fileOutput, Stats stats) + { + string[] ss = fileOutput.Split(';'); + string fileName = ss[0]; + string content = ss[1] + .Replace("TotalSeconds", stats.TotalSeconds.ToString(CultureInfo.InvariantCulture)) + .Replace("EOL", Environment.NewLine); + File.AppendAllText(fileName, content); + } + + private static void RunBenchmarkSwitcher(LoadResizeSaveParallelMemoryStress lrs, out Stopwatch timer) + { + Console.WriteLine(@"Choose a library for image resizing stress test: 1. System.Drawing 2. ImageSharp @@ -149,210 +146,209 @@ 6. NetVips 7. ImageMagick "); - ConsoleKey key = Console.ReadKey().Key; - if (key < ConsoleKey.D1 || key > ConsoleKey.D6) - { - Console.WriteLine("Unrecognized command."); - Environment.Exit(-1); - } - - timer = Stopwatch.StartNew(); + ConsoleKey key = Console.ReadKey().Key; + if (key < ConsoleKey.D1 || key > ConsoleKey.D6) + { + Console.WriteLine("Unrecognized command."); + Environment.Exit(-1); + } - switch (key) - { - case ConsoleKey.D1: - lrs.SystemDrawingBenchmarkParallel(); - break; - case ConsoleKey.D2: - lrs.ImageSharpBenchmarkParallel(); - break; - case ConsoleKey.D3: - lrs.MagicScalerBenchmarkParallel(); - break; - case ConsoleKey.D4: - lrs.SkiaBitmapBenchmarkParallel(); - break; - case ConsoleKey.D5: - lrs.SkiaBitmapDecodeToTargetSizeBenchmarkParallel(); - break; - case ConsoleKey.D6: - lrs.NetVipsBenchmarkParallel(); - break; - case ConsoleKey.D7: - lrs.MagickBenchmarkParallel(); - break; - } + timer = Stopwatch.StartNew(); - timer.Stop(); + switch (key) + { + case ConsoleKey.D1: + lrs.SystemDrawingBenchmarkParallel(); + break; + case ConsoleKey.D2: + lrs.ImageSharpBenchmarkParallel(); + break; + case ConsoleKey.D3: + lrs.MagicScalerBenchmarkParallel(); + break; + case ConsoleKey.D4: + lrs.SkiaBitmapBenchmarkParallel(); + break; + case ConsoleKey.D5: + lrs.SkiaBitmapDecodeToTargetSizeBenchmarkParallel(); + break; + case ConsoleKey.D6: + lrs.NetVipsBenchmarkParallel(); + break; + case ConsoleKey.D7: + lrs.MagickBenchmarkParallel(); + break; } - private struct Stats - { - public double TotalSeconds { get; } + timer.Stop(); + } - public double TotalMegapixels { get; } + private struct Stats + { + public double TotalSeconds { get; } - public double MegapixelsPerSec { get; } + public double TotalMegapixels { get; } - public double MegapixelsPerSecPerCpu { get; } + public double MegapixelsPerSec { get; } - public Stats(Stopwatch sw, double totalMegapixels) - { - this.TotalMegapixels = totalMegapixels; - this.TotalSeconds = sw.ElapsedMilliseconds / 1000.0; - this.MegapixelsPerSec = totalMegapixels / this.TotalSeconds; - this.MegapixelsPerSecPerCpu = this.MegapixelsPerSec / Environment.ProcessorCount; - } + public double MegapixelsPerSecPerCpu { get; } - public string GetMarkdown() - { - var bld = new StringBuilder(); - bld.AppendLine($"| {nameof(this.TotalSeconds)} | {nameof(this.MegapixelsPerSec)} | {nameof(this.MegapixelsPerSecPerCpu)} |"); - bld.AppendLine( - $"| {L(nameof(this.TotalSeconds))} | {L(nameof(this.MegapixelsPerSec))} | {L(nameof(this.MegapixelsPerSecPerCpu))} |"); - - bld.Append("| "); - bld.AppendFormat(F(nameof(this.TotalSeconds)), this.TotalSeconds); - bld.Append(" | "); - bld.AppendFormat(F(nameof(this.MegapixelsPerSec)), this.MegapixelsPerSec); - bld.Append(" | "); - bld.AppendFormat(F(nameof(this.MegapixelsPerSecPerCpu)), this.MegapixelsPerSecPerCpu); - bld.AppendLine(" |"); - - return bld.ToString(); - - static string L(string header) => new('-', header.Length); - static string F(string column) => $"{{0,{column.Length}:f3}}"; - } + public Stats(Stopwatch sw, double totalMegapixels) + { + this.TotalMegapixels = totalMegapixels; + this.TotalSeconds = sw.ElapsedMilliseconds / 1000.0; + this.MegapixelsPerSec = totalMegapixels / this.TotalSeconds; + this.MegapixelsPerSecPerCpu = this.MegapixelsPerSec / Environment.ProcessorCount; } - private class CommandLineOptions + public string GetMarkdown() { - [Option('a', "async-imagesharp", Required = false, Default = false, HelpText = "Async ImageSharp without benchmark switching")] - public bool AsyncImageSharp { get; set; } + var bld = new StringBuilder(); + bld.AppendLine($"| {nameof(this.TotalSeconds)} | {nameof(this.MegapixelsPerSec)} | {nameof(this.MegapixelsPerSecPerCpu)} |"); + bld.AppendLine( + $"| {L(nameof(this.TotalSeconds))} | {L(nameof(this.MegapixelsPerSec))} | {L(nameof(this.MegapixelsPerSecPerCpu))} |"); + + bld.Append("| "); + bld.AppendFormat(F(nameof(this.TotalSeconds)), this.TotalSeconds); + bld.Append(" | "); + bld.AppendFormat(F(nameof(this.MegapixelsPerSec)), this.MegapixelsPerSec); + bld.Append(" | "); + bld.AppendFormat(F(nameof(this.MegapixelsPerSecPerCpu)), this.MegapixelsPerSecPerCpu); + bld.AppendLine(" |"); + + return bld.ToString(); + + static string L(string header) => new('-', header.Length); + static string F(string column) => $"{{0,{column.Length}:f3}}"; + } + } - [Option('i', "imagesharp", Required = false, Default = false, HelpText = "Test ImageSharp without benchmark switching")] - public bool ImageSharp { get; set; } + private class CommandLineOptions + { + [Option('a', "async-imagesharp", Required = false, Default = false, HelpText = "Async ImageSharp without benchmark switching")] + public bool AsyncImageSharp { get; set; } - [Option('d', "default-allocator", Required = false, Default = false, HelpText = "Keep default MemoryAllocator and ignore all settings")] - public bool KeepDefaultAllocator { get; set; } + [Option('i', "imagesharp", Required = false, Default = false, HelpText = "Test ImageSharp without benchmark switching")] + public bool ImageSharp { get; set; } - [Option('m', "max-contiguous", Required = false, Default = 4, HelpText = "Maximum size of contiguous pool buffers in MegaBytes")] - public int MaxContiguousPoolBufferMegaBytes { get; set; } = 4; + [Option('d', "default-allocator", Required = false, Default = false, HelpText = "Keep default MemoryAllocator and ignore all settings")] + public bool KeepDefaultAllocator { get; set; } - [Option('s', "poolsize", Required = false, Default = 4096, HelpText = "The size of the pool in MegaBytes")] - public int MaxPoolSizeMegaBytes { get; set; } = 4096; + [Option('m', "max-contiguous", Required = false, Default = 4, HelpText = "Maximum size of contiguous pool buffers in MegaBytes")] + public int MaxContiguousPoolBufferMegaBytes { get; set; } = 4; - [Option('u', "max-nonpool", Required = false, Default = 32, HelpText = "Maximum size of non-pooled contiguous blocks in MegaBytes")] - public int MaxCapacityOfNonPoolBuffersMegaBytes { get; set; } = 32; + [Option('s', "poolsize", Required = false, Default = 4096, HelpText = "The size of the pool in MegaBytes")] + public int MaxPoolSizeMegaBytes { get; set; } = 4096; - [Option('p', "parallelism", Required = false, Default = -1, HelpText = "Level of parallelism")] - public int MaxDegreeOfParallelism { get; set; } = -1; + [Option('u', "max-nonpool", Required = false, Default = 32, HelpText = "Maximum size of non-pooled contiguous blocks in MegaBytes")] + public int MaxCapacityOfNonPoolBuffersMegaBytes { get; set; } = 32; - [Option('r', "repeat-count", Required = false, Default = 1, HelpText = "Times to run the whole benchmark")] - public int RepeatCount { get; set; } = 1; + [Option('p', "parallelism", Required = false, Default = -1, HelpText = "Level of parallelism")] + public int MaxDegreeOfParallelism { get; set; } = -1; - [Option('g', "gc-frequency", Required = false, Default = 0, HelpText = "Positive number: call GC every 'g'-th resize. Negative number: call GC '-g' times in the end.")] - public int GcFrequency { get; set; } + [Option('r', "repeat-count", Required = false, Default = 1, HelpText = "Times to run the whole benchmark")] + public int RepeatCount { get; set; } = 1; - [Option('e', "release-at-end", Required = false, Default = false, HelpText = "Specify to run ReleaseRetainedResources() after execution")] - public bool ReleaseRetainedResourcesAtEnd { get; set; } + [Option('g', "gc-frequency", Required = false, Default = 0, HelpText = "Positive number: call GC every 'g'-th resize. Negative number: call GC '-g' times in the end.")] + public int GcFrequency { get; set; } - [Option('w', "pause", Required = false, Default = false, HelpText = "Specify to pause and wait for user input after the execution")] - public bool PauseAtEnd { get; set; } + [Option('e', "release-at-end", Required = false, Default = false, HelpText = "Specify to run ReleaseRetainedResources() after execution")] + public bool ReleaseRetainedResourcesAtEnd { get; set; } - [Option('f', "file", Required = false, Default = null, HelpText = "Specify to print the execution time to a file. Format: ';' see the code for formatstr semantics.")] - public string FileOutput { get; set; } + [Option('w', "pause", Required = false, Default = false, HelpText = "Specify to pause and wait for user input after the execution")] + public bool PauseAtEnd { get; set; } - [Option('t', "trim-period", Required = false, Default = null, HelpText = "Trim period for the pool in seconds")] - public int? TrimTimeSeconds { get; set; } + [Option('f', "file", Required = false, Default = null, HelpText = "Specify to print the execution time to a file. Format: ';' see the code for formatstr semantics.")] + public string FileOutput { get; set; } - [Option('l', "leak-frequency", Required = false, Default = 0, HelpText = "Inject leaking memory allocations after every 'l'-th resize to stress test finalizer behavior.")] - public int LeakFrequency { get; set; } + [Option('t', "trim-period", Required = false, Default = null, HelpText = "Trim period for the pool in seconds")] + public int? TrimTimeSeconds { get; set; } - public static CommandLineOptions Parse(string[] args) - { - CommandLineOptions result = null; - ParserResult parserResult = Parser.Default.ParseArguments(args).WithParsed(o => - { - result = o; - }); + [Option('l', "leak-frequency", Required = false, Default = 0, HelpText = "Inject leaking memory allocations after every 'l'-th resize to stress test finalizer behavior.")] + public int LeakFrequency { get; set; } - if (result == null) - { - Console.WriteLine(HelpText.RenderUsageText(parserResult)); - } + public static CommandLineOptions Parse(string[] args) + { + CommandLineOptions result = null; + ParserResult parserResult = Parser.Default.ParseArguments(args).WithParsed(o => + { + result = o; + }); - return result; + if (result == null) + { + Console.WriteLine(HelpText.RenderUsageText(parserResult)); } - public override string ToString() => - $"p({this.MaxDegreeOfParallelism})_i({this.ImageSharp})_a({this.AsyncImageSharp})_d({this.KeepDefaultAllocator})_m({this.MaxContiguousPoolBufferMegaBytes})_s({this.MaxPoolSizeMegaBytes})_u({this.MaxCapacityOfNonPoolBuffersMegaBytes})_r({this.RepeatCount})_g({this.GcFrequency})_e({this.ReleaseRetainedResourcesAtEnd})_l({this.LeakFrequency})"; + return result; + } + + public override string ToString() => + $"p({this.MaxDegreeOfParallelism})_i({this.ImageSharp})_a({this.AsyncImageSharp})_d({this.KeepDefaultAllocator})_m({this.MaxContiguousPoolBufferMegaBytes})_s({this.MaxPoolSizeMegaBytes})_u({this.MaxCapacityOfNonPoolBuffersMegaBytes})_r({this.RepeatCount})_g({this.GcFrequency})_e({this.ReleaseRetainedResourcesAtEnd})_l({this.LeakFrequency})"; - public MemoryAllocator CreateMemoryAllocator() + public MemoryAllocator CreateMemoryAllocator() + { + if (this.TrimTimeSeconds.HasValue) { - if (this.TrimTimeSeconds.HasValue) - { - return new UniformUnmanagedMemoryPoolMemoryAllocator( - 1024 * 1024, - (int)B(this.MaxContiguousPoolBufferMegaBytes), - B(this.MaxPoolSizeMegaBytes), - (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes), - new UniformUnmanagedMemoryPool.TrimSettings - { - TrimPeriodMilliseconds = this.TrimTimeSeconds.Value * 1000 - }); - } - else - { - return new UniformUnmanagedMemoryPoolMemoryAllocator( - 1024 * 1024, - (int)B(this.MaxContiguousPoolBufferMegaBytes), - B(this.MaxPoolSizeMegaBytes), - (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes)); - } + return new UniformUnmanagedMemoryPoolMemoryAllocator( + 1024 * 1024, + (int)B(this.MaxContiguousPoolBufferMegaBytes), + B(this.MaxPoolSizeMegaBytes), + (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes), + new UniformUnmanagedMemoryPool.TrimSettings + { + TrimPeriodMilliseconds = this.TrimTimeSeconds.Value * 1000 + }); + } + else + { + return new UniformUnmanagedMemoryPoolMemoryAllocator( + 1024 * 1024, + (int)B(this.MaxContiguousPoolBufferMegaBytes), + B(this.MaxPoolSizeMegaBytes), + (int)B(this.MaxCapacityOfNonPoolBuffersMegaBytes)); } - - private static long B(double megaBytes) => (long)(megaBytes * 1024 * 1024); } - private void ForEachImage(Action action) => this.Benchmarks.ForEachImageParallel(action); + private static long B(double megaBytes) => (long)(megaBytes * 1024 * 1024); + } + + private void ForEachImage(Action action) => this.Benchmarks.ForEachImageParallel(action); - private void SystemDrawingBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SystemDrawingResize); + private void SystemDrawingBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SystemDrawingResize); - private void ImageSharpBenchmarkParallel() => - this.ForEachImage(f => + private void ImageSharpBenchmarkParallel() => + this.ForEachImage(f => + { + int cnt = Interlocked.Increment(ref this.imageCounter); + this.Benchmarks.ImageSharpResize(f); + if (this.leakFrequency > 0 && cnt % this.leakFrequency == 0) { - int cnt = Interlocked.Increment(ref this.imageCounter); - this.Benchmarks.ImageSharpResize(f); - if (this.leakFrequency > 0 && cnt % this.leakFrequency == 0) - { - _ = Configuration.Default.MemoryAllocator.Allocate(1 << 16); - Size size = this.Benchmarks.LastProcessedImageSize; - _ = new Image(size.Width, size.Height); - } + _ = Configuration.Default.MemoryAllocator.Allocate(1 << 16); + Size size = this.Benchmarks.LastProcessedImageSize; + _ = new Image(size.Width, size.Height); + } - if (this.gcFrequency > 0 && cnt % this.gcFrequency == 0) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - } - }); + if (this.gcFrequency > 0 && cnt % this.gcFrequency == 0) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } + }); - private void ImageSharpBenchmarkParallelAsync() => - this.Benchmarks.ForEachImageParallelAsync(f => this.Benchmarks.ImageSharpResizeAsync(f)) - .GetAwaiter() - .GetResult(); + private void ImageSharpBenchmarkParallelAsync() => + this.Benchmarks.ForEachImageParallelAsync(f => this.Benchmarks.ImageSharpResizeAsync(f)) + .GetAwaiter() + .GetResult(); - private void MagickBenchmarkParallel() => this.ForEachImage(this.Benchmarks.MagickResize); + private void MagickBenchmarkParallel() => this.ForEachImage(this.Benchmarks.MagickResize); - private void MagicScalerBenchmarkParallel() => this.ForEachImage(this.Benchmarks.MagicScalerResize); + private void MagicScalerBenchmarkParallel() => this.ForEachImage(this.Benchmarks.MagicScalerResize); - private void SkiaBitmapBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SkiaBitmapResize); + private void SkiaBitmapBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SkiaBitmapResize); - private void SkiaBitmapDecodeToTargetSizeBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SkiaBitmapDecodeToTargetSize); + private void SkiaBitmapDecodeToTargetSizeBenchmarkParallel() => this.ForEachImage(this.Benchmarks.SkiaBitmapDecodeToTargetSize); - private void NetVipsBenchmarkParallel() => this.ForEachImage(this.Benchmarks.NetVipsResize); - } + private void NetVipsBenchmarkParallel() => this.ForEachImage(this.Benchmarks.NetVipsResize); } diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs index 5b5dedaab3..b5c8b70cde 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs @@ -1,11 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Reflection; -using System.Threading; -using SixLabors.ImageSharp.Memory.Internals; -using SixLabors.ImageSharp.Tests.Formats.Jpg; using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; using SixLabors.ImageSharp.Tests.ProfilingBenchmarks; using Xunit.Abstractions; @@ -14,68 +10,67 @@ #pragma warning disable SA1515 #pragma warning disable SA1512 -namespace SixLabors.ImageSharp.Tests.ProfilingSandbox +namespace SixLabors.ImageSharp.Tests.ProfilingSandbox; + +public class Program { - public class Program + private class ConsoleOutput : ITestOutputHelper { - private class ConsoleOutput : ITestOutputHelper - { - public void WriteLine(string message) => Console.WriteLine(message); + public void WriteLine(string message) => Console.WriteLine(message); - public void WriteLine(string format, params object[] args) => Console.WriteLine(format, args); - } + public void WriteLine(string format, params object[] args) => Console.WriteLine(format, args); + } - /// - /// The main entry point. Useful for executing benchmarks and performance unit tests manually, - /// when the IDE test runners lack some of the functionality. Eg.: it's not possible to run JetBrains memory profiler for unit tests. - /// - /// - /// The arguments to pass to the program. - /// - public static void Main(string[] args) + /// + /// The main entry point. Useful for executing benchmarks and performance unit tests manually, + /// when the IDE test runners lack some of the functionality. Eg.: it's not possible to run JetBrains memory profiler for unit tests. + /// + /// + /// The arguments to pass to the program. + /// + public static void Main(string[] args) + { + try { - try - { - LoadResizeSaveParallelMemoryStress.Run(args); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - - // RunJpegEncoderProfilingTests(); - // RunJpegColorProfilingTests(); - // RunDecodeJpegProfilingTests(); - // RunToVector4ProfilingTest(); - // RunResizeProfilingTest(); - - // Console.ReadLine(); + LoadResizeSaveParallelMemoryStress.Run(args); } - - private static Version GetNetCoreVersion() + catch (Exception ex) { - Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; - Console.WriteLine(assembly.Location); - string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); - int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); - if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) - { - return Version.Parse(assemblyPath[netCoreAppIndex + 1]); - } - - return null; + Console.WriteLine(ex); } - private static void RunResizeProfilingTest() - { - var test = new ResizeProfilingBenchmarks(new ConsoleOutput()); - test.ResizeBicubic(4000, 4000); - } + // RunJpegEncoderProfilingTests(); + // RunJpegColorProfilingTests(); + // RunDecodeJpegProfilingTests(); + // RunToVector4ProfilingTest(); + // RunResizeProfilingTest(); - private static void RunToVector4ProfilingTest() + // Console.ReadLine(); + } + + private static Version GetNetCoreVersion() + { + Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; + Console.WriteLine(assembly.Location); + string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); + if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) { - var tests = new PixelOperationsTests.Rgba32_OperationsTests(new ConsoleOutput()); - tests.Benchmark_ToVector4(); + return Version.Parse(assemblyPath[netCoreAppIndex + 1]); } + + return null; + } + + private static void RunResizeProfilingTest() + { + var test = new ResizeProfilingBenchmarks(new ConsoleOutput()); + test.ResizeBicubic(4000, 4000); + } + + private static void RunToVector4ProfilingTest() + { + var tests = new PixelOperationsTests.Rgba32_OperationsTests(new ConsoleOutput()); + tests.Benchmark_ToVector4(); } }