mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 302 additions and 43 deletions
@ -0,0 +1,45 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Various extension and utility methods for <see cref="Vector4"/> and <see cref="Vector{T}"/> utilizing SIMD capabilities
|
|||
/// </summary>
|
|||
internal static class SimdUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Transform all scalars in 'v' in a way that converting them to <see cref="int"/> would have rounding semantics.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static Vector4 PseudoRound(this Vector4 v) |
|||
{ |
|||
var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1)); |
|||
|
|||
return v + (sign * 0.5f); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics.
|
|||
/// Source:
|
|||
/// <see>
|
|||
/// <cref>https://github.com/tmpvar/voxviz/blob/master/deps/glm/glm/simd/common.h#L110</cref>
|
|||
/// </see>
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static Vector<float> FastRound(this Vector<float> x) |
|||
{ |
|||
Vector<int> magic0 = new Vector<int>(-2147483648); // 0x80000000
|
|||
Vector<float> sgn0 = Vector.AsVectorSingle(magic0); |
|||
Vector<float> and0 = Vector.BitwiseAnd(sgn0, x); |
|||
Vector<float> or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f)); |
|||
Vector<float> add0 = Vector.Add(x, or0); |
|||
Vector<float> sub0 = Vector.Subtract(add0, or0); |
|||
return sub0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using SixLabors.ImageSharp; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Common; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.General |
|||
{ |
|||
public class Block8x8F_Round |
|||
{ |
|||
private Block8x8F block = default(Block8x8F); |
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
if (Vector<float>.Count != 8) |
|||
{ |
|||
throw new NotSupportedException("Vector<float>.Count != 8"); |
|||
} |
|||
|
|||
for (int i = 0; i < Block8x8F.Size; i++) |
|||
{ |
|||
this.block[i] = i * 44.8f; |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void ScalarRound() |
|||
{ |
|||
ref float b = ref Unsafe.As<Block8x8F, float>(ref this.block); |
|||
|
|||
for (int i = 0; i < Block8x8F.Size; i++) |
|||
{ |
|||
ref float v = ref Unsafe.Add(ref b, i); |
|||
v = MathF.Round(v); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void SimdRound() |
|||
{ |
|||
ref Block8x8F b = ref this.block; |
|||
|
|||
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V0L); |
|||
row0 = SimdUtils.FastRound(row0); |
|||
ref Vector<float> row1 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V1L); |
|||
row1 = SimdUtils.FastRound(row1); |
|||
ref Vector<float> row2 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V2L); |
|||
row2 = SimdUtils.FastRound(row2); |
|||
ref Vector<float> row3 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V3L); |
|||
row3 = SimdUtils.FastRound(row3); |
|||
ref Vector<float> row4 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V4L); |
|||
row4 = SimdUtils.FastRound(row4); |
|||
ref Vector<float> row5 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V5L); |
|||
row5 = SimdUtils.FastRound(row5); |
|||
ref Vector<float> row6 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V6L); |
|||
row6 = SimdUtils.FastRound(row6); |
|||
ref Vector<float> row7 = ref Unsafe.As<Vector4, Vector<float>>(ref b.V7L); |
|||
row7 = SimdUtils.FastRound(row7); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
using System; |
|||
using System.Numerics; |
|||
using Xunit; |
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Common |
|||
{ |
|||
using Xunit.Abstractions; |
|||
|
|||
public class SimdUtilsTests |
|||
{ |
|||
private ITestOutputHelper Output { get; } |
|||
|
|||
public SimdUtilsTests(ITestOutputHelper output) |
|||
{ |
|||
this.Output = output; |
|||
} |
|||
|
|||
private static int R(float f) => (int)MathF.Round(f, MidpointRounding.AwayFromZero); |
|||
|
|||
private static int Re(float f) => (int)MathF.Round(f, MidpointRounding.ToEven); |
|||
|
|||
// TODO: Move this to a proper test class!
|
|||
[Theory] |
|||
[InlineData(0.32, 54.5, -3.5, -4.1)] |
|||
[InlineData(5.3, 536.4, 4.5, 8.1)] |
|||
public void PseudoRound(float x, float y, float z, float w) |
|||
{ |
|||
var v = new Vector4(x, y, z, w); |
|||
|
|||
Vector4 actual = v.PseudoRound(); |
|||
|
|||
Assert.Equal( |
|||
R(v.X), |
|||
(int)actual.X |
|||
); |
|||
Assert.Equal( |
|||
R(v.Y), |
|||
(int)actual.Y |
|||
); |
|||
Assert.Equal( |
|||
R(v.Z), |
|||
(int)actual.Z |
|||
); |
|||
Assert.Equal( |
|||
R(v.W), |
|||
(int)actual.W |
|||
); |
|||
} |
|||
|
|||
private static Vector<float> CreateExactTestVector1() |
|||
{ |
|||
float[] data = new float[Vector<float>.Count]; |
|||
|
|||
data[0] = 0.1f; |
|||
data[1] = 0.4f; |
|||
data[2] = 0.5f; |
|||
data[3] = 0.9f; |
|||
|
|||
for (int i = 4; i < Vector<float>.Count; i++) |
|||
{ |
|||
data[i] = data[i - 4] + 100f; |
|||
} |
|||
return new Vector<float>(data); |
|||
} |
|||
|
|||
private static Vector<float> CreateRandomTestVector(int seed, float scale) |
|||
{ |
|||
float[] data = new float[Vector<float>.Count]; |
|||
Random rnd = new Random(); |
|||
for (int i = 0; i < Vector<float>.Count; i++) |
|||
{ |
|||
float v = (float)rnd.NextDouble() - 0.5f; |
|||
v *= 2 * scale; |
|||
data[i] = v; |
|||
} |
|||
return new Vector<float>(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Round() |
|||
{ |
|||
Vector<float> v = CreateExactTestVector1(); |
|||
Vector<float> r = v.FastRound(); |
|||
|
|||
this.Output.WriteLine(r.ToString()); |
|||
|
|||
AssertEvenRoundIsCorrect(r, v); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(1, 1f)] |
|||
[InlineData(1, 10f)] |
|||
[InlineData(1, 1000f)] |
|||
[InlineData(42, 1f)] |
|||
[InlineData(42, 10f)] |
|||
[InlineData(42, 1000f)] |
|||
public void Round_RandomValues(int seed, float scale) |
|||
{ |
|||
Vector<float> v = CreateRandomTestVector(seed, scale); |
|||
Vector<float> r = v.FastRound(); |
|||
|
|||
this.Output.WriteLine(v.ToString()); |
|||
this.Output.WriteLine(r.ToString()); |
|||
|
|||
AssertEvenRoundIsCorrect(r, v); |
|||
} |
|||
|
|||
private static void AssertEvenRoundIsCorrect(Vector<float> r, Vector<float> v) |
|||
{ |
|||
for (int i = 0; i < Vector<float>.Count; i++) |
|||
{ |
|||
int actual = (int)r[i]; |
|||
int expected = Re(v[i]); |
|||
Assert.Equal(expected, actual); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue