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