test: add tests for CorrectBoolean (#73)

This commit is contained in:
Oliver Booth 2023-04-01 23:40:41 +01:00
parent 34c49a2228
commit 6f16c0df3c
No known key found for this signature in database
GPG Key ID: 20BEB9DC87961025
5 changed files with 180 additions and 44 deletions

View File

@ -0,0 +1,86 @@
#if NET6_0_OR_GREATER
using System.Runtime.Intrinsics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using X10D.Core;
namespace X10D.Tests.Core;
[TestClass]
public class IntrinsicTests
{
[TestMethod]
public void CorrectBoolean_ShouldReturnExpectedVector64Result_GivenInputVector()
{
var mock = new Mock<ISse2SupportProvider>();
mock.Setup(provider => provider.IsSupported).Returns(true);
var inputVector = Vector64.Create(0, 1, 2, 0, 3, 0, 0, (byte)4);
var expectedResult = Vector64.Create(0, 1, 1, 0, 1, 0, 0, (byte)1);
Vector64<byte> result = inputVector.CorrectBoolean();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBoolean_ShouldReturnExpectedVector128Result_GivenInputVector()
{
var mock = new Mock<ISse2SupportProvider>();
mock.Setup(provider => provider.IsSupported).Returns(true);
var inputVector = Vector128.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, (byte)8);
var expectedResult = Vector128.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, (byte)1);
Vector128<byte> result = inputVector.CorrectBooleanInternal(mock.Object);
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBoolean_ShouldReturnExpectedVector128Result_WhenSse2NotSupported()
{
var mock = new Mock<ISse2SupportProvider>();
mock.Setup(provider => provider.IsSupported).Returns(false);
var inputVector = Vector128.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, (byte)8);
var expectedResult = Vector128.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, (byte)1);
Vector128<byte> result = inputVector.CorrectBooleanInternal(mock.Object);
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBoolean_ShouldReturnExpectedVector256Result_GivenInputVector()
{
var mock = new Mock<IAvx2SupportProvider>();
mock.Setup(provider => provider.IsSupported).Returns(true);
var inputVector = Vector256.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, 8, 0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0,
0, 7, (byte)8);
var expectedResult = Vector256.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
0, 0, 1, (byte)1);
Vector256<byte> result = inputVector.CorrectBooleanInternal(mock.Object);
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBoolean_ShouldReturnExpectedVector256Result_WhenSse2NotSupported()
{
var mock = new Mock<IAvx2SupportProvider>();
mock.Setup(provider => provider.IsSupported).Returns(false);
var inputVector = Vector256.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, 8, 0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0,
0, 7, (byte)8);
var expectedResult = Vector256.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
0, 0, 1, (byte)1);
Vector256<byte> result = inputVector.CorrectBooleanInternal(mock.Object);
Assert.AreEqual(expectedResult, result);
}
}
#endif

View File

