mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-09 23:45:42 +00:00
perf: fix performance of intrisics
This changes removes the CPU-arch support provider interfaces that were introduced with 87b6dbdd56
. That commit worsened the performance of the intrinsic methods because it resulted in a box when upcasting the System_SupportProvider value type to an interface, removing the potential for JIT to optimise the code path.
This commit is contained in:
parent
77836d51fc
commit
fdc0c6aa35
@ -1,5 +1,5 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Collections;
|
||||
|
||||
namespace X10D.Tests.Collections;
|
||||
@ -10,7 +10,8 @@ public class ByteTests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToArrayCorrectly()
|
||||
{
|
||||
bool[] bits = ((byte)0b11010100).Unpack();
|
||||
const byte value = 0b11010100;
|
||||
bool[] bits = value.Unpack();
|
||||
|
||||
Assert.AreEqual(8, bits.Length);
|
||||
|
||||
@ -27,8 +28,9 @@ public class ByteTests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
const byte value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[8];
|
||||
((byte)0b11010100).Unpack(bits);
|
||||
value.Unpack(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -41,14 +43,35 @@ public class ByteTests
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
|
||||
{
|
||||
var mock = new Mock<ISsse3SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
|
||||
[TestMethod]
|
||||
public void UnpackInternal_Fallback_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
const byte value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[8];
|
||||
((byte)0b11010100).UnpackInternal(bits, mock.Object);
|
||||
value.UnpackInternal_Fallback(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
Assert.IsTrue(bits[2]);
|
||||
Assert.IsFalse(bits[3]);
|
||||
Assert.IsTrue(bits[4]);
|
||||
Assert.IsFalse(bits[5]);
|
||||
Assert.IsTrue(bits[6]);
|
||||
Assert.IsTrue(bits[7]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
if (!Sse3.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const byte value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[8];
|
||||
value.UnpackInternal_Ssse3(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -64,7 +87,8 @@ public class ByteTests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldRepackEqually()
|
||||
{
|
||||
Assert.AreEqual(0b11010100, ((byte)0b11010100).Unpack().PackByte());
|
||||
const byte value = 0b11010100;
|
||||
Assert.AreEqual(value, value.Unpack().PackByte());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@ -72,8 +96,9 @@ public class ByteTests
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() =>
|
||||
{
|
||||
const byte value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[0];
|
||||
((byte)0b11010100).Unpack(bits);
|
||||
value.Unpack(bits);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Collections;
|
||||
|
||||
namespace X10D.Tests.Collections;
|
||||
@ -10,7 +10,8 @@ public class Int16Tests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToArrayCorrectly()
|
||||
{
|
||||
bool[] bits = ((short)0b11010100).Unpack();
|
||||
const short value = 0b11010100;
|
||||
bool[] bits = value.Unpack();
|
||||
|
||||
Assert.AreEqual(16, bits.Length);
|
||||
|
||||
@ -32,8 +33,31 @@ public class Int16Tests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
const short value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[16];
|
||||
((short)0b11010100).Unpack(bits);
|
||||
value.Unpack(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
Assert.IsTrue(bits[2]);
|
||||
Assert.IsFalse(bits[3]);
|
||||
Assert.IsTrue(bits[4]);
|
||||
Assert.IsFalse(bits[5]);
|
||||
Assert.IsTrue(bits[6]);
|
||||
Assert.IsTrue(bits[7]);
|
||||
|
||||
for (var index = 8; index < 16; index++)
|
||||
{
|
||||
Assert.IsFalse(bits[index]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
|
||||
{
|
||||
const short value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[16];
|
||||
value.UnpackInternal_Fallback(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -52,13 +76,16 @@ public class Int16Tests
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
|
||||
public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
var mock = new Mock<ISsse3SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
if (!Sse3.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const short value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[16];
|
||||
((short)0b11010100).UnpackInternal(bits, mock.Object);
|
||||
value.UnpackInternal_Ssse3(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -79,7 +106,8 @@ public class Int16Tests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldRepackEqually()
|
||||
{
|
||||
Assert.AreEqual(0b11010100, ((short)0b11010100).Unpack().PackInt16());
|
||||
const short value = 0b11010100;
|
||||
Assert.AreEqual(value, value.Unpack().PackInt16());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@ -87,8 +115,9 @@ public class Int16Tests
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() =>
|
||||
{
|
||||
const short value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[0];
|
||||
((short)0b11010100).Unpack(bits);
|
||||
value.Unpack(bits);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Collections;
|
||||
|
||||
namespace X10D.Tests.Collections;
|
||||
@ -10,7 +10,8 @@ public class Int32Tests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToArrayCorrectly()
|
||||
{
|
||||
bool[] bits = 0b11010100.Unpack();
|
||||
const int value = 0b11010100;
|
||||
bool[] bits = value.Unpack();
|
||||
|
||||
Assert.AreEqual(32, bits.Length);
|
||||
|
||||
@ -32,8 +33,31 @@ public class Int32Tests
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
const int value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[32];
|
||||
0b11010100.Unpack(bits);
|
||||
value.Unpack(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
Assert.IsTrue(bits[2]);
|
||||
Assert.IsFalse(bits[3]);
|
||||
Assert.IsTrue(bits[4]);
|
||||
Assert.IsFalse(bits[5]);
|
||||
Assert.IsTrue(bits[6]);
|
||||
Assert.IsTrue(bits[7]);
|
||||
|
||||
for (var index = 8; index < 32; index++)
|
||||
{
|
||||
Assert.IsFalse(bits[index]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UnpackInternal_Fallback_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
const int value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[32];
|
||||
value.UnpackInternal_Fallback(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -52,15 +76,16 @@ public class Int32Tests
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackFromAvx2()
|
||||
public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
var ssse3Mock = new Mock<ISsse3SupportProvider>();
|
||||
var avx2Mock = new Mock<IAvx2SupportProvider>();
|
||||
avx2Mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
ssse3Mock.Setup(provider => provider.IsSupported).Returns(true);
|
||||
if (!Ssse3.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[32];
|
||||
0b11010100.UnpackInternal(bits, ssse3Mock.Object, avx2Mock.Object);
|
||||
value.UnpackInternal_Ssse3(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -78,15 +103,16 @@ public class Int32Tests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallback()
|
||||
public void UnpackInternal_Avx2_ShouldUnpackToSpanCorrectly()
|
||||
{
|
||||
var ssse3Mock = new Mock<ISsse3SupportProvider>();
|
||||
var avx2Mock = new Mock<IAvx2SupportProvider>();
|
||||
ssse3Mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
avx2Mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
if (!Avx2.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[32];
|
||||
0b11010100.UnpackInternal(bits, ssse3Mock.Object, avx2Mock.Object);
|
||||
value.UnpackInternal_Avx2(bits);
|
||||
|
||||
Assert.IsFalse(bits[0]);
|
||||
Assert.IsFalse(bits[1]);
|
||||
@ -102,13 +128,13 @@ public class Int32Tests
|
||||
Assert.IsFalse(bits[index]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[TestMethod]
|
||||
public void Unpack_ShouldRepackEqually()
|
||||
{
|
||||
Assert.AreEqual(0b11010100, 0b11010100.Unpack().PackInt32());
|
||||
const int value = 0b11010100;
|
||||
Assert.AreEqual(value, value.Unpack().PackInt32());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@ -116,8 +142,9 @@ public class Int32Tests
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() =>
|
||||
{
|
||||
const int value = 0b11010100;
|
||||
Span<bool> bits = stackalloc bool[0];
|
||||
0b11010100.Unpack(bits);
|
||||
value.Unpack(bits);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using X10D.Core;
|
||||
|
||||
namespace X10D.Tests.Core;
|
||||
@ -12,9 +12,6 @@ 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);
|
||||
|
||||
@ -24,89 +21,86 @@ public class IntrinsicTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CorrectBoolean_ShouldReturnExpectedVector128Result_GivenInputVector()
|
||||
public void CorrectBooleanInternal_Fallback_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);
|
||||
Vector128<byte> result = inputVector.CorrectBooleanInternal_Fallback();
|
||||
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CorrectBoolean_ShouldReturnExpectedVector128Result_WhenSse2NotSupported()
|
||||
public void CorrectBooleanInternal_Sse2_ShouldReturnExpectedVector128Result_GivenInputVector()
|
||||
{
|
||||
var mock = new Mock<ISse2SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
Vector128<byte> result = inputVector.CorrectBooleanInternal_Sse2();
|
||||
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CorrectBoolean_ShouldReturnExpectedVector256Result_GivenInputVector()
|
||||
public void CorrectBooleanInternal_Avx2_ShouldReturnExpectedVector256Result_GivenInputVector()
|
||||
{
|
||||
var mock = new Mock<IAvx2SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(true);
|
||||
if (!Avx2.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
Vector256<byte> result = inputVector.CorrectBooleanInternal_Avx2();
|
||||
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CorrectBoolean_ShouldReturnExpectedVector256Result_WhenSse2NotSupported()
|
||||
public void CorrectBooleanInternal_Fallback_ShouldReturnExpectedVector256Result_GivenInputVector()
|
||||
{
|
||||
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);
|
||||
Vector256<byte> result = inputVector.CorrectBooleanInternal_Fallback();
|
||||
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReverseElements_ShouldReturnExpectedVector128Result_GivenInputVector()
|
||||
public void ReverseElementsInternal_Fallback_ShouldReturnExpectedVector128Result_GivenInputVector()
|
||||
{
|
||||
var mock = new Mock<ISse2SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(true);
|
||||
|
||||
var inputVector = Vector128.Create(42UL, 69UL);
|
||||
var expectedResult = Vector128.Create(69UL, 42UL);
|
||||
|
||||
Vector128<ulong> result = inputVector.ReverseElementsInternal(mock.Object);
|
||||
Vector128<ulong> result = inputVector.ReverseElementsInternal_Fallback();
|
||||
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReverseElements_ShouldReturnExpectedVector128Result_WhenSse2NotSupported()
|
||||
public void ReverseElementsInternal_Sse2_ShouldReturnExpectedVector128Result_GivenInputVector()
|
||||
{
|
||||
var mock = new Mock<ISse2SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inputVector = Vector128.Create(42UL, 69UL);
|
||||
var expectedResult = Vector128.Create(69UL, 42UL);
|
||||
|
||||
Vector128<ulong> result = inputVector.ReverseElementsInternal(mock.Object);
|
||||
Vector128<ulong> result = inputVector.ReverseElementsInternal_Sse2();
|
||||
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics;
|
||||
@ -21,6 +22,11 @@ public static class ByteExtensions
|
||||
/// <param name="value">The value to unpack.</param>
|
||||
/// <returns>An array of <see cref="bool" /> with length 8.</returns>
|
||||
[Pure]
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public static bool[] Unpack(this byte value)
|
||||
{
|
||||
var buffer = new bool[Size];
|
||||
@ -35,20 +41,12 @@ public static class ByteExtensions
|
||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||
[ExcludeFromCodeCoverage]
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public static void Unpack(this byte value, Span<bool> destination)
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
UnpackInternal(value, destination, new SystemSsse3SupportProvider());
|
||||
#else
|
||||
UnpackInternal(value, destination);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
internal static void UnpackInternal(this byte value, Span<bool> destination, ISsse3SupportProvider? ssse3SupportProvider)
|
||||
#else
|
||||
internal static void UnpackInternal(this byte value, Span<bool> destination)
|
||||
#endif
|
||||
{
|
||||
if (destination.Length < Size)
|
||||
{
|
||||
@ -56,9 +54,7 @@ public static class ByteExtensions
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
ssse3SupportProvider ??= new SystemSsse3SupportProvider();
|
||||
|
||||
if (ssse3SupportProvider.IsSupported)
|
||||
if (Sse3.IsSupported)
|
||||
{
|
||||
UnpackInternal_Ssse3(value, destination);
|
||||
return;
|
||||
@ -68,7 +64,12 @@ public static class ByteExtensions
|
||||
UnpackInternal_Fallback(value, destination);
|
||||
}
|
||||
|
||||
private static void UnpackInternal_Fallback(byte value, Span<bool> destination)
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal static void UnpackInternal_Fallback(this byte value, Span<bool> destination)
|
||||
{
|
||||
for (var index = 0; index < Size; index++)
|
||||
{
|
||||
@ -77,8 +78,12 @@ public static class ByteExtensions
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
|
||||
private unsafe static void UnpackInternal_Ssse3(byte value, Span<bool> destination)
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal unsafe static void UnpackInternal_Ssse3(this byte value, Span<bool> destination)
|
||||
{
|
||||
fixed (bool* pDestination = destination)
|
||||
{
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics;
|
||||
@ -20,6 +22,11 @@ public static class Int16Extensions
|
||||
/// <param name="value">The value to unpack.</param>
|
||||
/// <returns>An array of <see cref="bool" /> with length 16.</returns>
|
||||
[Pure]
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public static bool[] Unpack(this short value)
|
||||
{
|
||||
var ret = new bool[Size];
|
||||
@ -33,20 +40,13 @@ public static class Int16Extensions
|
||||
/// <param name="value">The value to unpack.</param>
|
||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||
[ExcludeFromCodeCoverage]
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public static void Unpack(this short value, Span<bool> destination)
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
UnpackInternal(value, destination, new SystemSsse3SupportProvider());
|
||||
#else
|
||||
UnpackInternal(value, destination);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
internal static void UnpackInternal(this short value, Span<bool> destination, ISsse3SupportProvider? ssse3SupportProvider)
|
||||
#else
|
||||
internal static void UnpackInternal(this short value, Span<bool> destination)
|
||||
#endif
|
||||
{
|
||||
if (destination.Length < Size)
|
||||
{
|
||||
@ -54,9 +54,7 @@ public static class Int16Extensions
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
ssse3SupportProvider ??= new SystemSsse3SupportProvider();
|
||||
|
||||
if (ssse3SupportProvider.IsSupported)
|
||||
if (Sse3.IsSupported)
|
||||
{
|
||||
UnpackInternal_Ssse3(value, destination);
|
||||
return;
|
||||
@ -66,7 +64,12 @@ public static class Int16Extensions
|
||||
UnpackInternal_Fallback(value, destination);
|
||||
}
|
||||
|
||||
private static void UnpackInternal_Fallback(short value, Span<bool> destination)
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal static void UnpackInternal_Fallback(this short value, Span<bool> destination)
|
||||
{
|
||||
for (var index = 0; index < Size; index++)
|
||||
{
|
||||
@ -75,16 +78,12 @@ public static class Int16Extensions
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
private struct SystemSsse3SupportProvider : ISsse3SupportProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
get => Sse3.IsSupported;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void UnpackInternal_Ssse3(short value, Span<bool> destination)
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal unsafe static void UnpackInternal_Ssse3(this short value, Span<bool> destination)
|
||||
{
|
||||
fixed (bool* pDestination = destination)
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics;
|
||||
@ -36,22 +37,6 @@ public static class Int32Extensions
|
||||
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static void Unpack(this int value, Span<bool> destination)
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
UnpackInternal(value, destination, new SystemSsse3SupportProvider(), new SystemAvx2SupportProvider());
|
||||
#else
|
||||
UnpackInternal(value, destination);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void UnpackInternal(this int value,
|
||||
Span<bool> destination
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
,
|
||||
ISsse3SupportProvider? ssse3SupportProvider,
|
||||
IAvx2SupportProvider? avx2SupportProvider
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (destination.Length < Size)
|
||||
{
|
||||
@ -59,16 +44,13 @@ public static class Int32Extensions
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
ssse3SupportProvider ??= new SystemSsse3SupportProvider();
|
||||
avx2SupportProvider ??= new SystemAvx2SupportProvider();
|
||||
|
||||
if (avx2SupportProvider.IsSupported)
|
||||
if (Avx2.IsSupported)
|
||||
{
|
||||
UnpackInternal_Avx2(value, destination);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ssse3SupportProvider.IsSupported)
|
||||
if (Sse3.IsSupported)
|
||||
{
|
||||
UnpackInternal_Ssse3(value, destination);
|
||||
return;
|
||||
@ -78,7 +60,12 @@ public static class Int32Extensions
|
||||
UnpackInternal_Fallback(value, destination);
|
||||
}
|
||||
|
||||
private static void UnpackInternal_Fallback(int value, Span<bool> destination)
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal static void UnpackInternal_Fallback(this int value, Span<bool> destination)
|
||||
{
|
||||
for (var index = 0; index < Size; index++)
|
||||
{
|
||||
@ -87,7 +74,12 @@ public static class Int32Extensions
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
private static unsafe void UnpackInternal_Ssse3(int value, Span<bool> destination)
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal static unsafe void UnpackInternal_Ssse3(this int value, Span<bool> destination)
|
||||
{
|
||||
fixed (bool* pDestination = destination)
|
||||
{
|
||||
@ -117,7 +109,7 @@ public static class Int32Extensions
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void UnpackInternal_Avx2(int value, Span<bool> destination)
|
||||
internal static unsafe void UnpackInternal_Avx2(this int value, Span<bool> destination)
|
||||
{
|
||||
fixed (bool* pDestination = destination)
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ public static class IntrinsicExtensions
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static Vector128<byte> CorrectBoolean(this Vector128<byte> vector)
|
||||
{
|
||||
return CorrectBooleanInternal(vector, new SystemSse2SupportProvider());
|
||||
return Sse2.IsSupported ? CorrectBooleanInternal_Sse2(vector) : CorrectBooleanInternal_Fallback(vector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -94,7 +94,7 @@ public static class IntrinsicExtensions
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static Vector256<byte> CorrectBoolean(this Vector256<byte> vector)
|
||||
{
|
||||
return CorrectBooleanInternal(vector, new SystemAvx2SupportProvider());
|
||||
return Avx2.IsSupported ? CorrectBooleanInternal_Avx2(vector) : CorrectBooleanInternal_Fallback(vector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -118,26 +118,13 @@ public static class IntrinsicExtensions
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static Vector128<ulong> ReverseElements(this Vector128<ulong> vector)
|
||||
{
|
||||
return ReverseElementsInternal(vector, new SystemSse2SupportProvider());
|
||||
return Sse2.IsSupported ? ReverseElementsInternal_Sse2(vector) : ReverseElementsInternal_Fallback(vector);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static Vector128<byte> CorrectBooleanInternal(this Vector128<byte> vector, ISse2SupportProvider? sse2SupportProvider)
|
||||
internal static Vector128<byte> CorrectBooleanInternal_Fallback(this Vector128<byte> vector)
|
||||
{
|
||||
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++)
|
||||
@ -151,18 +138,18 @@ public static class IntrinsicExtensions
|
||||
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static Vector256<byte> CorrectBooleanInternal(this Vector256<byte> vector, IAvx2SupportProvider? supportProvider)
|
||||
internal static Vector128<byte> CorrectBooleanInternal_Sse2(this Vector128<byte> vector)
|
||||
{
|
||||
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));
|
||||
Vector128<byte> cmp = Sse2.CompareEqual(vector, Vector128<byte>.Zero);
|
||||
Vector128<byte> result = Sse2.AndNot(cmp, Vector128.Create((byte)1));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static Vector256<byte> CorrectBooleanInternal_Fallback(this Vector256<byte> vector)
|
||||
{
|
||||
Vector256<byte> output = IntrinsicUtility.GetUninitializedVector256<byte>();
|
||||
|
||||
for (var index = 0; index < Vector256<byte>.Count; index++)
|
||||
@ -175,17 +162,19 @@ public static class IntrinsicExtensions
|
||||
}
|
||||
|
||||
[Pure]
|
||||
[CLSCompliant(false)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static Vector128<ulong> ReverseElementsInternal(this Vector128<ulong> vector, ISse2SupportProvider? supportProvider)
|
||||
internal static Vector256<byte> CorrectBooleanInternal_Avx2(this Vector256<byte> vector)
|
||||
{
|
||||
supportProvider ??= new SystemSse2SupportProvider();
|
||||
Vector256<byte> cmp = Avx2.CompareEqual(vector, Vector256<byte>.Zero);
|
||||
Vector256<byte> result = Avx2.AndNot(cmp, Vector256.Create((byte)1));
|
||||
|
||||
if (supportProvider.IsSupported)
|
||||
{
|
||||
return Sse2.Shuffle(vector.AsDouble(), vector.AsDouble(), 0b01).AsUInt64();
|
||||
return result;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static Vector128<ulong> ReverseElementsInternal_Fallback(this Vector128<ulong> vector)
|
||||
{
|
||||
Vector128<ulong> output = IntrinsicUtility.GetUninitializedVector128<ulong>();
|
||||
|
||||
Unsafe.As<Vector128<ulong>, ulong>(ref output) = Unsafe.Add(ref Unsafe.As<Vector128<ulong>, ulong>(ref vector), 1);
|
||||
@ -193,5 +182,12 @@ public static class IntrinsicExtensions
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static Vector128<ulong> ReverseElementsInternal_Sse2(this Vector128<ulong> vector)
|
||||
{
|
||||
return Sse2.Shuffle(vector.AsDouble(), vector.AsDouble(), 0b01).AsUInt64();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1,13 +0,0 @@
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object which provides the status of the support of AVX2 instructions.
|
||||
/// </summary>
|
||||
public interface IAvx2SupportProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether AVX2 instructions are supported on this platform.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if AVX2 instructions are supported; otherwise, <see langword="false" />.</value>
|
||||
bool IsSupported { get; }
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
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; }
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object which provides the status of the support of SSSE3 instructions.
|
||||
/// </summary>
|
||||
public interface ISsse3SupportProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether SSSE3 instructions are supported on this platform.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if SSSE3 instructions are supported; otherwise, <see langword="false" />.</value>
|
||||
bool IsSupported { get; }
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace X10D;
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal struct SystemAvx2SupportProvider : IAvx2SupportProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
get => Avx2.IsSupported;
|
||||
#else
|
||||
get => false;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace X10D;
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal struct SystemSse2SupportProvider : ISse2SupportProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
get => Sse2.IsSupported;
|
||||
#else
|
||||
get => false;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace X10D;
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal struct SystemSsse3SupportProvider : ISsse3SupportProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
get => Sse3.IsSupported;
|
||||
#else
|
||||
get => false;
|
||||
#endif
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user