Browse Source

Update missed test and benchmarks project

pull/2229/head
James Jackson-South 4 years ago
parent
commit
5528a2923c
  1. 58
      tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs
  2. 72
      tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs
  3. 40
      tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs
  4. 58
      tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs
  5. 88
      tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs
  6. 52
      tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs
  7. 21
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_AddInPlace.cs
  8. 676
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
  9. 773
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
  10. 231
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs
  11. 58
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
  12. 43
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs
  13. 21
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceScalar.cs
  14. 49
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs
  15. 892
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
  16. 47
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs
  17. 61
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs
  18. 75
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs
  19. 37
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs
  20. 61
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs
  21. 61
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs
  22. 61
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs
  23. 94
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
  24. 92
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
  25. 52
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs
  26. 102
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
  27. 162
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs
  28. 109
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs
  29. 42
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
  30. 343
      tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
  31. 94
      tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs
  32. 58
      tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs
  33. 136
      tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs
  34. 82
      tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs
  35. 109
      tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs
  36. 72
      tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs
  37. 108
      tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs
  38. 179
      tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs
  39. 178
      tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs
  40. 228
      tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs
  41. 122
      tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs
  42. 236
      tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs
  43. 9
      tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs
  44. 148
      tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs
  45. 90
      tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs
  46. 85
      tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs
  47. 104
      tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs
  48. 164
      tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs
  49. 110
      tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs
  50. 110
      tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs
  51. 126
      tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs
  52. 76
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  53. 63
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs
  54. 21
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs
  55. 254
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs
  56. 90
      tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs
  57. 27
      tests/ImageSharp.Benchmarks/Color/ColorEquality.cs
  58. 33
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs
  59. 33
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs
  60. 33
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs
  61. 33
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs
  62. 33
      tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs
  63. 81
      tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs
  64. 123
      tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs
  65. 55
      tests/ImageSharp.Benchmarks/Config.cs
  66. 114
      tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs
  67. 178
      tests/ImageSharp.Benchmarks/General/Array2D.cs
  68. 82
      tests/ImageSharp.Benchmarks/General/ArrayReverse.cs
  69. 60
      tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs
  70. 88
      tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs
  71. 132
      tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs
  72. 50
      tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs
  73. 77
      tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs
  74. 29
      tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
  75. 45
      tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
  76. 58
      tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs
  77. 28
      tests/ImageSharp.Benchmarks/General/BasicMath/Round.cs
  78. 69
      tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs
  79. 430
      tests/ImageSharp.Benchmarks/General/CopyBuffers.cs
  80. 114
      tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs
  81. 31
      tests/ImageSharp.Benchmarks/General/GetSetPixel.cs
  82. 407
      tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs
  83. 425
      tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs
  84. 27
      tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs
  85. 276
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs
  86. 199
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs
  87. 159
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs
  88. 155
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs
  89. 111
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs
  90. 127
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs
  91. 454
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs
  92. 276
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs
  93. 626
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs
  94. 147
      tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs
  95. 111
      tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs
  96. 37
      tests/ImageSharp.Benchmarks/General/StructCasting.cs
  97. 102
      tests/ImageSharp.Benchmarks/General/Vector4Constants.cs
  98. 69
      tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs
  99. 69
      tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs
  100. 71
      tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs

58
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<Rgba32>(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<Rgba32>(memoryStream);
return new Size(image.Width, image.Height);
}
}

72
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<Rgba32> bmpCore;
private Stream bmpStream;
private SDImage bmpDrawing;
private Image<Rgba32> 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<Rgba32>(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<Rgba32>(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);
}
}

40
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<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" };
protected override IEnumerable<string> 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;
});
}

58
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<Rgba32>(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<Rgba32>(memoryStream);
return new Size(image.Width, image.Height);
}
}

88
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.Processing.Processors.Quantization;
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<Rgba32> bmpCore;
// System.Drawing needs this.
private Stream bmpStream;
private SDImage bmpDrawing;
private Image<Rgba32> 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<Rgba32>(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<Rgba32>(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);
}
}

52
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<string> InputImageSubfoldersOrFiles => new[] { "Gif/" };
protected override IEnumerable<string> 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;
});
}

21
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;
}
}

