mirror of https://github.com/SixLabors/ImageSharp
125 changed files with 8147 additions and 8364 deletions
@ -1,49 +1,47 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Tests; |
using SixLabors.ImageSharp.Tests; |
||||
using SDImage = System.Drawing.Image; |
using SDImage = System.Drawing.Image; |
||||
using SDSize = System.Drawing.Size; |
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))] |
private byte[] bmpBytes; |
||||
public class DecodeBmp |
|
||||
{ |
|
||||
private byte[] bmpBytes; |
|
||||
|
|
||||
private string TestImageFullPath |
private string TestImageFullPath |
||||
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void ReadImages() |
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)] |
[Params(TestImages.Bmp.Car)] |
||||
public string TestImage { get; set; } |
public string TestImage { get; set; } |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "System.Drawing Bmp")] |
[Benchmark(Baseline = true, Description = "System.Drawing Bmp")] |
||||
public SDSize BmpSystemDrawing() |
public SDSize BmpSystemDrawing() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.bmpBytes); |
using var memoryStream = new MemoryStream(this.bmpBytes); |
||||
using var image = SDImage.FromStream(memoryStream); |
using var image = SDImage.FromStream(memoryStream); |
||||
return image.Size; |
return image.Size; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "ImageSharp Bmp")] |
[Benchmark(Description = "ImageSharp Bmp")] |
||||
public Size BmpImageSharp() |
public Size BmpImageSharp() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.bmpBytes); |
using var memoryStream = new MemoryStream(this.bmpBytes); |
||||
using var image = Image.Load<Rgba32>(memoryStream); |
using var image = Image.Load<Rgba32>(memoryStream); |
||||
return new Size(image.Width, image.Height); |
return new Size(image.Width, image.Height); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,32 +1,30 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Drawing.Imaging; |
using System.Drawing.Imaging; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Formats.Bmp; |
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))] |
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; |
||||
public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded |
|
||||
{ |
|
||||
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; |
|
||||
|
|
||||
[Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] |
[Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] |
||||
public void EncodeBmpImageSharp() |
public void EncodeBmpImageSharp() |
||||
=> this.ForEachImageSharpImage((img, ms) => |
=> this.ForEachImageSharpImage((img, ms) => |
||||
{ |
{ |
||||
img.Save(ms, new BmpEncoder()); |
img.Save(ms, new BmpEncoder()); |
||||
return null; |
return null; |
||||
}); |
}); |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")] |
[Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")] |
||||
public void EncodeBmpSystemDrawing() |
public void EncodeBmpSystemDrawing() |
||||
=> this.ForEachSystemDrawingImage((img, ms) => |
=> this.ForEachSystemDrawingImage((img, ms) => |
||||
{ |
{ |
||||
img.Save(ms, ImageFormat.Bmp); |
img.Save(ms, ImageFormat.Bmp); |
||||
return null; |
return null; |
||||
}); |
}); |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,49 +1,47 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Tests; |
using SixLabors.ImageSharp.Tests; |
||||
using SDImage = System.Drawing.Image; |
using SDImage = System.Drawing.Image; |
||||
using SDSize = System.Drawing.Size; |
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))] |
private byte[] gifBytes; |
||||
public class DecodeGif |
|
||||
{ |
|
||||
private byte[] gifBytes; |
|
||||
|
|
||||
private string TestImageFullPath |
private string TestImageFullPath |
||||
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void ReadImages() |
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)] |
[Params(TestImages.Gif.Rings)] |
||||
public string TestImage { get; set; } |
public string TestImage { get; set; } |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "System.Drawing Gif")] |
[Benchmark(Baseline = true, Description = "System.Drawing Gif")] |
||||
public SDSize GifSystemDrawing() |
public SDSize GifSystemDrawing() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.gifBytes); |
using var memoryStream = new MemoryStream(this.gifBytes); |
||||
using var image = SDImage.FromStream(memoryStream); |
using var image = SDImage.FromStream(memoryStream); |
||||
return image.Size; |
return image.Size; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "ImageSharp Gif")] |
[Benchmark(Description = "ImageSharp Gif")] |
||||
public Size GifImageSharp() |
public Size GifImageSharp() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.gifBytes); |
using var memoryStream = new MemoryStream(this.gifBytes); |
||||
using var image = Image.Load<Rgba32>(memoryStream); |
using var image = Image.Load<Rgba32>(memoryStream); |
||||
return new Size(image.Width, image.Height); |
return new Size(image.Width, image.Height); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,43 +1,41 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Drawing.Imaging; |
using System.Drawing.Imaging; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Formats.Gif; |
using SixLabors.ImageSharp.Formats.Gif; |
||||
using SixLabors.ImageSharp.Processing; |
using SixLabors.ImageSharp.Processing; |
||||
using SixLabors.ImageSharp.Processing.Processors.Quantization; |
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))] |
[Params(InputImageCategory.AllImages)] |
||||
public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded |
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")] |
[Benchmark(Description = "EncodeGifMultiple - ImageSharp")] |
||||
public void EncodeGifImageSharp() |
public void EncodeGifImageSharp() |
||||
=> this.ForEachImageSharpImage((img, ms) => |
=> 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
|
Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) |
||||
var options = new GifEncoder |
}; |
||||
{ |
|
||||
Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) |
|
||||
}; |
|
||||
|
|
||||
img.Save(ms, options); |
img.Save(ms, options); |
||||
return null; |
return null; |
||||
}); |
}); |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "EncodeGifMultiple - System.Drawing")] |
[Benchmark(Baseline = true, Description = "EncodeGifMultiple - System.Drawing")] |
||||
public void EncodeGifSystemDrawing() |
public void EncodeGifSystemDrawing() |
||||
=> this.ForEachSystemDrawingImage((img, ms) => |
=> this.ForEachSystemDrawingImage((img, ms) => |
||||
{ |
{ |
||||
img.Save(ms, ImageFormat.Gif); |
img.Save(ms, ImageFormat.Gif); |
||||
return null; |
return null; |
||||
}); |
}); |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,51 +1,49 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
using System.Numerics; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
||||
|
|
||||
// ReSharper disable InconsistentNaming
|
// 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] |
[GlobalSetup] |
||||
public void Setup() |
public void Setup() |
||||
|
{ |
||||
|
if (Vector<float>.Count != 8) |
||||
{ |
{ |
||||
if (Vector<float>.Count != 8) |
throw new NotSupportedException("Vector<float>.Count != 8"); |
||||
{ |
|
||||
throw new NotSupportedException("Vector<float>.Count != 8"); |
|
||||
} |
|
||||
|
|
||||
for (short i = 0; i < Block8x8F.Size; i++) |
|
||||
{ |
|
||||
this.source[i] = i; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Baseline = true)] |
for (short i = 0; i < Block8x8F.Size; i++) |
||||
public void Scalar() |
|
||||
{ |
{ |
||||
this.dest.LoadFromInt16Scalar(ref this.source); |
this.source[i] = i; |
||||
} |
} |
||||
|
} |
||||
|
|
||||
[Benchmark] |
[Benchmark(Baseline = true)] |
||||
public void ExtendedAvx2() |
public void Scalar() |
||||
{ |
{ |
||||
this.dest.LoadFromInt16ExtendedAvx2(ref this.source); |
this.dest.LoadFromInt16Scalar(ref this.source); |
||||
} |
} |
||||
|
|
||||
// RESULT:
|
[Benchmark] |
||||
// Method | Mean | Error | StdDev | Scaled |
|
public void ExtendedAvx2() |
||||
// ------------- |---------:|----------:|----------:|-------:|
|
{ |
||||
// Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 |
|
this.dest.LoadFromInt16ExtendedAvx2(ref this.source); |
||||
// ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 |
|
|
||||
} |
} |
||||
|
|
||||
|
// 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 |
|
||||
} |
} |
||||
|
|||||
@ -1,61 +1,58 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Memory; |
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) |
protected ColorConversionBenchmark(int componentCount) |
||||
=> this.componentCount = componentCount; |
=> this.componentCount = componentCount; |
||||
|
|
||||
protected Buffer2D<float>[] Input { get; private set; } |
protected Buffer2D<float>[] Input { get; private set; } |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void Setup() |
public void Setup() |
||||
{ |
{ |
||||
this.Input = CreateRandomValues(this.componentCount, Count); |
this.Input = CreateRandomValues(this.componentCount, Count); |
||||
} |
} |
||||
|
|
||||
[GlobalCleanup] |
[GlobalCleanup] |
||||
public void Cleanup() |
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( |
private static Buffer2D<float>[] CreateRandomValues( |
||||
int componentCount, |
int componentCount, |
||||
int inputBufferLength, |
int inputBufferLength, |
||||
float minVal = 0f, |
float minVal = 0f, |
||||
float maxVal = 255f) |
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 values = new float[inputBufferLength]; |
||||
var buffers = new Buffer2D<float>[componentCount]; |
|
||||
for (int i = 0; i < componentCount; i++) |
|
||||
{ |
|
||||
var values = new float[inputBufferLength]; |
|
||||
|
|
||||
for (int j = 0; j < inputBufferLength; j++) |
for (int j = 0; j < inputBufferLength; j++) |
||||
{ |
{ |
||||
values[j] = ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; |
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); |
|
||||
} |
} |
||||
|
|
||||
return buffers; |
// no need to dispose when buffer is not array owner
|
||||
|
buffers[i] = Configuration.Default.MemoryAllocator.Allocate2D<float>(values.Length, 1); |
||||
} |
} |
||||
|
|
||||
|
return buffers; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,41 +1,39 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Tests; |
using SixLabors.ImageSharp.Tests; |
||||
using SDImage = System.Drawing.Image; |
using SDImage = System.Drawing.Image; |
||||
|
|
||||
// ReSharper disable InconsistentNaming
|
// 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>
|
protected override IEnumerable<string> InputImageSubfoldersOrFiles |
||||
/// An expensive Jpeg benchmark, running on a wide range of input images,
|
=> new[] |
||||
/// showing aggregate results.
|
{ |
||||
/// </summary>
|
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, |
||||
[Config(typeof(Config.ShortMultiFramework))] |
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, |
||||
public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase |
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, |
||||
{ |
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, |
||||
protected override IEnumerable<string> InputImageSubfoldersOrFiles |
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, |
||||
=> 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)] |
[Params(InputImageCategory.AllImages)] |
||||
public override InputImageCategory InputCategory { get; set; } |
public override InputImageCategory InputCategory { get; set; } |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void ImageSharp() |
public void ImageSharp() |
||||
=> this.ForEachStream(ms => Image.Load<Rgba32>(ms)); |
=> this.ForEachStream(ms => Image.Load<Rgba32>(ms)); |
||||
|
|
||||
[Benchmark(Baseline = true)] |
[Benchmark(Baseline = true)] |
||||
public void SystemDrawing() |
public void SystemDrawing() |
||||
=> this.ForEachStream(SDImage.FromStream); |
=> this.ForEachStream(SDImage.FromStream); |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,39 +1,37 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Formats; |
using SixLabors.ImageSharp.Formats; |
||||
using SixLabors.ImageSharp.Formats.Jpeg; |
using SixLabors.ImageSharp.Formats.Jpeg; |
||||
using SixLabors.ImageSharp.Tests; |
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))] |
private byte[] jpegBytes; |
||||
public class IdentifyJpeg |
|
||||
{ |
|
||||
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)] |
[Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] |
||||
public string TestImage { get; set; } |
public string TestImage { get; set; } |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void ReadImages() |
public void ReadImages() |
||||
|
{ |
||||
|
if (this.jpegBytes == null) |
||||
{ |
{ |
||||
if (this.jpegBytes == null) |
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); |
||||
{ |
|
||||
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public IImageInfo Identify() |
public IImageInfo Identify() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.jpegBytes); |
using var memoryStream = new MemoryStream(this.jpegBytes); |
||||
IImageDecoder decoder = new JpegDecoder(); |
IImageDecoder decoder = new JpegDecoder(); |
||||
return decoder.Identify(DecoderOptions.Default, memoryStream, default); |
return decoder.Identify(DecoderOptions.Default, memoryStream, default); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,67 +1,65 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Tests; |
using SixLabors.ImageSharp.Tests; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs |
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; |
|
||||
|
|
||||
[GlobalSetup] |
[Config(typeof(Config.ShortMultiFramework))] |
||||
public void ReadImages() |
public class DecodeFilteredPng |
||||
{ |
{ |
||||
this.filter0 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter0)); |
private byte[] filter0; |
||||
this.filter1 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.SubFilter3BytesPerPixel)); |
private byte[] filter1; |
||||
this.filter2 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.UpFilter)); |
private byte[] filter2; |
||||
this.filter3 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.PaethFilter3BytesPerPixel)); |
private byte[] filter3; |
||||
this.averageFilter3bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter3BytesPerPixel)); |
private byte[] averageFilter3bpp; |
||||
this.averageFilter4bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter4BytesPerPixel)); |
private byte[] averageFilter4bpp; |
||||
} |
|
||||
|
|
||||
[Benchmark(Baseline = true, Description = "None-filtered PNG file")] |
[GlobalSetup] |
||||
public Size PngFilter0() |
public void ReadImages() |
||||
=> LoadPng(this.filter0); |
{ |
||||
|
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")] |
[Benchmark(Baseline = true, Description = "None-filtered PNG file")] |
||||
public Size PngFilter1() |
public Size PngFilter0() |
||||
=> LoadPng(this.filter1); |
=> LoadPng(this.filter0); |
||||
|
|
||||
[Benchmark(Description = "Up-filtered PNG file")] |
[Benchmark(Description = "Sub-filtered PNG file")] |
||||
public Size PngFilter2() |
public Size PngFilter1() |
||||
=> LoadPng(this.filter2); |
=> LoadPng(this.filter1); |
||||
|
|
||||
[Benchmark(Description = "Average-filtered PNG file (3bpp)")] |
[Benchmark(Description = "Up-filtered PNG file")] |
||||
public Size PngAvgFilter1() |
public Size PngFilter2() |
||||
=> LoadPng(this.averageFilter3bpp); |
=> LoadPng(this.filter2); |
||||
|
|
||||
[Benchmark(Description = "Average-filtered PNG file (4bpp)")] |
[Benchmark(Description = "Average-filtered PNG file (3bpp)")] |
||||
public Size PngAvgFilter2() |
public Size PngAvgFilter1() |
||||
=> LoadPng(this.averageFilter4bpp); |
=> LoadPng(this.averageFilter3bpp); |
||||
|
|
||||
[Benchmark(Description = "Paeth-filtered PNG file")] |
[Benchmark(Description = "Average-filtered PNG file (4bpp)")] |
||||
public Size PngFilter4() |
public Size PngAvgFilter2() |
||||
=> LoadPng(this.filter3); |
=> LoadPng(this.averageFilter4bpp); |
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
[Benchmark(Description = "Paeth-filtered PNG file")] |
||||
private static Size LoadPng(byte[] bytes) |
public Size PngFilter4() |
||||
{ |
=> LoadPng(this.filter3); |
||||
using var image = Image.Load<Rgba32>(bytes); |
|
||||
return image.Size(); |
|
||||
} |
|
||||
|
|
||||
private static string TestImageFullPath(string path) |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, path); |
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); |
||||
} |
} |
||||
|
|||||
@ -1,49 +1,47 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Tests; |
using SixLabors.ImageSharp.Tests; |
||||
using SDImage = System.Drawing.Image; |
using SDImage = System.Drawing.Image; |
||||
using SDSize = System.Drawing.Size; |
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))] |
private byte[] pngBytes; |
||||
public class DecodePng |
|
||||
{ |
|
||||
private byte[] pngBytes; |
|
||||
|
|
||||
private string TestImageFullPath |
private string TestImageFullPath |
||||
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
||||
|
|
||||
[Params(TestImages.Png.Splash)] |
[Params(TestImages.Png.Splash)] |
||||
public string TestImage { get; set; } |
public string TestImage { get; set; } |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void ReadImages() |
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")] |
[Benchmark(Baseline = true, Description = "System.Drawing Png")] |
||||
public SDSize PngSystemDrawing() |
public SDSize PngSystemDrawing() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.pngBytes); |
using var memoryStream = new MemoryStream(this.pngBytes); |
||||
using var image = SDImage.FromStream(memoryStream); |
using var image = SDImage.FromStream(memoryStream); |
||||
return image.Size; |
return image.Size; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "ImageSharp Png")] |
[Benchmark(Description = "ImageSharp Png")] |
||||
public Size PngImageSharp() |
public Size PngImageSharp() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(this.pngBytes); |
using var memoryStream = new MemoryStream(this.pngBytes); |
||||
using var image = Image.Load<Rgba32>(memoryStream); |
using var image = Image.Load<Rgba32>(memoryStream); |
||||
return image.Size(); |
return image.Size(); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,56 +1,54 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using ImageMagick; |
using ImageMagick; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Tests; |
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))] |
private MagickImage tgaMagick; |
||||
public class EncodeTga |
private Image<Rgba32> tga; |
||||
{ |
|
||||
private MagickImage tgaMagick; |
|
||||
private Image<Rgba32> tga; |
|
||||
|
|
||||
private string TestImageFullPath |
private string TestImageFullPath |
||||
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
=> Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
||||
|
|
||||
[Params(TestImages.Tga.Bit24BottomLeft)] |
[Params(TestImages.Tga.Bit24BottomLeft)] |
||||
public string TestImage { get; set; } |
public string TestImage { get; set; } |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void ReadImages() |
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] |
[GlobalCleanup] |
||||
public void Cleanup() |
public void Cleanup() |
||||
{ |
{ |
||||
this.tga.Dispose(); |
this.tga.Dispose(); |
||||
this.tga = null; |
this.tga = null; |
||||
this.tgaMagick.Dispose(); |
this.tgaMagick.Dispose(); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "Magick Tga")] |
[Benchmark(Baseline = true, Description = "Magick Tga")] |
||||
public void MagickTga() |
public void MagickTga() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(); |
using var memoryStream = new MemoryStream(); |
||||
this.tgaMagick.Write(memoryStream, MagickFormat.Tga); |
this.tgaMagick.Write(memoryStream, MagickFormat.Tga); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "ImageSharp Tga")] |
[Benchmark(Description = "ImageSharp Tga")] |
||||
public void ImageSharpTga() |
public void ImageSharpTga() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(); |
using var memoryStream = new MemoryStream(); |
||||
this.tga.SaveAsTga(memoryStream); |
this.tga.SaveAsTga(memoryStream); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,87 +1,85 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk |
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; |
|
||||
|
|
||||
[GlobalSetup] |
[Config(typeof(Config.HwIntrinsics_SSE_AVX))] |
||||
public void Setup() |
public class Pad3Shuffle4Channel |
||||
{ |
{ |
||||
this.source = new byte[this.Count]; |
private static readonly DefaultPad3Shuffle4 Control = new DefaultPad3Shuffle4(1, 0, 3, 2); |
||||
new Random(this.Count).NextBytes(this.source); |
private static readonly XYZWPad3Shuffle4 ControlFast = default; |
||||
this.destination = new byte[this.Count * 4 / 3]; |
private byte[] source; |
||||
} |
private byte[] destination; |
||||
|
|
||||
[Params(96, 384, 768, 1536)] |
[GlobalSetup] |
||||
public int Count { get; set; } |
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] |
[Params(96, 384, 768, 1536)] |
||||
public void Pad3Shuffle4() |
public int Count { get; set; } |
||||
{ |
|
||||
SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); |
|
||||
} |
|
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void Pad3Shuffle4FastFallback() |
public void Pad3Shuffle4() |
||||
{ |
{ |
||||
SimdUtils.Pad3Shuffle4(this.source, this.destination, ControlFast); |
SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); |
||||
} |
|
||||
} |
} |
||||
|
|
||||
// 2020-10-30
|
[Benchmark] |
||||
// ##########
|
public void Pad3Shuffle4FastFallback() |
||||
//
|
{ |
||||
// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1)
|
SimdUtils.Pad3Shuffle4(this.source, this.destination, ControlFast); |
||||
// 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 | - | - | - | - |
|
|
||||
} |
} |
||||
|
|
||||
|
// 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 | - | - | - | - |
|
||||
|
|||||
@ -1,68 +1,66 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
using System.Numerics; |
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using System.Runtime.InteropServices; |
using System.Runtime.InteropServices; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk |
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; |
||||
{ |
|
||||
[Config(typeof(Config.ShortCore31))] |
|
||||
public class PremultiplyVector4 |
|
||||
{ |
|
||||
private static readonly Vector4[] Vectors = CreateVectors(); |
|
||||
|
|
||||
[Benchmark(Baseline = true)] |
[Config(typeof(Config.ShortCore31))] |
||||
public void PremultiplyBaseline() |
public class PremultiplyVector4 |
||||
{ |
{ |
||||
ref Vector4 baseRef = ref MemoryMarshal.GetReference<Vector4>(Vectors); |
private static readonly Vector4[] Vectors = CreateVectors(); |
||||
|
|
||||
for (int i = 0; i < Vectors.Length; i++) |
[Benchmark(Baseline = true)] |
||||
{ |
public void PremultiplyBaseline() |
||||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
{ |
||||
Premultiply(ref v); |
ref Vector4 baseRef = ref MemoryMarshal.GetReference<Vector4>(Vectors); |
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < Vectors.Length; i++) |
||||
public void Premultiply() |
|
||||
{ |
{ |
||||
Numerics.Premultiply(Vectors); |
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
||||
|
Premultiply(ref v); |
||||
} |
} |
||||
|
} |
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
[Benchmark] |
||||
private static void Premultiply(ref Vector4 source) |
public void Premultiply() |
||||
{ |
{ |
||||
float w = source.W; |
Numerics.Premultiply(Vectors); |
||||
source *= w; |
} |
||||
source.W = w; |
|
||||
} |
|
||||
|
|
||||
private static Vector4[] CreateVectors() |
[MethodImpl(InliningOptions.ShortMethod)] |
||||
{ |
private static void Premultiply(ref Vector4 source) |
||||
var rnd = new Random(42); |
{ |
||||
return GenerateRandomVectorArray(rnd, 2048, 0, 1); |
float w = source.W; |
||||
} |
source *= w; |
||||
|
source.W = w; |
||||
|
} |
||||
|
|
||||
private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) |
private static Vector4[] CreateVectors() |
||||
{ |
{ |
||||
var values = new Vector4[length]; |
var rnd = new Random(42); |
||||
|
return GenerateRandomVectorArray(rnd, 2048, 0, 1); |
||||
|
} |
||||
|
|
||||
for (int i = 0; i < length; i++) |
private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) |
||||
{ |
{ |
||||
ref Vector4 v = ref values[i]; |
var values = new Vector4[length]; |
||||
v.X = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
v.Y = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
v.Z = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
v.W = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
} |
|
||||
|
|
||||
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) |
return values; |
||||
=> ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; |
|
||||
} |
} |
||||
|
|
||||
|
private static float GetRandomFloat(Random rnd, float minVal, float maxVal) |
||||
|
=> ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; |
||||
} |
} |
||||
|
|||||
@ -1,64 +1,62 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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))] |
private static readonly DefaultShuffle3 Control = new DefaultShuffle3(1, 0, 2); |
||||
public class Shuffle3Channel |
private byte[] source; |
||||
{ |
private byte[] destination; |
||||
private static readonly DefaultShuffle3 Control = new DefaultShuffle3(1, 0, 2); |
|
||||
private byte[] source; |
|
||||
private byte[] destination; |
|
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void Setup() |
public void Setup() |
||||
{ |
{ |
||||
this.source = new byte[this.Count]; |
this.source = new byte[this.Count]; |
||||
new Random(this.Count).NextBytes(this.source); |
new Random(this.Count).NextBytes(this.source); |
||||
this.destination = new byte[this.Count]; |
this.destination = new byte[this.Count]; |
||||
} |
} |
||||
|
|
||||
[Params(96, 384, 768, 1536)] |
[Params(96, 384, 768, 1536)] |
||||
public int Count { get; set; } |
public int Count { get; set; } |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void Shuffle3() |
public void Shuffle3() |
||||
{ |
{ |
||||
SimdUtils.Shuffle3(this.source, this.destination, Control); |
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 | - | - | - | - |
|
||||
|
|||||
@ -1,95 +1,93 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk |
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; |
|
||||
|
|
||||
[GlobalSetup] |
[Config(typeof(Config.HwIntrinsics_SSE_AVX))] |
||||
public void Setup() |
public class Shuffle4Slice3Channel |
||||
{ |
{ |
||||
this.source = new byte[this.Count]; |
private static readonly DefaultShuffle4Slice3 Control = new DefaultShuffle4Slice3(1, 0, 3, 2); |
||||
new Random(this.Count).NextBytes(this.source); |
private static readonly XYZWShuffle4Slice3 ControlFast = default; |
||||
this.destination = new byte[(int)(this.Count * (3 / 4F))]; |
private byte[] source; |
||||
} |
private byte[] destination; |
||||
|
|
||||
[Params(128, 256, 512, 1024, 2048)] |
[GlobalSetup] |
||||
public int Count { get; set; } |
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] |
[Params(128, 256, 512, 1024, 2048)] |
||||
public void Shuffle4Slice3() |
public int Count { get; set; } |
||||
{ |
|
||||
SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); |
|
||||
} |
|
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void Shuffle4Slice3FastFallback() |
public void Shuffle4Slice3() |
||||
{ |
{ |
||||
SimdUtils.Shuffle4Slice3(this.source, this.destination, ControlFast); |
SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); |
||||
} |
|
||||
} |
} |
||||
|
|
||||
// 2020-10-29
|
[Benchmark] |
||||
// ##########
|
public void Shuffle4Slice3FastFallback() |
||||
//
|
{ |
||||
// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1)
|
SimdUtils.Shuffle4Slice3(this.source, this.destination, ControlFast); |
||||
// 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 | - | - | - | - |
|
|
||||
} |
} |
||||
|
|
||||
|
// 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 | - | - | - | - |
|
||||
|
|||||
@ -1,67 +1,65 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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))] |
private byte[] source; |
||||
public class ShuffleByte4Channel |
private byte[] destination; |
||||
{ |
|
||||
private byte[] source; |
|
||||
private byte[] destination; |
|
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void Setup() |
public void Setup() |
||||
{ |
{ |
||||
this.source = new byte[this.Count]; |
this.source = new byte[this.Count]; |
||||
new Random(this.Count).NextBytes(this.source); |
new Random(this.Count).NextBytes(this.source); |
||||
this.destination = new byte[this.Count]; |
this.destination = new byte[this.Count]; |
||||
} |
} |
||||
|
|
||||
[Params(128, 256, 512, 1024, 2048)] |
[Params(128, 256, 512, 1024, 2048)] |
||||
public int Count { get; set; } |
public int Count { get; set; } |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void Shuffle4Channel() |
public void Shuffle4Channel() |
||||
{ |
{ |
||||
SimdUtils.Shuffle4<WXYZShuffle4>(this.source, this.destination, default); |
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 | - | - | - | - |
|
||||
|
|||||
@ -1,68 +1,66 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Tests; |
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))] |
private static readonly byte Control = default(WXYZShuffle4).Control; |
||||
public class ShuffleFloat4Channel |
private float[] source; |
||||
{ |
private float[] destination; |
||||
private static readonly byte Control = default(WXYZShuffle4).Control; |
|
||||
private float[] source; |
|
||||
private float[] destination; |
|
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void Setup() |
public void Setup() |
||||
{ |
{ |
||||
this.source = new Random(this.Count).GenerateRandomFloatArray(this.Count, 0, 256); |
this.source = new Random(this.Count).GenerateRandomFloatArray(this.Count, 0, 256); |
||||
this.destination = new float[this.Count]; |
this.destination = new float[this.Count]; |
||||
} |
} |
||||
|
|
||||
[Params(128, 256, 512, 1024, 2048)] |
[Params(128, 256, 512, 1024, 2048)] |
||||
public int Count { get; set; } |
public int Count { get; set; } |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void Shuffle4Channel() |
public void Shuffle4Channel() |
||||
{ |
{ |
||||
SimdUtils.Shuffle4(this.source, this.destination, Control); |
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 | - | - | - | - |
|
||||
|
|||||
@ -1,68 +1,66 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
using System.Numerics; |
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using System.Runtime.InteropServices; |
using System.Runtime.InteropServices; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk |
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; |
||||
{ |
|
||||
[Config(typeof(Config.ShortCore31))] |
|
||||
public class UnPremultiplyVector4 |
|
||||
{ |
|
||||
private static readonly Vector4[] Vectors = CreateVectors(); |
|
||||
|
|
||||
[Benchmark(Baseline = true)] |
[Config(typeof(Config.ShortCore31))] |
||||
public void UnPremultiplyBaseline() |
public class UnPremultiplyVector4 |
||||
{ |
{ |
||||
ref Vector4 baseRef = ref MemoryMarshal.GetReference<Vector4>(Vectors); |
private static readonly Vector4[] Vectors = CreateVectors(); |
||||
|
|
||||
for (int i = 0; i < Vectors.Length; i++) |
[Benchmark(Baseline = true)] |
||||
{ |
public void UnPremultiplyBaseline() |
||||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
{ |
||||
UnPremultiply(ref v); |
ref Vector4 baseRef = ref MemoryMarshal.GetReference<Vector4>(Vectors); |
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < Vectors.Length; i++) |
||||
public void UnPremultiply() |
|
||||
{ |
{ |
||||
Numerics.UnPremultiply(Vectors); |
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
||||
|
UnPremultiply(ref v); |
||||
} |
} |
||||
|
} |
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
[Benchmark] |
||||
private static void UnPremultiply(ref Vector4 source) |
public void UnPremultiply() |
||||
{ |
{ |
||||
float w = source.W; |
Numerics.UnPremultiply(Vectors); |
||||
source /= w; |
} |
||||
source.W = w; |
|
||||
} |
|
||||
|
|
||||
private static Vector4[] CreateVectors() |
[MethodImpl(InliningOptions.ShortMethod)] |
||||
{ |
private static void UnPremultiply(ref Vector4 source) |
||||
var rnd = new Random(42); |
{ |
||||
return GenerateRandomVectorArray(rnd, 2048, 0, 1); |
float w = source.W; |
||||
} |
source /= w; |
||||
|
source.W = w; |
||||
|
} |
||||
|
|
||||
private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) |
private static Vector4[] CreateVectors() |
||||
{ |
{ |
||||
var values = new Vector4[length]; |
var rnd = new Random(42); |
||||
|
return GenerateRandomVectorArray(rnd, 2048, 0, 1); |
||||
|
} |
||||
|
|
||||
for (int i = 0; i < length; i++) |
private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) |
||||
{ |
{ |
||||
ref Vector4 v = ref values[i]; |
var values = new Vector4[length]; |
||||
v.X = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
v.Y = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
v.Z = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
v.W = GetRandomFloat(rnd, minVal, maxVal); |
|
||||
} |
|
||||
|
|
||||
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) |
return values; |
||||
=> ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; |
|
||||
} |
} |
||||
|
|
||||
|
private static float GetRandomFloat(Random rnd, float minVal, float maxVal) |
||||
|
=> ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; |
||||
} |
} |
||||
|
|||||
@ -1,72 +1,70 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Compression.Zlib; |
using SixLabors.ImageSharp.Compression.Zlib; |
||||
using SharpAdler32 = ICSharpCode.SharpZipLib.Checksum.Adler32; |
using SharpAdler32 = ICSharpCode.SharpZipLib.Checksum.Adler32; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.General |
namespace SixLabors.ImageSharp.Benchmarks.General; |
||||
{ |
|
||||
[Config(typeof(Config.ShortMultiFramework))] |
|
||||
public class Adler32Benchmark |
|
||||
{ |
|
||||
private byte[] data; |
|
||||
private readonly SharpAdler32 adler = new SharpAdler32(); |
|
||||
|
|
||||
[Params(1024, 2048, 4096)] |
[Config(typeof(Config.ShortMultiFramework))] |
||||
public int Count { get; set; } |
public class Adler32Benchmark |
||||
|
{ |
||||
|
private byte[] data; |
||||
|
private readonly SharpAdler32 adler = new SharpAdler32(); |
||||
|
|
||||
[GlobalSetup] |
[Params(1024, 2048, 4096)] |
||||
public void SetUp() |
public int Count { get; set; } |
||||
{ |
|
||||
this.data = new byte[this.Count]; |
|
||||
new Random(1).NextBytes(this.data); |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Baseline = true)] |
[GlobalSetup] |
||||
public long SharpZipLibCalculate() |
public void SetUp() |
||||
{ |
{ |
||||
this.adler.Reset(); |
this.data = new byte[this.Count]; |
||||
this.adler.Update(this.data); |
new Random(1).NextBytes(this.data); |
||||
return this.adler.Value; |
} |
||||
} |
|
||||
|
|
||||
[Benchmark] |
[Benchmark(Baseline = true)] |
||||
public uint SixLaborsCalculate() |
public long SharpZipLibCalculate() |
||||
{ |
{ |
||||
return Adler32.Calculate(this.data); |
this.adler.Reset(); |
||||
} |
this.adler.Update(this.data); |
||||
|
return this.adler.Value; |
||||
} |
} |
||||
|
|
||||
// ########## 17/05/2020 ##########
|
[Benchmark] |
||||
//
|
public uint SixLaborsCalculate() |
||||
// | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
|
{ |
||||
// |--------------------- |-------------- |------ |------------:|------------:|----------:|------:|--------:|------:|------:|------:|----------:|
|
return Adler32.Calculate(this.data); |
||||
// | 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 | - | - | - | - |
|
|
||||
} |
} |
||||
|
|
||||
|
// ########## 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 | - | - | - | - |
|
||||
|
|||||
@ -1,130 +1,126 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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 | |
'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 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 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 | |
'Array access using DenseMatrix' | 32 | 223.2 ns | 5.248 ns | 15.22 ns | 0.65 | 0.07 | |
||||
|
|
||||
* |
* |
||||
*/ |
*/ |
||||
public class Array2D |
public class Array2D |
||||
{ |
{ |
||||
private float[] flatArray; |
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)] |
[Params(4, 16, 32)] |
||||
public int Count { get; set; } |
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] |
[GlobalSetup] |
||||
public void SetUp() |
public void SetUp() |
||||
{ |
{ |
||||
this.flatArray = new float[this.Count * this.Count]; |
this.flatArray = new float[this.Count * this.Count]; |
||||
this.array2D = new float[this.Count, this.Count]; |
this.array2D = new float[this.Count, this.Count]; |
||||
this.jaggedData = new float[this.Count][]; |
this.jaggedData = new float[this.Count][]; |
||||
|
|
||||
for (int i = 0; i < this.Count; i++) |
for (int i = 0; i < this.Count; i++) |
||||
{ |
{ |
||||
this.jaggedData[i] = new float[this.Count]; |
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 = (this.Count / 2) - 10; |
||||
this.Min = Math.Max(0, this.Min); |
this.Min = Math.Max(0, this.Min); |
||||
this.Max = this.Min + Math.Min(10, this.Count); |
this.Max = this.Min + Math.Min(10, this.Count); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Emulated 2D array access using flat array")] |
[Benchmark(Description = "Emulated 2D array access using flat array")] |
||||
public float FlatArrayIndex() |
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; |
for (int j = this.Min; j < this.Max; j++) |
||||
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++) |
ref float v = ref a[(count * i) + j]; |
||||
{ |
v = i * j; |
||||
ref float v = ref a[(count * i) + j]; |
s += v; |
||||
v = i * j; |
|
||||
s += v; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
return s; |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "Array access using 2D array")] |
return s; |
||||
public float Array2DIndex() |
} |
||||
|
|
||||
|
[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; |
for (int j = this.Min; j < this.Max; j++) |
||||
float[,] a = this.array2D; |
|
||||
for (int i = this.Min; i < this.Max; i++) |
|
||||
{ |
{ |
||||
for (int j = this.Min; j < this.Max; j++) |
ref float v = ref a[i, j]; |
||||
{ |
v = i * j; |
||||
ref float v = ref a[i, j]; |
s += v; |
||||
v = i * j; |
|
||||
s += v; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
return s; |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Array access using a jagged array")] |
return s; |
||||
public float ArrayJaggedIndex() |
} |
||||
|
|
||||
|
[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; |
for (int j = this.Min; j < this.Max; j++) |
||||
float[][] a = this.jaggedData; |
|
||||
for (int i = this.Min; i < this.Max; i++) |
|
||||
{ |
{ |
||||
for (int j = this.Min; j < this.Max; j++) |
ref float v = ref a[i][j]; |
||||
{ |
v = i * j; |
||||
ref float v = ref a[i][j]; |
s += v; |
||||
v = i * j; |
|
||||
s += v; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
return s; |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Array access using DenseMatrix")] |
return s; |
||||
public float ArrayMatrixIndex() |
} |
||||
|
|
||||
|
[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; |
for (int j = this.Min; j < this.Max; j++) |
||||
DenseMatrix<float> a = this.matrix; |
|
||||
for (int i = this.Min; i < this.Max; i++) |
|
||||
{ |
{ |
||||
for (int j = this.Min; j < this.Max; j++) |
ref float v = ref a[i, j]; |
||||
{ |
v = i * j; |
||||
ref float v = ref a[i, j]; |
s += v; |
||||
v = i * j; |
|
||||
s += v; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
return s; |
|
||||
} |
} |
||||
|
|
||||
|
return s; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,59 +1,57 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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] |
[GlobalSetup] |
||||
public void SetUp() |
public void SetUp() |
||||
{ |
{ |
||||
this.source = new byte[this.Count]; |
this.source = new byte[this.Count]; |
||||
this.destination = new byte[this.Count]; |
this.destination = new byte[this.Count]; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "Copy using Array.Reverse()")] |
[Benchmark(Baseline = true, Description = "Copy using Array.Reverse()")] |
||||
public void ReverseArray() |
public void ReverseArray() |
||||
{ |
{ |
||||
Array.Reverse(this.source, 0, this.Count); |
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); |
byte tmp = this.source[i]; |
||||
|
this.source[i] = this.source[this.source.Length - i - 1]; |
||||
/* |
this.source[this.source.Length - i - 1] = tmp; |
||||
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; |
|
||||
}*/ |
|
||||
} |
|
||||
|
|
||||
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; |
byte temp = source[i]; |
||||
int j = index + length - 1; |
source[i] = source[j]; |
||||
while (i < j) |
source[j] = temp; |
||||
{ |
i++; |
||||
byte temp = source[i]; |
j--; |
||||
source[i] = source[j]; |
|
||||
source[j] = temp; |
|
||||
i++; |
|
||||
j--; |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,43 +1,41 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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")] |
[Benchmark(Baseline = true, Description = "Maths Abs")] |
||||
public int MathAbs() |
public int MathAbs() |
||||
{ |
{ |
||||
int x = this.X; |
int x = this.X; |
||||
return Math.Abs(x); |
return Math.Abs(x); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Conditional Abs")] |
[Benchmark(Description = "Conditional Abs")] |
||||
public int ConditionalAbs() |
public int ConditionalAbs() |
||||
{ |
{ |
||||
int x = this.X; |
int x = this.X; |
||||
return x < 0 ? -x : x; |
return x < 0 ? -x : x; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Bitwise Abs")] |
[Benchmark(Description = "Bitwise Abs")] |
||||
public int AbsBitwise() |
public int AbsBitwise() |
||||
{ |
{ |
||||
int x = this.X; |
int x = this.X; |
||||
return (x ^ (x >> 31)) - (x >> 31); |
return (x ^ (x >> 31)) - (x >> 31); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Bitwise Abs With Variable")] |
[Benchmark(Description = "Bitwise Abs With Variable")] |
||||
public int AbsBitwiseVer() |
public int AbsBitwiseVer() |
||||
{ |
{ |
||||
int x = this.X; |
int x = this.X; |
||||
int y = x >> 31; |
int y = x >> 31; |
||||
return (x ^ y) - y; |
return (x ^ y) - y; |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,70 +1,68 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using BenchmarkDotNet.Attributes; |
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; |
float acc = 0; |
||||
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)] |
for (int i = 0; i < Values.Length; i++) |
||||
public float UsingMathF() |
|
||||
{ |
{ |
||||
float acc = 0; |
acc += ClampUsingMathF(Values[i], this.min, this.max); |
||||
|
} |
||||
|
|
||||
for (int i = 0; i < Values.Length; i++) |
return acc; |
||||
{ |
} |
||||
acc += ClampUsingMathF(Values[i], this.min, this.max); |
|
||||
} |
|
||||
|
|
||||
return acc; |
[Benchmark] |
||||
} |
public float UsingBranching() |
||||
|
{ |
||||
|
float acc = 0; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < Values.Length; i++) |
||||
public float UsingBranching() |
|
||||
{ |
{ |
||||
float acc = 0; |
acc += ClampUsingBranching(Values[i], this.min, this.max); |
||||
|
} |
||||
|
|
||||
for (int i = 0; i < Values.Length; i++) |
return acc; |
||||
{ |
} |
||||
acc += ClampUsingBranching(Values[i], this.min, this.max); |
|
||||
} |
|
||||
|
|
||||
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)] |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
private static float ClampUsingMathF(float x, float min, float max) |
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)] |
if (x <= min) |
||||
private static float ClampUsingBranching(float x, float min, float max) |
|
||||
{ |
{ |
||||
if (x >= max) |
return min; |
||||
{ |
|
||||
return max; |
|
||||
} |
|
||||
|
|
||||
if (x <= min) |
|
||||
{ |
|
||||
return min; |
|
||||
} |
|
||||
|
|
||||
return x; |
|
||||
} |
} |
||||
|
|
||||
// RESULTS:
|
return x; |
||||
// 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 |
|
|
||||
} |
} |
||||
|
|
||||
|
// 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 |
|
||||
} |
} |
||||
|
|||||
@ -1,92 +1,90 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using BenchmarkDotNet.Attributes; |
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)] |
int value = this.Value; |
||||
public int Value { get; set; } |
return (byte)Math.Min(Math.Max(byte.MinValue, value), byte.MaxValue); |
||||
|
} |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "Maths Clamp")] |
[Benchmark(Description = "No Maths Clamp")] |
||||
public byte ClampMaths() |
public byte ClampNoMaths() |
||||
{ |
{ |
||||
int value = this.Value; |
int value = this.Value; |
||||
return (byte)Math.Min(Math.Max(byte.MinValue, value), byte.MaxValue); |
value = value >= byte.MaxValue ? byte.MaxValue : value; |
||||
} |
return (byte)(value <= byte.MinValue ? byte.MinValue : value); |
||||
|
} |
||||
|
|
||||
[Benchmark(Description = "No Maths Clamp")] |
[Benchmark(Description = "No Maths No Equals Clamp")] |
||||
public byte ClampNoMaths() |
public byte ClampNoMathsNoEquals() |
||||
{ |
{ |
||||
int value = this.Value; |
int value = this.Value; |
||||
value = value >= byte.MaxValue ? byte.MaxValue : value; |
value = value > byte.MaxValue ? byte.MaxValue : value; |
||||
return (byte)(value <= byte.MinValue ? byte.MinValue : value); |
return (byte)(value < byte.MinValue ? byte.MinValue : value); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "No Maths No Equals Clamp")] |
[Benchmark(Description = "No Maths Clamp No Ternary")] |
||||
public byte ClampNoMathsNoEquals() |
public byte ClampNoMathsNoTernary() |
||||
|
{ |
||||
|
int value = this.Value; |
||||
|
|
||||
|
if (value >= byte.MaxValue) |
||||
{ |
{ |
||||
int value = this.Value; |
return byte.MaxValue; |
||||
value = value > byte.MaxValue ? byte.MaxValue : value; |
|
||||
return (byte)(value < byte.MinValue ? byte.MinValue : value); |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "No Maths Clamp No Ternary")] |
if (value <= byte.MinValue) |
||||
public byte ClampNoMathsNoTernary() |
|
||||
{ |
{ |
||||
int value = this.Value; |
return byte.MinValue; |
||||
|
|
||||
if (value >= byte.MaxValue) |
|
||||
{ |
|
||||
return byte.MaxValue; |
|
||||
} |
|
||||
|
|
||||
if (value <= byte.MinValue) |
|
||||
{ |
|
||||
return byte.MinValue; |
|
||||
} |
|
||||
|
|
||||
return (byte)value; |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "No Maths No Equals Clamp No Ternary")] |
return (byte)value; |
||||
public byte ClampNoMathsEqualsNoTernary() |
} |
||||
{ |
|
||||
int value = this.Value; |
|
||||
|
|
||||
if (value > byte.MaxValue) |
[Benchmark(Description = "No Maths No Equals Clamp No Ternary")] |
||||
{ |
public byte ClampNoMathsEqualsNoTernary() |
||||
return byte.MaxValue; |
{ |
||||
} |
int value = this.Value; |
||||
|
|
||||
if (value < byte.MinValue) |
if (value > byte.MaxValue) |
||||
{ |
{ |
||||
return byte.MinValue; |
return byte.MaxValue; |
||||
} |
} |
||||
|
|
||||
return (byte)value; |
if (value < byte.MinValue) |
||||
} |
{ |
||||
|
return byte.MinValue; |
||||
|
} |
||||
|
|
||||
[Benchmark(Description = "Clamp using Bitwise Abs")] |
return (byte)value; |
||||
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)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)] |
return (byte)x; |
||||
private static int AbsBitwiseVer(ref int x) |
} |
||||
{ |
|
||||
int y = x >> 31; |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
return (x ^ y) - y; |
private static int AbsBitwiseVer(ref int x) |
||||
} |
{ |
||||
|
int y = x >> 31; |
||||
|
return (x ^ y) - y; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,42 +1,40 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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")] |
[Benchmark(Baseline = true, Description = "Math.Pow 2")] |
||||
public float MathPow() |
public float MathPow() |
||||
{ |
{ |
||||
float x = this.X; |
float x = this.X; |
||||
return (float)Math.Pow(x, 2); |
return (float)Math.Pow(x, 2); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Pow x2")] |
[Benchmark(Description = "Pow x2")] |
||||
public float PowMult() |
public float PowMult() |
||||
{ |
{ |
||||
float x = this.X; |
float x = this.X; |
||||
return x * x; |
return x * x; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Math.Pow 3")] |
[Benchmark(Description = "Math.Pow 3")] |
||||
public float MathPow3() |
public float MathPow3() |
||||
{ |
{ |
||||
float x = this.X; |
float x = this.X; |
||||
return (float)Math.Pow(x, 3); |
return (float)Math.Pow(x, 3); |
||||
} |
} |
||||
|
|
||||
[Benchmark(Description = "Pow x3")] |
[Benchmark(Description = "Pow x3")] |
||||
public float PowMult3() |
public float PowMult3() |
||||
{ |
{ |
||||
float x = this.X; |
float x = this.X; |
||||
return x * x * x; |
return x * x * x; |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,25 +1,23 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
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] |
[Benchmark] |
||||
public int ConvertTo() => Convert.ToInt32(Input); |
public int ConvertTo() => Convert.ToInt32(Input); |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public int MathRound() => (int)Math.Round(Input); |
public int MathRound() => (int)Math.Round(Input); |
||||
|
|
||||
// Results 20th Jan 2019
|
// Results 20th Jan 2019
|
||||
// Method | Mean | Error | StdDev | Median |
|
// Method | Mean | Error | StdDev | Median |
|
||||
//---------- |----------:|----------:|----------:|----------:|
|
//---------- |----------:|----------:|----------:|----------:|
|
||||
// ConvertTo | 3.1967 ns | 0.1234 ns | 0.2129 ns | 3.2340 ns |
|
// 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 |
|
// MathRound | 0.0528 ns | 0.0374 ns | 0.1079 ns | 0.0000 ns |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,228 +1,226 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
using System.Buffers; |
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using System.Runtime.InteropServices; |
using System.Runtime.InteropServices; |
||||
using BenchmarkDotNet.Attributes; |
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>
|
private byte[] destArray; |
||||
/// Compare different methods for copying native and/or managed buffers.
|
|
||||
/// Conclusions:
|
private MemoryHandle destHandle; |
||||
/// - Span.CopyTo() has terrible performance on classic .NET Framework
|
|
||||
/// - Buffer.MemoryCopy() performance is good enough for all sizes (but needs pinning)
|
private Memory<byte> destMemory; |
||||
/// </summary>
|
|
||||
[Config(typeof(Config.ShortMultiFramework))] |
private byte[] sourceArray; |
||||
public class CopyBuffers |
|
||||
|
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; |
Unsafe.CopyBlock(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); |
||||
|
|
||||
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 | - | - | - | - |
|
|
||||
} |
} |
||||
|
|
||||
|
[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 | - | - | - | - |
|
||||
} |
} |
||||
|
|||||
@ -1,72 +1,70 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.Compression.Zlib; |
using SixLabors.ImageSharp.Compression.Zlib; |
||||
using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; |
using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.General |
namespace SixLabors.ImageSharp.Benchmarks.General; |
||||
{ |
|
||||
[Config(typeof(Config.ShortMultiFramework))] |
|
||||
public class Crc32Benchmark |
|
||||
{ |
|
||||
private byte[] data; |
|
||||
private readonly SharpCrc32 crc = new SharpCrc32(); |
|
||||
|
|
||||
[Params(1024, 2048, 4096)] |
[Config(typeof(Config.ShortMultiFramework))] |
||||
public int Count { get; set; } |
public class Crc32Benchmark |
||||
|
{ |
||||
|
private byte[] data; |
||||
|
private readonly SharpCrc32 crc = new SharpCrc32(); |
||||
|
|
||||
[GlobalSetup] |
[Params(1024, 2048, 4096)] |
||||
public void SetUp() |
public int Count { get; set; } |
||||
{ |
|
||||
this.data = new byte[this.Count]; |
|
||||
new Random(1).NextBytes(this.data); |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Baseline = true)] |
[GlobalSetup] |
||||
public long SharpZipLibCalculate() |
public void SetUp() |
||||
{ |
{ |
||||
this.crc.Reset(); |
this.data = new byte[this.Count]; |
||||
this.crc.Update(this.data); |
new Random(1).NextBytes(this.data); |
||||
return this.crc.Value; |
} |
||||
} |
|
||||
|
|
||||
[Benchmark] |
[Benchmark(Baseline = true)] |
||||
public long SixLaborsCalculate() |
public long SharpZipLibCalculate() |
||||
{ |
{ |
||||
return Crc32.Calculate(this.data); |
this.crc.Reset(); |
||||
} |
this.crc.Update(this.data); |
||||
|
return this.crc.Value; |
||||
} |
} |
||||
|
|
||||
// ########## 17/05/2020 ##########
|
[Benchmark] |
||||
//
|
public long SixLaborsCalculate() |
||||
// | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
|
{ |
||||
// |--------------------- |-------------- |------ |-------------:|-------------:|-----------:|------:|--------:|------:|------:|------:|----------:|
|
return Crc32.Calculate(this.data); |
||||
// | 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 | - | - | - | - |
|
|
||||
} |
} |
||||
|
|
||||
|
// ########## 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 | - | - | - | - |
|
||||
|
|||||
@ -1,279 +1,276 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
using System.Buffers; |
||||
using System.IO; |
|
||||
using System.Runtime.CompilerServices; |
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>
|
/// <summary>
|
||||
/// A readonly stream wrapper that add a secondary level buffer in addition to native stream
|
/// The length, in bytes, of the underlying buffer.
|
||||
/// buffered reading to reduce the overhead of small incremental reads.
|
|
||||
/// </summary>
|
/// </summary>
|
||||
internal sealed unsafe class BufferedReadStreamWrapper : IDisposable |
public const int BufferLength = 8192; |
||||
{ |
|
||||
/// <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; |
|
||||
|
|
||||
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 MemoryHandle readBufferHandle; |
||||
private int readBufferIndex; |
|
||||
|
|
||||
// Matches what the stream position would be without buffering
|
private readonly byte* pinnedReadBuffer; |
||||
private long readerPosition; |
|
||||
|
|
||||
private bool isDisposed; |
// Index within our buffer, not reader position.
|
||||
|
private int readBufferIndex; |
||||
|
|
||||
/// <summary>
|
// Matches what the stream position would be without buffering
|
||||
/// Initializes a new instance of the <see cref="BufferedReadStreamWrapper"/> class.
|
private long readerPosition; |
||||
/// </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(); |
|
||||
} |
|
||||
|
|
||||
this.stream = stream; |
private bool isDisposed; |
||||
this.Position = (int)stream.Position; |
|
||||
this.Length = stream.Length; |
|
||||
|
|
||||
this.readBuffer = ArrayPool<byte>.Shared.Rent(BufferLength); |
/// <summary>
|
||||
this.readBufferHandle = new Memory<byte>(this.readBuffer).Pin(); |
/// Initializes a new instance of the <see cref="BufferedReadStreamWrapper"/> class.
|
||||
this.pinnedReadBuffer = (byte*)this.readBufferHandle.Pointer; |
/// </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.
|
// Ensure all underlying buffers have been flushed before we attempt to read the stream.
|
||||
this.readBufferIndex = BufferLength; |
// 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>
|
this.stream = stream; |
||||
/// Gets the length, in bytes, of the stream.
|
this.Position = (int)stream.Position; |
||||
/// </summary>
|
this.Length = stream.Length; |
||||
public long Length { get; } |
|
||||
|
|
||||
/// <summary>
|
this.readBuffer = ArrayPool<byte>.Shared.Rent(BufferLength); |
||||
/// Gets or sets the current position within the stream.
|
this.readBufferHandle = new Memory<byte>(this.readBuffer).Pin(); |
||||
/// </summary>
|
this.pinnedReadBuffer = (byte*)this.readBufferHandle.Pointer; |
||||
public long Position |
|
||||
{ |
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
get => this.readerPosition; |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] |
// This triggers a full read on first attempt.
|
||||
set |
this.readBufferIndex = BufferLength; |
||||
{ |
} |
||||
// 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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
/// <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)] |
[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; |
||||
} |
} |
||||
|
else |
||||
// Our buffer has been read.
|
|
||||
// We need to refill and start again.
|
|
||||
if (this.readBufferIndex > MaxBufferIndex) |
|
||||
{ |
{ |
||||
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++; |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
return this.pinnedReadBuffer[this.readBufferIndex++]; |
public int ReadByte() |
||||
|
{ |
||||
|
if (this.readerPosition >= this.Length) |
||||
|
{ |
||||
|
return -1; |
||||
} |
} |
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
// Our buffer has been read.
|
||||
public int Read(byte[] buffer, int offset, int count) |
// We need to refill and start again.
|
||||
|
if (this.readBufferIndex > MaxBufferIndex) |
||||
{ |
{ |
||||
// Too big for our buffer. Read directly from the stream.
|
this.FillReadBuffer(); |
||||
if (count > BufferLength) |
} |
||||
{ |
|
||||
return this.ReadToBufferDirectSlow(buffer, offset, count); |
|
||||
} |
|
||||
|
|
||||
// Too big for remaining buffer but less than entire buffer length
|
this.readerPosition++; |
||||
// Copy to buffer then read from there.
|
return this.pinnedReadBuffer[this.readBufferIndex++]; |
||||
if (count + this.readBufferIndex > BufferLength) |
} |
||||
{ |
|
||||
return this.ReadToBufferViaCopySlow(buffer, offset, count); |
|
||||
} |
|
||||
|
|
||||
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.
|
return this.ReadToBufferViaCopySlow(buffer, offset, count); |
||||
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; |
|
||||
} |
} |
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
return this.ReadToBufferViaCopyFast(buffer, offset, count); |
||||
public long Seek(long offset, SeekOrigin origin) |
} |
||||
|
|
||||
|
public void Flush() |
||||
|
{ |
||||
|
// Reset the stream position to match reader position.
|
||||
|
if (this.readerPosition != this.stream.Position) |
||||
{ |
{ |
||||
switch (origin) |
this.stream.Seek(this.readerPosition, SeekOrigin.Begin); |
||||
{ |
this.readerPosition = (int)this.stream.Position; |
||||
case SeekOrigin.Begin: |
} |
||||
this.Position = offset; |
|
||||
break; |
|
||||
|
|
||||
case SeekOrigin.Current: |
// Reset to trigger full read on next attempt.
|
||||
this.Position += offset; |
this.readBufferIndex = BufferLength; |
||||
break; |
} |
||||
|
|
||||
case SeekOrigin.End: |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
this.Position = this.Length - offset; |
public long Seek(long offset, SeekOrigin origin) |
||||
break; |
{ |
||||
} |
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/>
|
return this.readerPosition; |
||||
public void Dispose() |
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (!this.isDisposed) |
||||
{ |
{ |
||||
if (!this.isDisposed) |
this.isDisposed = true; |
||||
{ |
this.readBufferHandle.Dispose(); |
||||
this.isDisposed = true; |
ArrayPool<byte>.Shared.Return(this.readBuffer); |
||||
this.readBufferHandle.Dispose(); |
this.Flush(); |
||||
ArrayPool<byte>.Shared.Return(this.readBuffer); |
|
||||
this.Flush(); |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
private bool IsInReadBuffer(long newPosition, out long index) |
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; |
this.stream.Seek(this.readerPosition, SeekOrigin.Begin); |
||||
return index > -1 && index < BufferLength; |
|
||||
} |
} |
||||
|
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] |
this.stream.Read(this.readBuffer, 0, BufferLength); |
||||
private void FillReadBuffer() |
this.readBufferIndex = 0; |
||||
{ |
} |
||||
if (this.readerPosition != this.stream.Position) |
|
||||
{ |
|
||||
this.stream.Seek(this.readerPosition, SeekOrigin.Begin); |
|
||||
} |
|
||||
|
|
||||
this.stream.Read(this.readBuffer, 0, BufferLength); |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
this.readBufferIndex = 0; |
private int ReadToBufferViaCopyFast(byte[] buffer, int offset, int count) |
||||
} |
{ |
||||
|
int n = this.GetCopyCount(count); |
||||
|
this.CopyBytes(buffer, offset, n); |
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
this.readerPosition += n; |
||||
private int ReadToBufferViaCopyFast(byte[] buffer, int offset, int count) |
this.readBufferIndex += n; |
||||
{ |
|
||||
int n = this.GetCopyCount(count); |
|
||||
this.CopyBytes(buffer, offset, n); |
|
||||
|
|
||||
this.readerPosition += n; |
return n; |
||||
this.readBufferIndex += 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)] |
return this.ReadToBufferViaCopyFast(buffer, offset, count); |
||||
private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count) |
} |
||||
{ |
|
||||
// Refill our buffer then copy.
|
|
||||
this.FillReadBuffer(); |
|
||||
|
|
||||
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)] |
int n = this.stream.Read(buffer, offset, count); |
||||
private int ReadToBufferDirectSlow(byte[] buffer, int offset, int count) |
this.Position += n; |
||||
{ |
|
||||
// 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); |
return n; |
||||
this.Position += 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)] |
if (n < 0) |
||||
private int GetCopyCount(int count) |
|
||||
{ |
{ |
||||
long n = this.Length - this.readerPosition; |
return 0; |
||||
if (n > count) |
|
||||
{ |
|
||||
return count; |
|
||||
} |
|
||||
|
|
||||
if (n < 0) |
|
||||
{ |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
return (int)n; |
|
||||
} |
} |
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
return (int)n; |
||||
private void CopyBytes(byte[] buffer, int offset, int count) |
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private void CopyBytes(byte[] buffer, int offset, int count) |
||||
|
{ |
||||
|
// Same as MemoryStream.
|
||||
|
if (count < 9) |
||||
{ |
{ |
||||
// Same as MemoryStream.
|
int byteCount = count; |
||||
if (count < 9) |
int read = this.readBufferIndex; |
||||
{ |
byte* pinned = this.pinnedReadBuffer; |
||||
int byteCount = count; |
|
||||
int read = this.readBufferIndex; |
while (--byteCount > -1) |
||||
byte* pinned = this.pinnedReadBuffer; |
|
||||
|
|
||||
while (--byteCount > -1) |
|
||||
{ |
|
||||
buffer[offset + byteCount] = pinned[read + byteCount]; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
{ |
||||
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); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,261 +1,258 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
using SixLabors.ImageSharp.IO; |
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))] |
private readonly byte[] buffer = CreateTestBytes(); |
||||
public class BufferedStreams |
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(); |
this.stream1 = new MemoryStream(this.buffer); |
||||
private readonly byte[] chunk1 = new byte[2]; |
this.stream2 = new MemoryStream(this.buffer); |
||||
private readonly byte[] chunk2 = new byte[2]; |
this.stream3 = new MemoryStream(this.buffer); |
||||
|
this.stream4 = new MemoryStream(this.buffer); |
||||
private MemoryStream stream1; |
this.stream5 = new MemoryStream(this.buffer); |
||||
private MemoryStream stream2; |
this.stream6 = new MemoryStream(this.buffer); |
||||
private MemoryStream stream3; |
this.stream6 = new MemoryStream(this.buffer); |
||||
private MemoryStream stream4; |
|
||||
private MemoryStream stream5; |
this.chunkedMemoryStream1 = new ChunkedMemoryStream(Configuration.Default.MemoryAllocator); |
||||
private MemoryStream stream6; |
this.chunkedMemoryStream1.Write(this.buffer); |
||||
private ChunkedMemoryStream chunkedMemoryStream1; |
this.chunkedMemoryStream1.Position = 0; |
||||
private ChunkedMemoryStream chunkedMemoryStream2; |
|
||||
private BufferedReadStream bufferedStream1; |
this.chunkedMemoryStream2 = new ChunkedMemoryStream(Configuration.Default.MemoryAllocator); |
||||
private BufferedReadStream bufferedStream2; |
this.chunkedMemoryStream2.Write(this.buffer); |
||||
private BufferedReadStream bufferedStream3; |
this.chunkedMemoryStream2.Position = 0; |
||||
private BufferedReadStream bufferedStream4; |
|
||||
private BufferedReadStreamWrapper bufferedStreamWrap1; |
this.bufferedStream1 = new BufferedReadStream(Configuration.Default, this.stream3); |
||||
private BufferedReadStreamWrapper bufferedStreamWrap2; |
this.bufferedStream2 = new BufferedReadStream(Configuration.Default, this.stream4); |
||||
|
this.bufferedStream3 = new BufferedReadStream(Configuration.Default, this.chunkedMemoryStream1); |
||||
[GlobalSetup] |
this.bufferedStream4 = new BufferedReadStream(Configuration.Default, this.chunkedMemoryStream2); |
||||
public void CreateStreams() |
this.bufferedStreamWrap1 = new BufferedReadStreamWrapper(this.stream5); |
||||
{ |
this.bufferedStreamWrap2 = new BufferedReadStreamWrapper(this.stream6); |
||||
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; |
|
||||
|
|
||||
for (int i = 0; i < stream.Length / 2; i++) |
[GlobalCleanup] |
||||
{ |
public void DestroyStreams() |
||||
r += stream.Read(b, 0, 2); |
{ |
||||
} |
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] |
for (int i = 0; i < stream.Length / 2; i++) |
||||
public int BufferedReadStreamRead() |
|
||||
{ |
{ |
||||
int r = 0; |
r += stream.Read(b, 0, 2); |
||||
BufferedReadStream reader = this.bufferedStream1; |
} |
||||
byte[] b = this.chunk2; |
|
||||
|
|
||||
for (int i = 0; i < reader.Length / 2; i++) |
return r; |
||||
{ |
} |
||||
r += reader.Read(b, 0, 2); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark] |
||||
} |
public int BufferedReadStreamRead() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
BufferedReadStream reader = this.bufferedStream1; |
||||
|
byte[] b = this.chunk2; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < reader.Length / 2; i++) |
||||
public int BufferedReadStreamChunkedRead() |
|
||||
{ |
{ |
||||
int r = 0; |
r += reader.Read(b, 0, 2); |
||||
BufferedReadStream reader = this.bufferedStream3; |
} |
||||
byte[] b = this.chunk2; |
|
||||
|
|
||||
for (int i = 0; i < reader.Length / 2; i++) |
return r; |
||||
{ |
} |
||||
r += reader.Read(b, 0, 2); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark] |
||||
} |
public int BufferedReadStreamChunkedRead() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
BufferedReadStream reader = this.bufferedStream3; |
||||
|
byte[] b = this.chunk2; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < reader.Length / 2; i++) |
||||
public int BufferedReadStreamWrapRead() |
|
||||
{ |
{ |
||||
int r = 0; |
r += reader.Read(b, 0, 2); |
||||
BufferedReadStreamWrapper reader = this.bufferedStreamWrap1; |
} |
||||
byte[] b = this.chunk2; |
|
||||
|
|
||||
for (int i = 0; i < reader.Length / 2; i++) |
return r; |
||||
{ |
} |
||||
r += reader.Read(b, 0, 2); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark] |
||||
} |
public int BufferedReadStreamWrapRead() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
BufferedReadStreamWrapper reader = this.bufferedStreamWrap1; |
||||
|
byte[] b = this.chunk2; |
||||
|
|
||||
[Benchmark(Baseline = true)] |
for (int i = 0; i < reader.Length / 2; i++) |
||||
public int StandardStreamReadByte() |
|
||||
{ |
{ |
||||
int r = 0; |
r += reader.Read(b, 0, 2); |
||||
Stream stream = this.stream2; |
} |
||||
|
|
||||
for (int i = 0; i < stream.Length; i++) |
return r; |
||||
{ |
} |
||||
r += stream.ReadByte(); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark(Baseline = true)] |
||||
} |
public int StandardStreamReadByte() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
Stream stream = this.stream2; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < stream.Length; i++) |
||||
public int BufferedReadStreamReadByte() |
|
||||
{ |
{ |
||||
int r = 0; |
r += stream.ReadByte(); |
||||
BufferedReadStream reader = this.bufferedStream2; |
} |
||||
|
|
||||
for (int i = 0; i < reader.Length; i++) |
return r; |
||||
{ |
} |
||||
r += reader.ReadByte(); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark] |
||||
} |
public int BufferedReadStreamReadByte() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
BufferedReadStream reader = this.bufferedStream2; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < reader.Length; i++) |
||||
public int BufferedReadStreamChunkedReadByte() |
|
||||
{ |
{ |
||||
int r = 0; |
r += reader.ReadByte(); |
||||
BufferedReadStream reader = this.bufferedStream4; |
} |
||||
|
|
||||
for (int i = 0; i < reader.Length; i++) |
return r; |
||||
{ |
} |
||||
r += reader.ReadByte(); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark] |
||||
} |
public int BufferedReadStreamChunkedReadByte() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
BufferedReadStream reader = this.bufferedStream4; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < reader.Length; i++) |
||||
public int BufferedReadStreamWrapReadByte() |
|
||||
{ |
{ |
||||
int r = 0; |
r += reader.ReadByte(); |
||||
BufferedReadStreamWrapper reader = this.bufferedStreamWrap2; |
} |
||||
|
|
||||
for (int i = 0; i < reader.Length; i++) |
return r; |
||||
{ |
} |
||||
r += reader.ReadByte(); |
|
||||
} |
|
||||
|
|
||||
return r; |
[Benchmark] |
||||
} |
public int BufferedReadStreamWrapReadByte() |
||||
|
{ |
||||
|
int r = 0; |
||||
|
BufferedReadStreamWrapper reader = this.bufferedStreamWrap2; |
||||
|
|
||||
[Benchmark] |
for (int i = 0; i < reader.Length; i++) |
||||
public int ArrayReadByte() |
|
||||
{ |
{ |
||||
byte[] b = this.buffer; |
r += reader.ReadByte(); |
||||
int r = 0; |
|
||||
for (int i = 0; i < b.Length; i++) |
|
||||
{ |
|
||||
r += b[i]; |
|
||||
} |
|
||||
|
|
||||
return r; |
|
||||
} |
} |
||||
|
|
||||
private static byte[] CreateTestBytes() |
return r; |
||||
{ |
} |
||||
var buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; |
|
||||
var random = new Random(); |
|
||||
random.NextBytes(buffer); |
|
||||
|
|
||||
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; |
||||
} |
} |
||||
|
|
||||
/* |
private static byte[] CreateTestBytes() |
||||
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 |
var buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; |
||||
.NET Core SDK=3.1.401 |
var random = new Random(); |
||||
[Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT |
random.NextBytes(buffer); |
||||
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 |
return buffer; |
||||
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 | - | - | - | - | |
|
||||
*/ |
|
||||
} |
} |
||||
|
|
||||
|
/* |
||||
|
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 | - | - | - | - | |
||||
|
*/ |
||||
|
|||||
@ -1,179 +1,177 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using System.Runtime.InteropServices; |
using System.Runtime.InteropServices; |
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
using SixLabors.ImageSharp.PixelFormats; |
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)] |
[Params(64)] |
||||
public int Count { get; set; } |
public int Count { get; set; } |
||||
|
|
||||
[GlobalSetup] |
[GlobalSetup] |
||||
public void Setup() |
public void Setup() |
||||
{ |
{ |
||||
this.source = new Rgba32[this.Count]; |
this.source = new Rgba32[this.Count]; |
||||
this.dest = new Argb32[this.Count]; |
this.dest = new Argb32[this.Count]; |
||||
} |
} |
||||
|
|
||||
[Benchmark(Baseline = true)] |
[Benchmark(Baseline = true)] |
||||
public void Default() |
public void Default() |
||||
{ |
{ |
||||
ref Rgba32 sBase = ref this.source[0]; |
ref Rgba32 sBase = ref this.source[0]; |
||||
ref Argb32 dBase = ref this.dest[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); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] |
for (int i = 0; i < this.Count; i++) |
||||
private static void Default_GenericImpl<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest) |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
{ |
||||
ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); |
Rgba32 s = Unsafe.Add(ref sBase, i); |
||||
ref TPixel dBase = ref MemoryMarshal.GetReference(dest); |
Unsafe.Add(ref dBase, i).FromRgba32(s); |
||||
|
|
||||
for (int i = 0; i < source.Length; i++) |
|
||||
{ |
|
||||
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] |
for (int i = 0; i < source.Length; i++) |
||||
public void Default_Generic() |
|
||||
{ |
{ |
||||
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] |
for (int i = 0; i < this.Count; i += 2) |
||||
public void Default_Group2() |
|
||||
{ |
{ |
||||
ref Rgba32 sBase = ref this.source[0]; |
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); |
||||
ref Argb32 dBase = ref this.dest[0]; |
Rgba32 s1 = Unsafe.Add(ref s0, 1); |
||||
|
|
||||
for (int i = 0; i < this.Count; i += 2) |
ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); |
||||
{ |
d0.FromRgba32(s0); |
||||
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); |
Unsafe.Add(ref d0, 1).FromRgba32(s1); |
||||
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] |
for (int i = 0; i < this.Count; i += 4) |
||||
public void Default_Group4() |
|
||||
{ |
{ |
||||
ref Rgba32 sBase = ref this.source[0]; |
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); |
||||
ref Argb32 dBase = ref this.dest[0]; |
ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); |
||||
|
ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); |
||||
for (int i = 0; i < this.Count; i += 4) |
Rgba32 s3 = Unsafe.Add(ref s2, 1); |
||||
{ |
|
||||
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); |
ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); |
||||
ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); |
ref Argb32 d1 = ref Unsafe.Add(ref d0, 1); |
||||
ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); |
ref Argb32 d2 = ref Unsafe.Add(ref d1, 1); |
||||
Rgba32 s3 = Unsafe.Add(ref s2, 1); |
|
||||
|
d0.FromRgba32(s0); |
||||
ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); |
d1.FromRgba32(s1); |
||||
ref Argb32 d1 = ref Unsafe.Add(ref d0, 1); |
d2.FromRgba32(s2); |
||||
ref Argb32 d2 = ref Unsafe.Add(ref d1, 1); |
Unsafe.Add(ref d2, 1).FromRgba32(s3); |
||||
|
|
||||
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] |
for (int i = 0; i < this.Count; i++) |
||||
public void BitOps() |
|
||||
{ |
{ |
||||
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]); |
uint s = Unsafe.Add(ref sBase, i); |
||||
ref uint dBase = ref Unsafe.As<Argb32, uint>(ref this.dest[0]); |
Unsafe.Add(ref dBase, i) = FromRgba32.ToArgb32(s); |
||||
|
|
||||
for (int i = 0; i < this.Count; i++) |
|
||||
{ |
|
||||
uint s = Unsafe.Add(ref sBase, i); |
|
||||
Unsafe.Add(ref dBase, i) = FromRgba32.ToArgb32(s); |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
[Benchmark] |
[Benchmark] |
||||
public void BitOps_GroupAsULong() |
public void BitOps_GroupAsULong() |
||||
{ |
{ |
||||
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]); |
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]); |
||||
ref ulong dBase = ref Unsafe.As<Argb32, ulong>(ref this.dest[0]); |
ref ulong dBase = ref Unsafe.As<Argb32, ulong>(ref this.dest[0]); |
||||
|
|
||||
for (int i = 0; i < this.Count / 2; i++) |
for (int i = 0; i < this.Count / 2; i++) |
||||
{ |
{ |
||||
ulong s = Unsafe.Add(ref sBase, i); |
ulong s = Unsafe.Add(ref sBase, i); |
||||
uint lo = (uint)s; |
uint lo = (uint)s; |
||||
uint hi = (uint)(s >> 32); |
uint hi = (uint)(s >> 32); |
||||
lo = FromRgba32.ToArgb32(lo); |
lo = FromRgba32.ToArgb32(lo); |
||||
hi = FromRgba32.ToArgb32(hi); |
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>
|
// packedRgba = [aa bb gg rr]
|
||||
/// Converts a packed <see cref="Rgba32"/> to <see cref="Argb32"/>.
|
// ROL(8, packedRgba) = [bb gg rr aa]
|
||||
/// </summary>
|
return (packedRgba << 8) | (packedRgba >> 24); |
||||
/// <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; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
// RESULTS:
|
/// <summary>
|
||||
// Method | Count | Mean | Error | StdDev | Scaled |
|
/// Converts a packed <see cref="Rgba32"/> to <see cref="Bgra32"/>.
|
||||
// -------------------- |------ |----------:|----------:|----------:|-------:|
|
/// </summary>
|
||||
// Default | 64 | 107.33 ns | 1.0633 ns | 0.9426 ns | 1.00 |
|
/// <returns>The bgra value.</returns>
|
||||
// Default_Generic | 64 | 111.15 ns | 0.3789 ns | 0.3544 ns | 1.04 |
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
// Default_Group2 | 64 | 90.36 ns | 0.7779 ns | 0.6896 ns | 0.84 |
|
public static uint ToBgra32(uint packedRgba) |
||||
// 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 |
|
// packedRgba = [aa bb gg rr]
|
||||
// BitOps_GroupAsULong | 64 | 41.80 ns | 0.2227 ns | 0.2083 ns | 0.39 |
|
// 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 |
|
||||
} |
} |
||||
|
|||||
@ -1,62 +1,60 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
using System.Numerics; |
||||
using BenchmarkDotNet.Attributes; |
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>
|
private static readonly Vector4 A = new Vector4(1.2f); |
||||
/// Has it any effect on performance to store SIMD constants as static readonly fields? Is it OK to always inline them?
|
private static readonly Vector4 B = new Vector4(3.4f); |
||||
/// Spoiler: the difference seems to be statistically insignificant!
|
private static readonly Vector4 C = new Vector4(5.6f); |
||||
/// </summary>
|
private static readonly Vector4 D = new Vector4(7.8f); |
||||
public class Vector4Constants |
|
||||
|
private Random random; |
||||
|
|
||||
|
private Vector4 parameter; |
||||
|
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
{ |
{ |
||||
private static readonly Vector4 A = new Vector4(1.2f); |
this.random = new Random(42); |
||||
private static readonly Vector4 B = new Vector4(3.4f); |
this.parameter = new Vector4( |
||||
private static readonly Vector4 C = new Vector4(5.6f); |
this.GetRandomFloat(), |
||||
private static readonly Vector4 D = new Vector4(7.8f); |
this.GetRandomFloat(), |
||||
|
this.GetRandomFloat(), |
||||
private Random random; |
this.GetRandomFloat()); |
||||
|
|
||||
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(); |
|
||||
} |
} |
||||
|
|
||||
|
[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(); |
||||
} |
} |
||||
|
|||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue