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:
Oliver Booth 2023-04-02 15:17:28 +01:00
parent 77836d51fc
commit fdc0c6aa35
No known key found for this signature in database
GPG Key ID: 20BEB9DC87961025
14 changed files with 243 additions and 275 deletions

View File

@ -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);
});
}
}

View File

@ -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);
});
}
}

View File

@ -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);
});
}
}

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}