676
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 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 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<Block8x8F, byte>(ref this.block);
ref byte destBase = ref Unsafe.AsRef<byte>(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<Block8x8F, byte>(ref this.block);
ref byte destBase = ref Unsafe.AsRef<byte>(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<float>(this.bufferPtr);
int stride = Width;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
Vector<float> row2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
Vector<float> row3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
Vector<float> row4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
Vector<float> row5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(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<float>(this.bufferPtr);
int stride = Width;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
Vector<float> row2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
Vector<float> row3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
Vector<float> row4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
Vector<float> row5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(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<float>(this.bufferPtr);
int stride = Width;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
d0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
d1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
d2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
d3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
d4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
d5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
d6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
d7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
}
// [Benchmark]
public void UseVector8_V2()
{
ref Block8x8F s = ref this.block;
ref float origin = ref Unsafe.AsRef<float>(this.bufferPtr);
int stride = Width;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
d0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
d1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
d2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
d3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
d4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
d5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
d6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
d7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
}
[Benchmark]
public void UseVector8_V3()
{
int stride = Width * sizeof(float);
ref float d = ref this.unpinnedBuffer[0];
ref Vector<float> s = ref Unsafe.As<Block8x8F, Vector<float>>(ref this.block);
Vector<float> v0 = s;
Vector<float> v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1);
Vector<float> v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2);
Vector<float> v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3);
Unsafe.As<float, Vector<float>>(ref d) = v0;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2;
Unsafe.As<float, Vector<float>>(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<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2;
Unsafe.As<float, Vector<float>>(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<float> s = ref Unsafe.As<Block8x8F, Vector<float>>(ref this.block);
Vector<float> v0 = s;
Vector<float> v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1);
Vector<float> v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2);
Vector<float> v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3);
Unsafe.As<float, Vector<float>>(ref d) = v0;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2;
Unsafe.As<float, Vector<float>>(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<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1;
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2;
Unsafe.As<float, Vector<float>>(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<float> v;
[Benchmark]
public void UseVector256_Avx2_Variant1()
{
int stride = Width;
float* d = this.bufferPtr;
float* s = this.blockPtr;
Vector256<float> 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<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
Vector256<float> v3 = Avx.LoadVector256(s + (8 * 3));
Vector256<float> v4 = Avx.LoadVector256(s + (8 * 4));
Vector256<float> v5 = Avx.LoadVector256(s + (8 * 5));
Vector256<float> v6 = Avx.LoadVector256(s + (8 * 6));
Vector256<float> 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<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
Vector256<float> v3 = Avx.LoadVector256(s + (8 * 3));
Vector256<float> v4 = Avx.LoadVector256(s + (8 * 4));
Vector256<float> v5 = Avx.LoadVector256(s + (8 * 5));
Vector256<float> v6 = Avx.LoadVector256(s + (8 * 6));
Vector256<float> 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<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
Vector256<float> 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<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
Vector256<float> 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<float> s = ref Unsafe.As<Block8x8F, Vector256<float>>(ref this.block);
Vector256<float> v0 = s;
Vector256<float> v1 = Unsafe.Add(ref s, 1);
Vector256<float> v2 = Unsafe.Add(ref s, 2);
Vector256<float> v3 = Unsafe.Add(ref s, 3);
Unsafe.As<float, Vector256<float>>(ref d) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride)) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 2)) = v2;
Unsafe.As<float, Vector256<float>>(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<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 4)) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 5)) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 6)) = v2;
Unsafe.As<float, Vector256<float>>(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<float> s = ref Unsafe.As<Block8x8F, Vector256<float>>(ref this.block);
Vector256<float> v0 = s;
Vector256<float> v1 = Unsafe.Add(ref s, 1);
Vector256<float> v2 = Unsafe.Add(ref s, 2);
Vector256<float> v3 = Unsafe.Add(ref s, 3);
Unsafe.As<float, Vector256<float>>(ref d) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride)) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 2)) = v2;
Unsafe.As<float, Vector256<float>>(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<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 4)) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 5)) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref d, stride * 6)) = v2;
Unsafe.As<float, Vector256<float>>(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<float> s = ref Unsafe.As<Block8x8F, Vector256<float>>(ref this.block);
Vector256<float> v0 = s;
Vector256<float> v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1);
Vector256<float> v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2);
Vector256<float> v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3);
Unsafe.As<float, Vector256<float>>(ref d) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2;
Unsafe.As<float, Vector256<float>>(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<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2;
Unsafe.As<float, Vector256<float>>(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<float> s = ref Unsafe.As<Block8x8F, Vector256<float>>(ref this.block);
Vector256<float> v0 = s;
Vector256<float> v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1);
Vector256<float> v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2);
Vector256<float> v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3);
Unsafe.As<float, Vector256<float>>(ref d) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2;
Unsafe.As<float, Vector256<float>>(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<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1;
Unsafe.As<float, Vector256<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2;
Unsafe.As<float, Vector256<float>>(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<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
Vector256<float> 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<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
Vector256<float> 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<sbyte> v0 = Avx.LoadVector256(s);
Vector256<sbyte> v1 = Avx.LoadVector256(s + 32);
Vector256<sbyte> v2 = Avx.LoadVector256(s + (32 * 2));
Vector256<sbyte> 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<sbyte> v0 = Avx.LoadVector256(s);
Vector256<sbyte> v1 = Avx.LoadVector256(s + 32);
Vector256<sbyte> v2 = Avx.LoadVector256(s + (32 * 2));
Vector256<sbyte> 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 |
}

773
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs

@ -9,402 +9,401 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
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<float> buffer;
private Buffer2DRegion<float> destRegion;
[GlobalSetup]
public void Setup()
{
private Block8x8F block;
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500);
this.destRegion = this.buffer.GetRegion(200, 100, 128, 128);
}
private Buffer2D<float> 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<float> 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<float>(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<float, Vector2>(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<float, Vector2>(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<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(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<float, Vector2>(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<float, Vector2>(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<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 3)) = wRight;
}
[Benchmark]
public void UseVector4_SafeRightCorner()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(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<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = zRight;
Unsafe.Add(ref dTopLeft, 7) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight;
Unsafe.Add(ref dBottomLeft, 7) = wRight;
}
[Benchmark]
public void UseVector4_V2()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(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<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
var xyLeft = new Vector4(sLeft.X)
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(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<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = zRight;
Unsafe.Add(ref dTopLeft, 7) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(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<float, Vector2>(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<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(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 |
}

231
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs

@ -8,153 +8,152 @@ 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;
/// <summary>
/// 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
/// </summary>
public unsafe class Block8x8F_DivideRound
{
/// <summary>
/// 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
/// </summary>
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);
}
}

58
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<float>.Count != 8)
{
if (Vector<float>.Count != 8)
{
throw new NotSupportedException("Vector<float>.Count != 8");
}
for (short i = 0; i < Block8x8F.Size; i++)
{
this.source[i] = i;
}
throw new NotSupportedException("Vector<float>.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 |
}

43
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;
}
}

21
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;
}
}

49
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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
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 |
*/
*/

892
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 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 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<float>.Count != 8)
{
if (Vector<float>.Count != 8)
{
throw new NotSupportedException("Vector<float>.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<float>.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<Block8x8F, float>(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<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L);
row0 = SimdUtils.FastRound(row0);
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L);
row1 = SimdUtils.FastRound(row1);
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L);
row2 = SimdUtils.FastRound(row2);
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L);
row3 = SimdUtils.FastRound(row3);
ref Vector<float> row4 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L);
row4 = SimdUtils.FastRound(row4);
ref Vector<float> row5 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L);
row5 = SimdUtils.FastRound(row5);
ref Vector<float> row6 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L);
row6 = SimdUtils.FastRound(row6);
ref Vector<float> row7 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V7L);
row7 = SimdUtils.FastRound(row7);
}
[Benchmark]
public void ScalarRound()
{
ref float b = ref Unsafe.As<Block8x8F, float>(ref this.block);
[Benchmark]
public void SimdUtils_FastRound_Vector8_ForceAligned()
for (int i = 0; i < Block8x8F.Size; i++)
{
ref Block8x8F b = ref Unsafe.AsRef<Block8x8F>(this.alignedPtr);
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L);
row0 = SimdUtils.FastRound(row0);
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L);
row1 = SimdUtils.FastRound(row1);
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L);
row2 = SimdUtils.FastRound(row2);
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L);
row3 = SimdUtils.FastRound(row3);
ref Vector<float> row4 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L);
row4 = SimdUtils.FastRound(row4);
ref Vector<float> row5 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L);
row5 = SimdUtils.FastRound(row5);
ref Vector<float> row6 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L);
row6 = SimdUtils.FastRound(row6);
ref Vector<float> row7 = ref Unsafe.As<Vector4, Vector<float>>(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<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L);
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L);
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L);
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L);
row0 = SimdUtils.FastRound(row0);
row1 = SimdUtils.FastRound(row1);
row2 = SimdUtils.FastRound(row2);
row3 = SimdUtils.FastRound(row3);
row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L);
row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L);
row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L);
row3 = ref Unsafe.As<Vector4, Vector<float>>(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<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L);
row0 = SimdUtils.FastRound(row0);
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L);
row1 = SimdUtils.FastRound(row1);
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L);
row2 = SimdUtils.FastRound(row2);
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L);
row3 = SimdUtils.FastRound(row3);
ref Vector<float> row4 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L);
row4 = SimdUtils.FastRound(row4);
ref Vector<float> row5 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L);
row5 = SimdUtils.FastRound(row5);
ref Vector<float> row6 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L);
row6 = SimdUtils.FastRound(row6);
ref Vector<float> row7 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V7L);
row7 = SimdUtils.FastRound(row7);
}
[Benchmark]
public void Sse41_V1()
{
ref Vector128<float> b0 = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
[Benchmark]
public void SimdUtils_FastRound_Vector8_ForceAligned()
{
ref Block8x8F b = ref Unsafe.AsRef<Block8x8F>(this.alignedPtr);
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L);
row0 = SimdUtils.FastRound(row0);
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L);
row1 = SimdUtils.FastRound(row1);
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L);
row2 = SimdUtils.FastRound(row2);
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L);
row3 = SimdUtils.FastRound(row3);
ref Vector<float> row4 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L);
row4 = SimdUtils.FastRound(row4);
ref Vector<float> row5 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L);
row5 = SimdUtils.FastRound(row5);
ref Vector<float> row6 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L);
row6 = SimdUtils.FastRound(row6);
ref Vector<float> row7 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V7L);
row7 = SimdUtils.FastRound(row7);
}
ref Vector128<float> p = ref b0;
p = Sse41.RoundToNearestInteger(p);
[Benchmark]
public void SimdUtils_FastRound_Vector8_Grouped()
{
ref Block8x8F b = ref this.block;
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L);
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L);
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L);
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L);
row0 = SimdUtils.FastRound(row0);
row1 = SimdUtils.FastRound(row1);
row2 = SimdUtils.FastRound(row2);
row3 = SimdUtils.FastRound(row3);
row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L);
row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L);
row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L);
row3 = ref Unsafe.As<Vector4, Vector<float>>(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<float> b0 = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
ref Vector128<float> 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<float> p = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
p = Sse41.RoundToNearestInteger(p);
var offset = (IntPtr)sizeof(Vector128<float>);
p = Sse41.RoundToNearestInteger(p);
[Benchmark]
public unsafe void Sse41_V2()
{
ref Vector128<float> p = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
p = Sse41.RoundToNearestInteger(p);
var offset = (IntPtr)sizeof(Vector128<float>);
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<float> p = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
p = Sse41.RoundToNearestInteger(p);
var offset = (IntPtr)sizeof(Vector128<float>);
[Benchmark]
public unsafe void Sse41_V3()
for (int i = 0; i < 15; i++)
{
ref Vector128<float> p = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
p = ref Unsafe.AddByteOffset(ref p, offset);
p = Sse41.RoundToNearestInteger(p);
var offset = (IntPtr)sizeof(Vector128<float>);
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<float> p = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
var offset = (IntPtr)sizeof(Vector128<float>);
ref Vector128<float> a = ref p;
ref Vector128<float> b = ref Unsafe.AddByteOffset(ref a, offset);
ref Vector128<float> c = ref Unsafe.AddByteOffset(ref b, offset);
ref Vector128<float> 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<float> p = ref Unsafe.As<Block8x8F, Vector128<float>>(ref this.block);
var offset = (IntPtr)sizeof(Vector128<float>);
ref Vector128<float> a = ref p;
ref Vector128<float> b = ref Unsafe.AddByteOffset(ref a, offset);
ref Vector128<float> c = ref Unsafe.AddByteOffset(ref b, offset);
ref Vector128<float> 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<float> 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<float> 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<float> 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<float> 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<float> v0 = Sse.LoadAlignedVector128(p0);
Vector128<float> v1 = Sse.LoadAlignedVector128(p1);
Vector128<float> v2 = Sse.LoadAlignedVector128(p2);
Vector128<float> v3 = Sse.LoadAlignedVector128(p3);
Vector128<float> v4 = Sse.LoadAlignedVector128(p4);
Vector128<float> v5 = Sse.LoadAlignedVector128(p5);
Vector128<float> v6 = Sse.LoadAlignedVector128(p6);
Vector128<float> 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<float> v0 = Sse.LoadAlignedVector128(p0);
Vector128<float> v1 = Sse.LoadAlignedVector128(p1);
Vector128<float> v2 = Sse.LoadAlignedVector128(p2);
Vector128<float> v3 = Sse.LoadAlignedVector128(p3);
Vector128<float> v4 = Sse.LoadAlignedVector128(p4);
Vector128<float> v5 = Sse.LoadAlignedVector128(p5);
Vector128<float> v6 = Sse.LoadAlignedVector128(p6);
Vector128<float> 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);
}
}

47
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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
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

61
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);
}
}

75
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<float>[] Input { get; private set; }
protected Buffer2D<float>[] 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<float> buffer in this.Input)
{
foreach (Buffer2D<float> buffer in this.Input)
{
buffer.Dispose();
}
buffer.Dispose();
}
}
private static Buffer2D<float>[] CreateRandomValues(
int componentCount,
int inputBufferLength,
float minVal = 0f,
float maxVal = 255f)
private static Buffer2D<float>[] CreateRandomValues(
int componentCount,
int inputBufferLength,
float minVal = 0f,
float maxVal = 255f)
{
var rnd = new Random(42);
var buffers = new Buffer2D<float>[componentCount];
for (int i = 0; i < componentCount; i++)
{
var rnd = new Random(42);
var buffers = new Buffer2D<float>[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<float>(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<float>(values.Length, 1);
}
return buffers;
}
}

37
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);
}
}

61
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);
}
}

61
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);
}
}

61
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);
}
}

94
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 |

92
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.IO;
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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
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 |

52
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;
/// <summary>
/// An expensive Jpeg benchmark, running on a wide range of input images,
/// showing aggregate results.
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase
{
/// <summary>
/// An expensive Jpeg benchmark, running on a wide range of input images,
/// showing aggregate results.
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase
{
protected override IEnumerable<string> 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<string> 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<Rgba32>(ms));
[Benchmark]
public void ImageSharp()
=> this.ForEachStream(ms => Image.Load<Rgba32>(ms));
[Benchmark(Baseline = true)]
public void SystemDrawing()
=> this.ForEachStream(SDImage.FromStream);
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
=> this.ForEachStream(SDImage.FromStream);
}

102
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 SDImage = System.Drawing.Image;
using SDSize = System.Drawing.Size;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg;
/// <summary>
/// Image-specific Jpeg benchmarks
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class DecodeJpeg_ImageSpecific
{
/// <summary>
/// Image-specific Jpeg benchmarks
/// </summary>
[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 |
*/
}

162
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;
/// <summary>
/// Benchmark for performance comparison between other codecs.
/// </summary>
/// <remarks>
/// This benchmarks tests baseline 4:2:0 chroma sampling path.
/// </remarks>
public class EncodeJpegComparison
{
/// <summary>
/// Benchmark for performance comparison between other codecs.
/// </summary>
/// <remarks>
/// This benchmarks tests baseline 4:2:0 chroma sampling path.
/// </remarks>
public class EncodeJpegComparison
// Big enough, 4:4:4 chroma sampling
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
// Change/add parameters for extra benchmarks
[Params(75, 90, 100)]
public int Quality;
private MemoryStream destinationStream;
// ImageSharp
private Image<Rgba32> imageImageSharp;
private JpegEncoder encoderImageSharp;
// SkiaSharp
private SKBitmap imageSkiaSharp;
[GlobalSetup(Target = nameof(BenchmarkImageSharp))]
public void SetupImageSharp()
{
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.imageImageSharp = Image.Load<Rgba32>(imageBinaryStream);
this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = 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<Rgba32> imageImageSharp;
private JpegEncoder encoderImageSharp;
// SkiaSharp
private SKBitmap imageSkiaSharp;
[GlobalSetup(Target = nameof(BenchmarkImageSharp))]
public void SetupImageSharp()
{
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.imageImageSharp = Image.Load<Rgba32>(imageBinaryStream);
this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = 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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
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 |

109
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;
/// <summary>
/// Benchmark for all available encoding features of the Jpeg file type.
/// </summary>
/// <remarks>
/// This benchmark does NOT compare ImageSharp to any other jpeg codecs.
/// </remarks>
public class EncodeJpegFeatures
{
/// <summary>
/// Benchmark for all available encoding features of the Jpeg file type.
/// </summary>
/// <remarks>
/// This benchmark does NOT compare ImageSharp to any other jpeg codecs.
/// </remarks>
public class EncodeJpegFeatures
{
// Big enough, 4:4:4 chroma sampling
// No metadata
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
// Big enough, 4:4:4 chroma sampling
// No metadata
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
public static IEnumerable<JpegEncodingColor> ColorSpaceValues => new[]
{
JpegEncodingColor.Luminance,
JpegEncodingColor.Rgb,
JpegEncodingColor.YCbCrRatio420,
JpegEncodingColor.YCbCrRatio444,
};
public static IEnumerable<JpegEncodingColor> 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<Rgb24> bmpCore;
private JpegEncoder encoder;
private Image<Rgb24> 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<Rgb24>(imageBinaryStream);
this.encoder = new JpegEncoder
{
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.bmpCore = Image.Load<Rgb24>(imageBinaryStream);
this.encoder = new JpegEncoder
{
Quality = this.Quality,
ColorType = this.TargetColorSpace,
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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
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 |

42
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);
}
}

343
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<string, byte[]> FileNamesToBytes { get; set; } = new Dictionary<string, byte[]>();
protected Dictionary<string, byte[]> FileNamesToBytes { get; set; } = new Dictionary<string, byte[]>();
protected Dictionary<string, Image<Rgba32>> FileNamesToImageSharpImages { get; set; } = new Dictionary<string, Image<Rgba32>>();
protected Dictionary<string, Image<Rgba32>> FileNamesToImageSharpImages { get; set; } = new Dictionary<string, Image<Rgba32>>();
protected Dictionary<string, Bitmap> FileNamesToSystemDrawingImages { get; set; } = new Dictionary<string, Bitmap>();
protected Dictionary<string, Bitmap> FileNamesToSystemDrawingImages { get; set; } = new Dictionary<string, Bitmap>();
/// <summary>
/// The values of this enum separate input files into categories.
/// </summary>
public enum InputImageCategory
{
/// <summary>
/// The values of this enum separate input files into categories.
/// Use all images.
/// </summary>
public enum InputImageCategory
{
/// <summary>
/// Use all images.
/// </summary>
AllImages,
/// <summary>
/// Use small images only.
/// </summary>
SmallImagesOnly,
/// <summary>
/// Use large images only.
/// </summary>
LargeImagesOnly
}
[Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)]
public virtual InputImageCategory InputCategory { get; set; }
protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath;
protected virtual IEnumerable<string> SearchPatterns => new[] { "*.*" };
AllImages,
/// <summary>
/// Gets the file names containing these strings are substrings are not processed by the benchmark.
/// Use small images only.
/// </summary>
protected virtual IEnumerable<string> ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
SmallImagesOnly,
/// <summary>
/// Gets folders containing files OR files to be processed by the benchmark.
/// Use large images only.
/// </summary>
protected IEnumerable<string> AllFoldersOrFiles
=> this.InputImageSubfoldersOrFiles.Select(f => Path.Combine(this.BaseFolder, f));
LargeImagesOnly
}
/// <summary>
/// Gets the large image threshold.
/// The images sized above this threshold will be included in.
/// </summary>
protected virtual int LargeImageThresholdInBytes => 100000;
[Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)]
public virtual InputImageCategory InputCategory { get; set; }
protected IEnumerable<KeyValuePair<string, T>> EnumeratePairsByBenchmarkSettings<T>(
Dictionary<string, T> input,
Predicate<T> 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<KeyValuePair<string, byte[]>> FileNames2Bytes
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToBytes,
arr => arr.Length < this.LargeImageThresholdInBytes);
protected virtual IEnumerable<string> SearchPatterns => new[] { "*.*" };
/// <summary>
/// Gets the file names containing these strings are substrings are not processed by the benchmark.
/// </summary>
protected virtual IEnumerable<string> ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
/// <summary>
/// Gets folders containing files OR files to be processed by the benchmark.
/// </summary>
protected IEnumerable<string> AllFoldersOrFiles
=> this.InputImageSubfoldersOrFiles.Select(f => Path.Combine(this.BaseFolder, f));
/// <summary>
/// Gets the large image threshold.
/// The images sized above this threshold will be included in.
/// </summary>
protected virtual int LargeImageThresholdInBytes => 100000;
protected IEnumerable<KeyValuePair<string, T>> EnumeratePairsByBenchmarkSettings<T>(
Dictionary<string, T> input,
Predicate<T> 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<KeyValuePair<string, byte[]>> FileNames2Bytes
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToBytes,
arr => arr.Length < this.LargeImageThresholdInBytes);
protected abstract IEnumerable<string> InputImageSubfoldersOrFiles { get; }
[GlobalSetup]
public virtual void Setup()
{
if (!Vector.IsHardwareAccelerated)
{
throw new Exception("Vector.IsHardwareAccelerated == false! Check your build settings!");
}
protected abstract IEnumerable<string> 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()
/// <summary>
/// Execute code for each image stream. If the returned object of the operation <see cref="Func{T, TResult}"/> is <see cref="IDisposable"/> it will be disposed.
/// </summary>
/// <param name="operation">The operation to execute. If the returned object is &lt;see cref="IDisposable"/&gt; it will be disposed </param>
protected void ForEachStream(Func<MemoryStream, object> operation)
{
foreach (KeyValuePair<string, byte[]> 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<string, byte[]> 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<Rgba32>(ms1);
}
this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes));
}
}
/// <summary>
/// Execute code for each image stream. If the returned object of the operation <see cref="Func{T, TResult}"/> is <see cref="IDisposable"/> it will be disposed.
/// </summary>
/// <param name="operation">The operation to execute. If the returned object is &lt;see cref="IDisposable"/&gt; it will be disposed </param>
protected void ForEachStream(Func<MemoryStream, object> operation)
protected IEnumerable<KeyValuePair<string, Image<Rgba32>>> FileNames2ImageSharpImages
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToImageSharpImages,
img => img.Width * img.Height < this.LargeImageThresholdInPixels);
protected IEnumerable<KeyValuePair<string, Bitmap>> FileNames2SystemDrawingImages
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToSystemDrawingImages,
img => img.Width * img.Height < this.LargeImageThresholdInPixels);
protected virtual int LargeImageThresholdInPixels => 700000;
protected void ForEachImageSharpImage(Func<Image<Rgba32>, object> operation)
{
foreach (KeyValuePair<string, byte[]> kv in this.FileNames2Bytes)
foreach (KeyValuePair<string, Image<Rgba32>> 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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
}
}
public abstract class WithImagesPreloaded : MultiImageBenchmarkBase
protected void ForEachImageSharpImage(Func<Image<Rgba32>, MemoryStream, object> operation)
{
protected override void ReadFilesImpl()
{
base.ReadFilesImpl();
foreach (KeyValuePair<string, byte[]> 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<Rgba32>(ms1);
}
this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes));
}
}
protected IEnumerable<KeyValuePair<string, Image<Rgba32>>> 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<KeyValuePair<string, Bitmap>> 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<Image<Rgba32>, object> operation)
protected void ForEachSystemDrawingImage(Func<Bitmap, object> operation)
{
foreach (KeyValuePair<string, Bitmap> kv in this.FileNames2SystemDrawingImages)
{
foreach (KeyValuePair<string, Image<Rgba32>> 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<Image<Rgba32>, 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<Bitmap, object> operation)
{
foreach (KeyValuePair<string, Bitmap> 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<Bitmap, MemoryStream, object> 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<Bitmap, MemoryStream, object> 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;
});
}
}
}