@ -1,5 +1,5 @@
#if NETCOREAPP3_0_OR_GREATER
#if NETCOREAPP3_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
@ -35,15 +35,15 @@ public static class IntrinsicExtensions
// TODO: AdvSimd implementation.
// TODO: WasmSimd implementation. (?)
var output = IntrinsicUtility.GetUninitializedVector64<byte>();
Vector64<byte> output = IntrinsicUtility.GetUninitializedVector64<byte>();
for (int i = 0; i < Vector64<byte>.Count; i++)
for (var i = 0; i < Vector64<byte>.Count; i++)
{
ref var writeElement = ref Unsafe.Add(ref Unsafe.As<Vector64<byte>, byte>(ref output), i);
ref byte writeElement = ref Unsafe.Add(ref Unsafe.As<Vector64<byte>, byte>(ref output), i);
#if NET7_0_OR_GREATER
writeElement = vector[i] == 0 ? (byte)0 : (byte)1;
#else
var element = Unsafe.Add(ref Unsafe.As<Vector64<byte>, byte>(ref vector), i);
byte element = Unsafe.Add(ref Unsafe.As<Vector64<byte>, byte>(ref vector), i);
writeElement = element == 0 ? (byte)0 : (byte)1;
#endif
}
@ -68,28 +68,10 @@ public static class IntrinsicExtensions
/// </returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
[ExcludeFromCodeCoverage]
public static Vector128<byte> CorrectBoolean(this Vector128<byte> vector)
{
if (Sse2.IsSupported)
{
var cmp = Sse2.CompareEqual(vector, Vector128<byte>.Zero);
var result = Sse2.AndNot(cmp, Vector128.Create((byte)1));
return result;
}
// TODO: AdvSimd implementation.
// TODO: WasmSimd implementation.
var output = IntrinsicUtility.GetUninitializedVector128<byte>();
for (int i = 0; i < Vector128<byte>.Count; i++)
{
Unsafe.Add(ref Unsafe.As<Vector128<byte>, byte>(ref output), i) =
Unsafe.Add(ref Unsafe.As<Vector128<byte>, byte>(ref vector), i) == 0 ? (byte)0 : (byte)1;
}
return output;
return CorrectBooleanInternal(vector, new SystemSse2SupportProvider());
}
/// <summary>
@ -109,25 +91,10 @@ public static class IntrinsicExtensions
/// </returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
[ExcludeFromCodeCoverage]
public static Vector256<byte> CorrectBoolean(this Vector256<byte> vector)
{
if (Avx2.IsSupported)
{
var cmp = Avx2.CompareEqual(vector, Vector256<byte>.Zero);
var result = Avx2.AndNot(cmp, Vector256.Create((byte)1));
return result;
}
var output = IntrinsicUtility.GetUninitializedVector256<byte>();
for (int i = 0; i < Vector256<byte>.Count; i++)
{
Unsafe.Add(ref Unsafe.As<Vector256<byte>, byte>(ref output), i) =
Unsafe.Add(ref Unsafe.As<Vector256<byte>, byte>(ref vector), i) == 0 ? (byte)0 : (byte)1;
}
return output;
return CorrectBooleanInternal(vector, new SystemAvx2SupportProvider());
}
/// <summary>
@ -162,5 +129,58 @@ public static class IntrinsicExtensions
return output;
}
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static Vector128<byte> CorrectBooleanInternal(this Vector128<byte> vector, ISse2SupportProvider? sse2SupportProvider)
{
sse2SupportProvider ??= new SystemSse2SupportProvider();
if (sse2SupportProvider.IsSupported)
{
Vector128<byte> cmp = Sse2.CompareEqual(vector, Vector128<byte>.Zero);
Vector128<byte> result = Sse2.AndNot(cmp, Vector128.Create((byte)1));
return result;
}
// TODO: AdvSimd implementation.
// TODO: WasmSimd implementation.
Vector128<byte> output = IntrinsicUtility.GetUninitializedVector128<byte>();
for (var index = 0; index < Vector128<byte>.Count; index++)
{
Unsafe.Add(ref Unsafe.As<Vector128<byte>, byte>(ref output), index) =
Unsafe.Add(ref Unsafe.As<Vector128<byte>, byte>(ref vector), index) == 0 ? (byte)0 : (byte)1;
}
return output;
}
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static Vector256<byte> CorrectBooleanInternal(this Vector256<byte> vector, IAvx2SupportProvider? supportProvider)
{
supportProvider ??= new SystemAvx2SupportProvider();
if (supportProvider.IsSupported)
{
Vector256<byte> cmp = Avx2.CompareEqual(vector, Vector256<byte>.Zero);
Vector256<byte> result = Avx2.AndNot(cmp, Vector256.Create((byte)1));
return result;
}
Vector256<byte> output = IntrinsicUtility.GetUninitializedVector256<byte>();
for (var index = 0; index < Vector256<byte>.Count; index++)
{
Unsafe.Add(ref Unsafe.As<Vector256<byte>, byte>(ref output), index) =
Unsafe.Add(ref Unsafe.As<Vector256<byte>, byte>(ref vector), index) == 0 ? (byte)0 : (byte)1;
}
return output;
}
}
#endif

View File

@ -3,7 +3,6 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace X10D.Core;

View File

@ -0,0 +1,13 @@
namespace X10D;
/// <summary>
/// Represents an object which provides the status of the support of SSE2 instructions.
/// </summary>
public interface ISse2SupportProvider
{
/// <summary>
/// Gets a value indicating whether SSE2 instructions are supported on this platform.
/// </summary>
/// <value><see langword="true" /> if SSE2 instructions are supported; otherwise, <see langword="false" />.</value>
bool IsSupported { get; }
}

View File

@ -0,0 +1,18 @@
#if NETCOREAPP3_0_OR_GREATER
using System.Runtime.Intrinsics.X86;
#endif
namespace X10D;
internal struct SystemSse2SupportProvider : ISse2SupportProvider
{
/// <inheritdoc />
public bool IsSupported
{
#if NETCOREAPP3_0_OR_GREATER
get => Sse2.IsSupported;
#else
get => false;
#endif
}
}