diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 1395975ec..02a2e9ee5 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -61,9 +61,6 @@ namespace SixLabors.ImageSharp return x & (m - 1); } - [MethodImpl(InliningOptions.ShortMethod)] - public static float Clamp(float x, float min, float max) => Math.Min(max, Math.Max(min, x)); - /// /// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation. /// diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index fade8da79..737e62006 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan source, Span dest) { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same size!"); + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); #if NETCOREAPP2_1 ExtendedIntrinsics.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest); @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest) { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same size!"); + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); #if NETCOREAPP2_1 ExtendedIntrinsics.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest); @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp } [MethodImpl(InliningOptions.ShortMethod)] - private static byte ConvertToByte(float f) => (byte)ImageMaths.Clamp((f * 255f) + 0.5f, 0, 255f); + private static byte ConvertToByte(float f) => (byte)ComparableExtensions.Clamp((f * 255f) + 0.5f, 0, 255f); [Conditional("DEBUG")] private static void VerifyIsAvx2Compatible(string operation) diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs new file mode 100644 index 000000000..3b7dea095 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + 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() + { + float acc = 0; + + for (int i = 0; i < Values.Length; i++) + { + acc += ClampUsingMathF(Values[i], this.min, this.max); + } + + return acc; + } + + [Benchmark] + public float UsingBranching() + { + float acc = 0; + + for (int i = 0; i < Values.Length; i++) + { + 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)] + private static float ClampUsingBranching(float x, float min, float max) + { + if (x >= max) + { + return max; + } + + if (x <= min) + { + return min; + } + + return x; + } + + // RESULTS: + // Method | Mean | Error | StdDev | Scaled | + // --------------- |---------:|----------:|----------:|-------:| + // UsingMathF | 30.37 ns | 0.3764 ns | 0.3337 ns | 1.00 | + // UsingBranching | 18.66 ns | 0.1043 ns | 0.0871 ns | 0.61 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/Clamp.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs similarity index 98% rename from tests/ImageSharp.Benchmarks/General/BasicMath/Clamp.cs rename to tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs index d486cb2f3..6ce82ba11 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/Clamp.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs @@ -10,7 +10,7 @@ using BenchmarkDotNet.Attributes; namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath { - public class Clamp + public class ClampInt32IntoByte { [Params(-1, 0, 255, 256)] public int Value { get; set; } diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs index d8b1525be..75ef611a5 100644 --- a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers [InlineData(10.1f, -0.1f, 10, 10f)] public void Clamp(float x, float min, float max, float expected) { - float actual = ImageMaths.Clamp(x, min, max); + float actual = x.Clamp(min, max); Assert.Equal(expected, actual); }