94
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<Rgba32>(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<Rgba32>(bytes);
return image.Size();
}
private static string TestImageFullPath(string path)
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, path);
}

58
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<Rgba32>(memoryStream);
return image.Size();
}
[Benchmark(Description = "ImageSharp Png")]
public Size PngImageSharp()
{
using var memoryStream = new MemoryStream(this.pngBytes);
using var image = Image.Load<Rgba32>(memoryStream);
return image.Size();
}
}

136
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;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests;
namespace SixLabors.ImageSharp.Benchmarks.Codecs
namespace SixLabors.ImageSharp.Benchmarks.Codecs;
/// <summary>
/// Benchmarks saving png files using different quantizers.
/// System.Drawing cannot save indexed png files so we cannot compare.
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class EncodeIndexedPng
{
/// <summary>
/// Benchmarks saving png files using different quantizers.
/// System.Drawing cannot save indexed png files so we cannot compare.
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class EncodeIndexedPng
{
// System.Drawing needs this.
private Stream bmpStream;
private Image<Rgba32> bmpCore;
// System.Drawing needs this.
private Stream bmpStream;
private Image<Rgba32> 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<Rgba32>(this.bmpStream);
this.bmpStream.Position = 0;
}
this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Bmp.Car));
this.bmpCore = Image.Load<Rgba32>(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);
}
}

82
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<Rgba32> bmpCore;
// System.Drawing needs this.
private Stream bmpStream;
private SDImage bmpDrawing;
private Image<Rgba32> 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<Rgba32>(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<Rgba32>(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);
}
}

109
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<Bgr24>(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<Bgr24>(this.data);
return image.Width;
}
private class PfimAllocator : IImageAllocator
{
private int rented;
private readonly ArrayPool<byte> shared = ArrayPool<byte>.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<byte> shared = ArrayPool<byte>.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 |
*/
}

72
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<Rgba32> tga;
private MagickImage tgaMagick;
private Image<Rgba32> 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<Rgba32>(this.TestImageFullPath);
this.tgaMagick = new MagickImage(this.TestImageFullPath);
}
this.tga = Image.Load<Rgba32>(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);
}
}

108
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<Rgba32>(ms))
{
using (var ms = new MemoryStream(this.data))
using (var image = Image.Load<Rgba32>(ms))
{
return image.Size();
}
return image.Size();
}
}
}

179
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.PixelFormats;
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<Rgba32> core;
private Stream stream;
private SDImage drawing;
private Image<Rgba32> 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<Rgba32>(this.stream);
this.stream.Position = 0;
this.drawing = SDImage.FromStream(this.stream);
}
this.stream = File.OpenRead(this.TestImageFullPath);
this.core = Image.Load<Rgba32>(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;
}
}

178
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.Formats.Webp;
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<Rgba32>(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<Rgba32>(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<Rgba32>(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<Rgba32>(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 |
*/
}

228
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.Formats.Webp;
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<Rgba32> webp;
private MagickImage webpMagick;
private Image<Rgba32> 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<Rgba32>(this.TestImageFullPath);
this.webpMagick = new MagickImage(this.TestImageFullPath);
}
this.webp = Image.Load<Rgba32>(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 |
*/
}

122
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.Memory;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
{
public abstract class FromRgba32Bytes<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private IMemoryOwner<TPixel> destination;
private IMemoryOwner<byte> source;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk;
private Configuration configuration;
public abstract class FromRgba32Bytes<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private IMemoryOwner<TPixel> destination;
[Params(
128,
1024,
2048)]
public int Count { get; set; }
private IMemoryOwner<byte> source;
[GlobalSetup]
public void Setup()
{
this.configuration = Configuration.Default;
this.destination = this.configuration.MemoryAllocator.Allocate<TPixel>(this.Count);
this.source = this.configuration.MemoryAllocator.Allocate<byte>(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<byte> s = this.source.GetSpan();
Span<TPixel> d = this.destination.GetSpan();
[GlobalSetup]
public void Setup()
{
this.configuration = Configuration.Default;
this.destination = this.configuration.MemoryAllocator.Allocate<TPixel>(this.Count);
this.source = this.configuration.MemoryAllocator.Allocate<byte>(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<TPixel>().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count);
}
// [Benchmark]
public void Naive()
{
Span<byte> s = this.source.GetSpan();
Span<TPixel> d = this.destination.GetSpan();
[Benchmark]
public void OptimizedBulk()
for (int i = 0; i < this.Count; i++)
{
PixelOperations<TPixel>.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<Rgba32>
[Benchmark(Baseline = true)]
public void CommonBulk()
{
new PixelOperations<TPixel>().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count);
}
public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes<Bgra32>
[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<TPixel>.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count);
}
}
public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes<Rgba32>
{
}
public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes<Bgra32>
{
// 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 |
}

236
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.Memory;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
{
[Config(typeof(Config.ShortCore31))]
public abstract class FromVector4<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
protected IMemoryOwner<Vector4> source;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk;
protected IMemoryOwner<TPixel> destination;
[Config(typeof(Config.ShortCore31))]
public abstract class FromVector4<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
protected IMemoryOwner<Vector4> source;
protected Configuration Configuration => Configuration.Default;
protected IMemoryOwner<TPixel> 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<TPixel>(this.Count);
this.source = this.Configuration.MemoryAllocator.Allocate<Vector4>(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<TPixel>(this.Count);
this.source = this.Configuration.MemoryAllocator.Allocate<Vector4>(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<TPixel>().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<TPixel>.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
}
[Benchmark(Baseline = true)]
public void PixelOperations_Base()
{
new PixelOperations<TPixel>().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
}
public class FromVector4Rgba32 : FromVector4<Rgba32>
[Benchmark]
public void PixelOperations_Specialized()
{
[Benchmark]
public void FallbackIntrinsics128()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
}
}
SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
public class FromVector4Rgba32 : FromVector4<Rgba32>
{
[Benchmark]
public void FallbackIntrinsics128()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
[Benchmark]
public void ExtendedIntrinsic()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
[Benchmark]
public void ExtendedIntrinsic()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
[Benchmark]
public void UseHwIntrinsics()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
SimdUtils.HwIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
[Benchmark]
public void UseHwIntrinsics()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
private static ReadOnlySpan<byte> 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<float> src = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dest = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
private static ReadOnlySpan<byte> 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<byte>.Count;
[Benchmark]
public void UseAvx2_Grouped()
{
Span<float> src = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dest = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
ref Vector256<float> sourceBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(src));
ref Vector256<byte> destBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest));
int n = dest.Length / Vector<byte>.Count;
ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32);
Vector256<int> mask = Unsafe.As<byte, Vector256<int>>(ref maskBase);
ref Vector256<float> sourceBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(src));
ref Vector256<byte> destBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest));
var maxBytes = Vector256.Create(255f);
ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32);
Vector256<int> mask = Unsafe.As<byte, Vector256<int>>(ref maskBase);
for (int i = 0; i < n; i++)
{
ref Vector256<float> s = ref Unsafe.Add(ref sourceBase, i * 4);
var maxBytes = Vector256.Create(255f);
Vector256<float> f0 = s;
Vector256<float> f1 = Unsafe.Add(ref s, 1);
Vector256<float> f2 = Unsafe.Add(ref s, 2);
Vector256<float> f3 = Unsafe.Add(ref s, 3);
for (int i = 0; i < n; i++)
{
ref Vector256<float> 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<float> f0 = s;
Vector256<float> f1 = Unsafe.Add(ref s, 1);
Vector256<float> f2 = Unsafe.Add(ref s, 2);
Vector256<float> f3 = Unsafe.Add(ref s, 3);
Vector256<int> w0 = Avx.ConvertToVector256Int32(f0);
Vector256<int> w1 = Avx.ConvertToVector256Int32(f1);
Vector256<int> w2 = Avx.ConvertToVector256Int32(f2);
Vector256<int> 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<short> u0 = Avx2.PackSignedSaturate(w0, w1);
Vector256<short> u1 = Avx2.PackSignedSaturate(w2, w3);
Vector256<byte> b = Avx2.PackUnsignedSaturate(u0, u1);
b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte();
Vector256<int> w0 = Avx.ConvertToVector256Int32(f0);
Vector256<int> w1 = Avx.ConvertToVector256Int32(f1);
Vector256<int> w2 = Avx.ConvertToVector256Int32(f2);
Vector256<int> w3 = Avx.ConvertToVector256Int32(f3);
Unsafe.Add(ref destBase, i) = b;
}
}
Vector256<short> u0 = Avx2.PackSignedSaturate(w0, w1);
Vector256<short> u1 = Avx2.PackSignedSaturate(w2, w3);
Vector256<byte> b = Avx2.PackUnsignedSaturate(u0, u1);
b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<int> ConvertToInt32(Vector256<float> vf, Vector256<float> 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<int> ConvertToInt32(Vector256<float> vf, Vector256<float> 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 | - | - | - | - |
}

9
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<Rgb24>
{
[Config(typeof(Config.ShortMultiFramework))]
public class FromVector4_Rgb24 : FromVector4<Rgb24>
{
}
}
// 2020-11-02

148
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 | - | - | - | - |

90
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<Vector4>(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<Vector4>(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;
}

85
tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs

@ -7,53 +7,52 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk;
public abstract class Rgb24Bytes<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
public abstract class Rgb24Bytes<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
private IMemoryOwner<TPixel> source;
private IMemoryOwner<byte> destination;
private Configuration configuration;
[Params(16, 128, 1024)]
public int Count { get; set; }
[GlobalSetup]
public void Setup()
{
private IMemoryOwner<TPixel> source;
private IMemoryOwner<byte> 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<TPixel>(this.Count);
this.destination = this.configuration.MemoryAllocator.Allocate<byte>(this.Count * 3);
}
[GlobalCleanup]
public void Cleanup()
{
this.source.Dispose();
this.destination.Dispose();
}
[Benchmark(Baseline = true)]
public void CommonBulk() =>
new PixelOperations<TPixel>().ToRgb24Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
[Benchmark]
public void OptimizedBulk() =>
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
this.configuration = Configuration.Default;
this.source = this.configuration.MemoryAllocator.Allocate<TPixel>(this.Count);
this.destination = this.configuration.MemoryAllocator.Allocate<byte>(this.Count * 3);
}
public class Rgb24Bytes_Rgba32 : Rgb24Bytes<Rgba32>
[GlobalCleanup]
public void Cleanup()
{
this.source.Dispose();
this.destination.Dispose();
}
[Benchmark(Baseline = true)]
public void CommonBulk() =>
new PixelOperations<TPixel>().ToRgb24Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
[Benchmark]
public void OptimizedBulk() =>
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
}
public class Rgb24Bytes_Rgba32 : Rgb24Bytes<Rgba32>
{
}

104
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 | - | - | - | - |

164
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 | - | - | - | - |

110
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<WXYZShuffle4>(this.source, this.destination, default);
}
[Benchmark]
public void Shuffle4Channel()
{
SimdUtils.Shuffle4<WXYZShuffle4>(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 | - | - | - | - |

110
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 | - | - | - | - |

126
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.Memory;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
{
public abstract class ToRgba32Bytes<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private IMemoryOwner<TPixel> source;
private IMemoryOwner<byte> 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<TPixel>(this.Count);
this.destination = this.configuration.MemoryAllocator.Allocate<byte>(this.Count * 4);
}
public abstract class ToRgba32Bytes<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private IMemoryOwner<TPixel> source;
[GlobalCleanup]
public void Cleanup()
{
this.source.Dispose();
this.destination.Dispose();
}
private IMemoryOwner<byte> destination;
// [Benchmark]
public void Naive()
{
Span<TPixel> s = this.source.GetSpan();
Span<byte> 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<TPixel>().ToRgba32Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
[Benchmark]
public void OptimizedBulk() =>
PixelOperations<TPixel>.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<Rgba32>
[GlobalSetup]
public void Setup()
{
this.configuration = Configuration.Default;
this.source = this.configuration.MemoryAllocator.Allocate<TPixel>(this.Count);
this.destination = this.configuration.MemoryAllocator.Allocate<byte>(this.Count * 4);
}
public class ToRgba32Bytes_FromArgb32 : ToRgba32Bytes<Argb32>
[GlobalCleanup]
public void Cleanup()
{
this.source.Dispose();
this.destination.Dispose();
}
public class ToRgba32Bytes_FromBgra32 : ToRgba32Bytes<Bgra32>
// [Benchmark]
public void Naive()
{
Span<TPixel> s = this.source.GetSpan();
Span<byte> 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<TPixel>().ToRgba32Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
[Benchmark]
public void OptimizedBulk() =>
PixelOperations<TPixel>.Instance.ToRgba32Bytes(
this.configuration,
this.source.GetSpan(),
this.destination.GetSpan(),
this.Count);
}
public class ToRgba32Bytes_FromRgba32 : ToRgba32Bytes<Rgba32>
{
}
public class ToRgba32Bytes_FromArgb32 : ToRgba32Bytes<Argb32>
{
}
public class ToRgba32Bytes_FromBgra32 : ToRgba32Bytes<Bgra32>
{
}

76
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.Memory;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
{
public abstract class ToVector4<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
protected IMemoryOwner<TPixel> source;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk;
protected IMemoryOwner<Vector4> destination;
public abstract class ToVector4<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
protected IMemoryOwner<TPixel> source;
protected Configuration Configuration => Configuration.Default;
protected IMemoryOwner<Vector4> 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<TPixel>(this.Count);
this.destination = this.Configuration.MemoryAllocator.Allocate<Vector4>(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<TPixel>(this.Count);
this.destination = this.Configuration.MemoryAllocator.Allocate<Vector4>(this.Count);
}
// [Benchmark]
public void Naive()
{
Span<TPixel> s = this.source.GetSpan();
Span<Vector4> 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<TPixel> s = this.source.GetSpan();
Span<Vector4> d = this.destination.GetSpan();
[Benchmark]
public void PixelOperations_Specialized()
for (int i = 0; i < this.Count; i++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
d[i] = s[i].ToVector4();
}
}
[Benchmark]
public void PixelOperations_Specialized()
{
PixelOperations<TPixel>.Instance.ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
}
}

63
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs

@ -6,39 +6,38 @@ using BenchmarkDotNet.Attributes;
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<Bgra32>
{
[Config(typeof(Config.ShortMultiFramework))]
public class ToVector4_Bgra32 : ToVector4<Bgra32>
[Benchmark(Baseline = true)]
public void PixelOperations_Base()
{
[Benchmark(Baseline = true)]
public void PixelOperations_Base()
{
new PixelOperations<Bgra32>().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<Bgra32>().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 |
}

21
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs

@ -6,19 +6,18 @@ using BenchmarkDotNet.Attributes;
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<Rgb24>
{
[Config(typeof(Config.ShortMultiFramework))]
public class ToVector4_Rgb24 : ToVector4<Rgb24>
[Benchmark(Baseline = true)]
public void PixelOperations_Base()
{
[Benchmark(Baseline = true)]
public void PixelOperations_Base()
{
new PixelOperations<Rgb24>().ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
}
new PixelOperations<Rgb24>().ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
}
}

254
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 BenchmarkDotNet.Attributes;
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<Rgba32>
{
[Config(typeof(Config.ShortCore31))]
public class ToVector4_Rgba32 : ToVector4<Rgba32>
[Benchmark]
public void FallbackIntrinsics128()
{
[Benchmark]
public void FallbackIntrinsics128()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(sBytes, dFloats);
}
SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(sBytes, dFloats);
}
[Benchmark]
public void PixelOperations_Base()
=> new PixelOperations<Rgba32>().ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
[Benchmark]
public void PixelOperations_Base()
=> new PixelOperations<Rgba32>().ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
[Benchmark]
public void ExtendedIntrinsics()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
[Benchmark]
public void ExtendedIntrinsics()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
}
SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
}
[Benchmark]
public void HwIntrinsics()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
[Benchmark]
public void HwIntrinsics()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
}
SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
}
// [Benchmark]
public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
// [Benchmark]
public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
int n = dFloats.Length / Vector<byte>.Count;
int n = dFloats.Length / Vector<byte>.Count;
ref Vector<byte> sourceBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference((ReadOnlySpan<byte>)sBytes));
ref Vector<float> destBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(dFloats));
ref Vector<uint> destBaseU = ref Unsafe.As<Vector<float>, Vector<uint>>(ref destBase);
ref Vector<byte> sourceBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference((ReadOnlySpan<byte>)sBytes));
ref Vector<float> destBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(dFloats));
ref Vector<uint> destBaseU = ref Unsafe.As<Vector<float>, Vector<uint>>(ref destBase);
for (int i = 0; i < n; i++)
{
Vector<byte> b = Unsafe.Add(ref sourceBase, i);
for (int i = 0; i < n; i++)
{
Vector<byte> b = Unsafe.Add(ref sourceBase, i);
Vector.Widen(b, out Vector<ushort> s0, out Vector<ushort> s1);
Vector.Widen(s0, out Vector<uint> w0, out Vector<uint> w1);
Vector.Widen(s1, out Vector<uint> w2, out Vector<uint> w3);
Vector.Widen(b, out Vector<ushort> s0, out Vector<ushort> s1);
Vector.Widen(s0, out Vector<uint> w0, out Vector<uint> w1);
Vector.Widen(s1, out Vector<uint> w2, out Vector<uint> w3);
ref Vector<uint> 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<uint> 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<float>.Count;
var scale = new Vector<float>(1f / 255f);
n = dFloats.Length / Vector<float>.Count;
var scale = new Vector<float>(1f / 255f);
for (int i = 0; i < n; i++)
{
ref Vector<float> dRef = ref Unsafe.Add(ref destBase, i);
for (int i = 0; i < n; i++)
{
ref Vector<float> 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<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
int n = dFloats.Length / Vector<byte>.Count;
ref Vector<byte> sourceBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference((ReadOnlySpan<byte>)sBytes));
ref Vector<float> destBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(dFloats));
var scale = new Vector<float>(1f / 255f);
for (int i = 0; i < n; i++)
{
Vector<byte> b = Unsafe.Add(ref sourceBase, i);
Vector.Widen(b, out Vector<ushort> s0, out Vector<ushort> s1);
Vector.Widen(s0, out Vector<uint> w0, out Vector<uint> w1);
Vector.Widen(s1, out Vector<uint> w2, out Vector<uint> w3);
Vector<float> f0 = ConvertToNormalizedSingle(w0, scale);
Vector<float> f1 = ConvertToNormalizedSingle(w1, scale);
Vector<float> f2 = ConvertToNormalizedSingle(w2, scale);
Vector<float> f3 = ConvertToNormalizedSingle(w3, scale);
ref Vector<float> 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<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
int n = dFloats.Length / Vector<byte>.Count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<float> ConvertToNormalizedSingle(Vector<uint> u, Vector<float> scale)
ref Vector<byte> sourceBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference((ReadOnlySpan<byte>)sBytes));
ref Vector<float> destBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(dFloats));
var scale = new Vector<float>(1f / 255f);
for (int i = 0; i < n; i++)
{
var vi = Vector.AsVectorInt32(u);
var v = Vector.ConvertToSingle(vi);
v *= scale;
return v;
Vector<byte> b = Unsafe.Add(ref sourceBase, i);
Vector.Widen(b, out Vector<ushort> s0, out Vector<ushort> s1);
Vector.Widen(s0, out Vector<uint> w0, out Vector<uint> w1);
Vector.Widen(s1, out Vector<uint> w2, out Vector<uint> w3);
Vector<float> f0 = ConvertToNormalizedSingle(w0, scale);
Vector<float> f1 = ConvertToNormalizedSingle(w1, scale);
Vector<float> f2 = ConvertToNormalizedSingle(w2, scale);
Vector<float> f3 = ConvertToNormalizedSingle(w3, scale);
ref Vector<float> 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<float> ConvertToNormalizedSingle(Vector<uint> u, Vector<float> 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!
*/
}

90
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<Vector4>(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<Vector4>(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;
}

27
tests/ImageSharp.Benchmarks/Color/ColorEquality.cs

@ -5,22 +5,21 @@ using BenchmarkDotNet.Attributes;
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));
}
}

33
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs

@ -9,28 +9,27 @@ using SixLabors.ImageSharp.ColorSpaces;
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<XYZColor, LabColor> ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.D50).ToLab(Illuminants.D50).Build();
private static readonly IColorConverter<XYZColor, LabColor> 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;
}
}

33
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs

@ -9,28 +9,27 @@ using SixLabors.ImageSharp.ColorSpaces;
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<XYZColor, HunterLabColor> ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.C).ToHunterLab(Illuminants.C).Build();
private static readonly IColorConverter<XYZColor, HunterLabColor> 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;
}
}

33
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs

@ -8,28 +8,27 @@ using Colourful;
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<XYZColor, LMSColor> ColourfulConverter = new ConverterBuilder().FromXYZ().ToLMS().Build();
private static readonly IColorConverter<XYZColor, LMSColor> 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;
}
}

33
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs

@ -8,28 +8,27 @@ using Colourful;
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<XYZColor, RGBColor> ColourfulConverter = new ConverterBuilder().FromXYZ(RGBWorkingSpaces.sRGB.WhitePoint).ToRGB(RGBWorkingSpaces.sRGB).Build();
private static readonly IColorConverter<XYZColor, RGBColor> 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;
}
}

33
tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs

@ -8,28 +8,27 @@ using Colourful;
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<RGBColor, RGBColor> ColourfulConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.WideGamutRGB).ToRGB(RGBWorkingSpaces.sRGB).Build();
private static readonly IColorConverter<RGBColor, RGBColor> 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);
}
}

81
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);
}
}

123
tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs

@ -5,75 +5,74 @@ using System.Runtime.Intrinsics.X86;
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"));
}
}
}

55
tests/ImageSharp.Benchmarks/Config.cs

@ -11,44 +11,43 @@ using BenchmarkDotNet.Environments;
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
}
}

114
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 | - | - | - | - |

178
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<float> matrix;
private DenseMatrix<float> 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<float>(this.array2D);
this.matrix = new DenseMatrix<float>(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<float> a = this.matrix;
for (int i = this.Min; i < this.Max; i++)
{
float s = 0;
DenseMatrix<float> 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;
}
}

82
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--;
}
}
}

60
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;
}
}

88
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 |
}

132
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;
}
}

50
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);
}
}

77
tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs

@ -5,56 +5,55 @@ using System.Numerics;
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 |
}

29
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);
}
}

45
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 |
}

58
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;
}
}

28
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 |
}

69
tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs

@ -5,42 +5,41 @@ using BenchmarkDotNet.Attributes;
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<Rgba32> 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<Rgba32> 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<Rgba32>(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<Rgba32>(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 |
}

430
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;
/// <summary>
/// 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)
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class CopyBuffers
{
/// <summary>
/// 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)
/// </summary>
[Config(typeof(Config.ShortMultiFramework))]
public class CopyBuffers
private byte[] destArray;
private MemoryHandle destHandle;
private Memory<byte> destMemory;
private byte[] sourceArray;
private MemoryHandle sourceHandle;
private Memory<byte> 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<byte>(this.sourceArray);
this.sourceHandle = this.sourceMemory.Pin();
this.destArray = new byte[this.Count];
this.destMemory = new Memory<byte>(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<byte> destMemory;
private byte[] sourceArray;
private MemoryHandle sourceHandle;
private Memory<byte> 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<byte>(this.sourceArray);
this.sourceHandle = this.sourceMemory.Pin();
this.destArray = new byte[this.Count];
this.destMemory = new Memory<byte>(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 | - | - | - | - |
}

114
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 | - | - | - | - |

31
tests/ImageSharp.Benchmarks/General/GetSetPixel.cs

@ -5,24 +5,23 @@ using System.Drawing;
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<Rgba32>(400, 400);
image[200, 200] = Color.White;
return image[200, 200];
}
[Benchmark(Description = "ImageSharp GetSet pixel")]
public Rgba32 GetSetImageSharp()
{
using var image = new Image<Rgba32>(400, 400);
image[200, 200] = Color.White;
return image[200, 200];
}
}

407
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;
/// <summary>
/// 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.
/// </summary>
internal sealed unsafe class BufferedReadStreamWrapper : IDisposable
{
/// <summary>
/// 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.
/// </summary>
internal sealed unsafe class BufferedReadStreamWrapper : IDisposable
{
/// <summary>
/// The length, in bytes, of the underlying buffer.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="BufferedReadStreamWrapper"/> class.
/// </summary>
/// <param name="stream">The input stream.</param>
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<byte>.Shared.Rent(BufferLength);
this.readBufferHandle = new Memory<byte>(this.readBuffer).Pin();
this.pinnedReadBuffer = (byte*)this.readBufferHandle.Pointer;
/// <summary>
/// Initializes a new instance of the <see cref="BufferedReadStreamWrapper"/> class.
/// </summary>
/// <param name="stream">The input stream.</param>
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();
}
/// <summary>
/// Gets the length, in bytes, of the stream.
/// </summary>
public long Length { get; }
this.stream = stream;
this.Position = (int)stream.Position;
this.Length = stream.Length;
/// <summary>
/// Gets or sets the current position within the stream.
/// </summary>
public long Position
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.readerPosition;
this.readBuffer = ArrayPool<byte>.Shared.Rent(BufferLength);
this.readBufferHandle = new Memory<byte>(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;
}
/// <summary>
/// Gets the length, in bytes, of the stream.
/// </summary>
public long Length { get; }
/// <summary>
/// Gets or sets the current position within the stream.
/// </summary>
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;
}
/// <inheritdoc/>
public void Dispose()
return this.readerPosition;
}
/// <inheritdoc/>
public void Dispose()
{
if (!this.isDisposed)
{
if (!this.isDisposed)
{
this.isDisposed = true;
this.readBufferHandle.Dispose();
ArrayPool<byte>.Shared.Return(this.readBuffer);
this.Flush();
}
this.isDisposed = true;
this.readBufferHandle.Dispose();
ArrayPool<byte>.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);
}
}
}

425
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 | - | - | - | - |
*/

27
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<T>
where T : struct, ITestPixel<T>
{
public interface ITestPixel<T>
where T : struct, ITestPixel<T>
{
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);
}

276
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;
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<T>
where T : struct, ITestPixel<T>
{
internal struct ConversionRunner<T>
where T : struct, ITestPixel<T>
{
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<TestRgba> CompatibleMemLayoutRunner;
internal ConversionRunner<TestRgba> CompatibleMemLayoutRunner;
internal ConversionRunner<TestArgb> PermutedRunnerRgbaToArgb;
internal ConversionRunner<TestArgb> 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<TestRgba>(this.Count);
this.PermutedRunnerRgbaToArgb = new ConversionRunner<TestArgb>(this.Count);
}
[GlobalSetup]
public void Setup()
{
this.CompatibleMemLayoutRunner = new ConversionRunner<TestRgba>(this.Count);
this.PermutedRunnerRgbaToArgb = new ConversionRunner<TestArgb>(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<TestRgba, Rgba32>(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<TestRgba, Rgba32>(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<byte> source = MemoryMarshal.Cast<Rgba32, byte>(this.PermutedRunnerRgbaToArgb.Source);
Span<byte> dest = MemoryMarshal.Cast<TestArgb, byte>(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<byte> source = MemoryMarshal.Cast<Rgba32, byte>(this.PermutedRunnerRgbaToArgb.Source);
Span<byte> dest = MemoryMarshal.Cast<TestArgb, byte>(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 |
*/
}

199
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs

@ -9,138 +9,137 @@ using BenchmarkDotNet.Attributes;
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<TestRgbaVector>
{
[StructLayout(LayoutKind.Sequential)]
private struct TestRgbaVector : ITestPixel<TestRgbaVector>
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<T>
where T : struct, ITestPixel<T>
{
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<T>
where T : struct, ITestPixel<T>
[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<TestArgb> nonVectorRunner;
private ConversionRunner<TestRgbaVector> vectorRunner;
private ConversionRunner<TestArgb> nonVectorRunner;
[Params(32)]
public int Count { get; set; }
private ConversionRunner<TestRgbaVector> vectorRunner;
[GlobalSetup]
public void Setup()
{
this.nonVectorRunner = new ConversionRunner<TestArgb>(this.Count);
this.vectorRunner = new ConversionRunner<TestRgbaVector>(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<TestArgb>(this.Count);
this.vectorRunner = new ConversionRunner<TestRgbaVector>(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
*/

159
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs

@ -6,106 +6,105 @@ using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion;
/// <summary>
/// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer?
/// 1. Rgba32 ToRgba32();
/// OR
/// 2. void CopyToRgba32(ref Rgba32 dest);
/// ?
/// </summary>
public class PixelConversion_ConvertToRgba32
{
/// <summary>
/// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer?
/// 1. Rgba32 ToRgba32();
/// OR
/// 2. void CopyToRgba32(ref Rgba32 dest);
/// ?
/// </summary>
public class PixelConversion_ConvertToRgba32
private struct ConversionRunner<T>
where T : struct, ITestPixel<T>
{
private struct ConversionRunner<T>
where T : struct, ITestPixel<T>
{
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<TestRgba> compatibleMemoryLayoutRunner;
private ConversionRunner<TestArgb> permutedRunner;
private ConversionRunner<TestRgba> compatibleMemoryLayoutRunner;
[Params(32)]
public int Count { get; set; }
private ConversionRunner<TestArgb> permutedRunner;
[GlobalSetup]
public void Setup()
{
this.compatibleMemoryLayoutRunner = new ConversionRunner<TestRgba>(this.Count);
this.permutedRunner = new ConversionRunner<TestArgb>(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<TestRgba>(this.Count);
this.permutedRunner = new ConversionRunner<TestArgb>(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 |
*/

155
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs

@ -6,110 +6,109 @@ using BenchmarkDotNet.Attributes;
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<T>
where T : struct, ITestPixel<T>
{
private struct ConversionRunner<T>
where T : struct, ITestPixel<T>
{
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<TestRgba> compatibleMemoryLayoutRunner;
private ConversionRunner<TestArgb> permutedRunner;
private ConversionRunner<TestRgba> compatibleMemoryLayoutRunner;
[Params(32)]
public int Count { get; set; }
private ConversionRunner<TestArgb> permutedRunner;
[GlobalSetup]
public void Setup()
{
this.compatibleMemoryLayoutRunner = new ConversionRunner<TestRgba>(this.Count);
this.permutedRunner = new ConversionRunner<TestArgb>(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<TestRgba>(this.Count);
this.permutedRunner = new ConversionRunner<TestArgb>(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 |

111
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs

@ -5,79 +5,78 @@ using System.Numerics;
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<T>
where T : struct, ITestPixel<T>
{
private struct ConversionRunner<T>
where T : struct, ITestPixel<T>
{
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<TestRgba> runner;
[Params(32)]
public int Count { get; set; }
private ConversionRunner<TestRgba> runner;
[GlobalSetup]
public void Setup()
{
this.runner = new ConversionRunner<TestRgba>(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<TestRgba>(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 |
}

127
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs

@ -5,93 +5,92 @@ using System.Numerics;
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<T>
where T : struct, ITestPixel<T>
{
private struct ConversionRunner<T>
where T : struct, ITestPixel<T>
{
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<TestRgba> runner;
[Params(32)]
public int Count { get; set; }
private ConversionRunner<TestRgba> runner;
[GlobalSetup]
public void Setup()
{
this.runner = new ConversionRunner<TestRgba>(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<TestRgba>(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 |
}

454
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 System.Runtime.Intrinsics.X86;
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<byte> r = this.rBuf;
Span<byte> g = this.rBuf;
Span<byte> b = this.rBuf;
Span<Rgb24> rgb = this.rgbBuf;
[Benchmark]
public void Rgb24_Scalar_PerElement_Span()
{
Span<byte> r = this.rBuf;
Span<byte> g = this.rBuf;
Span<byte> b = this.rBuf;
Span<Rgb24> 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<byte, Byte8>(ref this.rBuf[0]);
ref Byte8 g = ref Unsafe.As<byte, Byte8>(ref this.rBuf[0]);
ref Byte8 b = ref Unsafe.As<byte, Byte8>(ref this.rBuf[0]);
ref Rgb24 rgb = ref this.rgbBuf[0];
[Benchmark]
public void Rgb24_Scalar_PerElement_Batched8()
{
ref Byte8 r = ref Unsafe.As<byte, Byte8>(ref this.rBuf[0]);
ref Byte8 g = ref Unsafe.As<byte, Byte8>(ref this.rBuf[0]);
ref Byte8 b = ref Unsafe.As<byte, Byte8>(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<byte, Byte4>(ref this.rBuf[0]);
ref Byte4 g = ref Unsafe.As<byte, Byte4>(ref this.rBuf[0]);
ref Byte4 b = ref Unsafe.As<byte, Byte4>(ref this.rBuf[0]);
ref Rgb24 rgb = ref this.rgbBuf[0];
[Benchmark]
public void Rgb24_Scalar_PerElement_Batched4()
{
ref Byte4 r = ref Unsafe.As<byte, Byte4>(ref this.rBuf[0]);
ref Byte4 g = ref Unsafe.As<byte, Byte4>(ref this.rBuf[0]);
ref Byte4 b = ref Unsafe.As<byte, Byte4>(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<float> rBase = ref Unsafe.As<float, Vector256<float>>(ref this.rFloat[0]);
ref Vector256<float> gBase = ref Unsafe.As<float, Vector256<float>>(ref this.gFloat[0]);
ref Vector256<float> bBase = ref Unsafe.As<float, Vector256<float>>(ref this.bFloat[0]);
ref Vector256<float> resultBase = ref Unsafe.As<float, Vector256<float>>(ref this.rgbaFloat[0]);
[Benchmark(Baseline = true)]
public void Rgba32_Avx2_Float()
{
ref Vector256<float> rBase = ref Unsafe.As<float, Vector256<float>>(ref this.rFloat[0]);
ref Vector256<float> gBase = ref Unsafe.As<float, Vector256<float>>(ref this.gFloat[0]);
ref Vector256<float> bBase = ref Unsafe.As<float, Vector256<float>>(ref this.bFloat[0]);
ref Vector256<float> resultBase = ref Unsafe.As<float, Vector256<float>>(ref this.rgbaFloat[0]);
int count = this.Count / Vector256<float>.Count;
int count = this.Count / Vector256<float>.Count;
ref byte control = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
ref byte control = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
var va = Vector256.Create(1F);
var va = Vector256.Create(1F);
for (int i = 0; i < count; i++)
{
Vector256<float> r = Unsafe.Add(ref rBase, i);
Vector256<float> g = Unsafe.Add(ref gBase, i);
Vector256<float> b = Unsafe.Add(ref bBase, i);
for (int i = 0; i < count; i++)
{
Vector256<float> r = Unsafe.Add(ref rBase, i);
Vector256<float> g = Unsafe.Add(ref gBase, i);
Vector256<float> 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<float> vte = Avx.UnpackLow(r, b);
Vector256<float> vto = Avx.UnpackLow(g, va);
Vector256<float> vte = Avx.UnpackLow(r, b);
Vector256<float> vto = Avx.UnpackLow(g, va);
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4);
ref Vector256<float> 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<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf;
ReadOnlySpan<byte> b = this.rBuf;
Span<Rgb24> rgb = this.rgbBuf;
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb);
}
[Benchmark]
public void Rgb24_Avx2_Bytes()
{
ReadOnlySpan<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf;
ReadOnlySpan<byte> b = this.rBuf;
Span<Rgb24> rgb = this.rgbBuf;
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb);
}
[Benchmark]
public void Rgba32_Avx2_Bytes()
{
ReadOnlySpan<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf;
ReadOnlySpan<byte> b = this.rBuf;
Span<Rgba32> rgb = this.rgbaBuf;
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb);
}
[Benchmark]
public void Rgba32_Avx2_Bytes()
{
ReadOnlySpan<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf;
ReadOnlySpan<byte> b = this.rBuf;
Span<Rgba32> 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 |
}

276
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<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
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<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
{
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<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Argb32, uint>(ref this.dest[0]);
[Benchmark]
public void BitOps()
for (int i = 0; i < this.Count; i++)
{
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Argb32, uint>(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<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Argb32, ulong>(ref this.dest[0]);
[Benchmark]
public void BitOps_GroupAsULong()
{
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Argb32, ulong>(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
{
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Argb32"/>.
/// </summary>
/// <returns>The argb value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static uint ToArgb32(uint packedRgba)
{
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Argb32"/>.
/// </summary>
/// <returns>The argb value.</returns>
[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);
}
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Bgra32"/>.
/// </summary>
/// <returns>The bgra value.</returns>
[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 |
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Bgra32"/>.
/// </summary>
/// <returns>The bgra value.</returns>
[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 |
}

626
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 BenchmarkDotNet.Attributes;
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<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
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<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
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<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
{
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<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Bgra32, uint>(ref this.dest[0]);
[Benchmark]
public void BitOps()
for (int i = 0; i < this.Count; i++)
{
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Bgra32, uint>(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<Rgba32, Tuple4OfUInt32>(ref this.source[0]);
ref Tuple4OfUInt32 dBase = ref Unsafe.As<Bgra32, Tuple4OfUInt32>(ref this.dest[0]);
for (int i = 0; i < this.Count / 4; i++)
{
ref Tuple4OfUInt32 sBase = ref Unsafe.As<Rgba32, Tuple4OfUInt32>(ref this.source[0]);
ref Tuple4OfUInt32 dBase = ref Unsafe.As<Bgra32, Tuple4OfUInt32>(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<Rgba32, Tuple4OfUInt32>(ref this.source[0]);
// [Benchmark]
public void Bitops_SingleTuple()
{
ref Tuple4OfUInt32 sBase = ref Unsafe.As<Rgba32, Tuple4OfUInt32>(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<uint> sBase = ref Unsafe.As<Rgba32, Octet<uint>>(ref this.source[0]);
ref Octet<uint> dBase = ref Unsafe.As<Bgra32, Octet<uint>>(ref this.dest[0]);
// [Benchmark]
public void Bitops_Simd()
{
ref Octet<uint> sBase = ref Unsafe.As<Rgba32, Octet<uint>>(ref this.source[0]);
ref Octet<uint> dBase = ref Unsafe.As<Bgra32, Octet<uint>>(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<uint> s, ref Octet<uint> d)
{
Vector<uint> sVec = Unsafe.As<Octet<uint>, Vector<uint>>(ref s);
var aMask = new Vector<uint>(0xFF00FF00);
var bMask = new Vector<uint>(0x00FF00FF);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void BitopsSimdImpl(ref Octet<uint> s, ref Octet<uint> d)
{
Vector<uint> sVec = Unsafe.As<Octet<uint>, Vector<uint>>(ref s);
var aMask = new Vector<uint>(0xFF00FF00);
var bMask = new Vector<uint>(0x00FF00FF);
Vector<uint> aa = sVec & aMask;
Vector<uint> bb = sVec & bMask;
Vector<uint> aa = sVec & aMask;
Vector<uint> bb = sVec & bMask;
B b = Unsafe.As<Vector<uint>, B>(ref bb);
B b = Unsafe.As<Vector<uint>, 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<uint> cc = Unsafe.As<C, Vector<uint>>(ref c);
Vector<uint> dd = aa + cc;
Vector<uint> cc = Unsafe.As<C, Vector<uint>>(ref c);
Vector<uint> dd = aa + cc;
d = Unsafe.As<Vector<uint>, Octet<uint>>(ref dd);
}
d = Unsafe.As<Vector<uint>, Octet<uint>>(ref dd);
}
// [Benchmark]
public void BitOps_Group2()
{
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Bgra32, uint>(ref this.dest[0]);
// [Benchmark]
public void BitOps_Group2()
for (int i = 0; i < this.Count; i++)
{
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Bgra32, uint>(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<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Bgra32, ulong>(ref this.dest[0]);
[Benchmark]
public void BitOps_GroupAsULong()
{
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Bgra32, ulong>(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<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Bgra32, ulong>(ref this.dest[0]);
// [Benchmark]
public void BitOps_GroupAsULong_V2()
{
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Bgra32, ulong>(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
{
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Argb32"/>.
/// </summary>
/// <returns>The argb value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static uint ToArgb32(uint packedRgba)
{
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Argb32"/>.
/// </summary>
/// <returns>The argb value.</returns>
[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);
}
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Bgra32"/>.
/// </summary>
/// <returns>The bgra value.</returns>
[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 |
/// <summary>
/// Converts a packed <see cref="Rgba32"/> to <see cref="Bgra32"/>.
/// </summary>
/// <returns>The bgra value.</returns>
[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 |
}

147
tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs

@ -6,89 +6,88 @@ using System.Runtime.CompilerServices;
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<TestArgb>
{
[StructLayout(LayoutKind.Sequential)]
public struct TestArgb : ITestPixel<TestArgb>
{
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;
}
}

111
tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs

@ -6,71 +6,70 @@ using System.Runtime.CompilerServices;
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<TestRgba>
{
[StructLayout(LayoutKind.Sequential)]
public struct TestRgba : ITestPixel<TestRgba>
{
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<Rgba32, TestRgba>(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba32(Rgba32 source)
{
this = Unsafe.As<Rgba32, TestRgba>(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba32(ref Rgba32 source)
{
this = Unsafe.As<Rgba32, TestRgba>(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba32(ref Rgba32 source)
{
this = Unsafe.As<Rgba32, TestRgba>(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<TestRgba, Rgba32>(ref this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32 ToRgba32()
{
return Unsafe.As<TestRgba, Rgba32>(ref this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyToRgba32(ref Rgba32 dest)
{
dest = Unsafe.As<TestRgba, Rgba32>(ref this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyToRgba32(ref Rgba32 dest)
{
dest = Unsafe.As<TestRgba, Rgba32>(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;
}
}

37
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<int, short>(ref x);
}
[Benchmark]
public short UnsafeCast()
{
int x = 5 * 2;
return Unsafe.As<int, short>(ref x);
}
[Benchmark]
public short UnsafeCastRef()
{
return Unsafe.As<int, short>(ref Unsafe.AsRef(5 * 2));
}
[Benchmark]
public short UnsafeCastRef()
{
return Unsafe.As<int, short>(ref Unsafe.AsRef(5 * 2));
}
}

102
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;
/// <summary>
/// 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!
/// </summary>
public class Vector4Constants
{
/// <summary>
/// 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!
/// </summary>
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();
}

69
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<uint>(this.testValue);
[Benchmark]
public void Simd()
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{
var v = new Vector<uint>(this.testValue);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{
var a = new Vector<uint>(this.input, i);
a = Vector.BitwiseOr(a, v);
a.CopyTo(this.result, i);
}
var a = new Vector<uint>(this.input, i);
a = Vector.BitwiseOr(a, v);
a.CopyTo(this.result, i);
}
}
}

69
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<float>(this.testValue);
[Benchmark]
public void Simd()
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{
var v = new Vector<float>(this.testValue);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{
var a = new Vector<float>(this.input, i);
a = a / v;
a.CopyTo(this.result, i);
}
var a = new Vector<float>(this.input, i);
a = a / v;
a.CopyTo(this.result, i);
}
}
}

71
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<uint>(this.testValue);
[Benchmark]
public void Simd()
{
var v = new Vector<uint>(this.testValue);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{
var a = new Vector<uint>(this.input, i);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{
var a = new Vector<uint>(this.input, i);
a = a / v;
a.CopyTo(this.result, i);
}
a = a / v;
a.CopyTo(this.result, i);
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save