Skip to content

Commit

Permalink
perf: fix performance of intrisics
Browse files Browse the repository at this point in the history
This changes removes the CPU-arch support provider interfaces that were introduced with 87b6dbd. 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.
  • Loading branch information
oliverbooth committed Apr 2, 2023
1 parent 77836d5 commit fdc0c6a
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 271 deletions.
45 changes: 35 additions & 10 deletions X10D.Tests/src/Collections/ByteTests.cs
@@ -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;
Expand All @@ -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);

Expand All @@ -27,8 +28,9 @@ public void Unpack_ShouldUnpackToArrayCorrectly()
[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]);
Expand All @@ -41,14 +43,35 @@ public void Unpack_ShouldUnpackToSpanCorrectly()
}

#if NET5_0_OR_GREATER

[TestMethod]
public void UnpackInternal_Fallback_ShouldUnpackToSpanCorrectly()
{
const byte value = 0b11010100;
Span<bool> bits = stackalloc bool[8];
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 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 byte value = 0b11010100;
Span<bool> bits = stackalloc bool[8];
((byte)0b11010100).UnpackInternal(bits, mock.Object);
value.UnpackInternal_Ssse3(bits);

Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Expand All @@ -64,16 +87,18 @@ public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
[TestMethod]
public void Unpack_ShouldRepackEqually()
{
Assert.AreEqual(0b11010100, ((byte)0b11010100).Unpack().PackByte());
const byte value = 0b11010100;
Assert.AreEqual(value, value.Unpack().PackByte());
}

[TestMethod]
public void Unpack_ShouldThrow_GivenTooSmallSpan()
{
Assert.ThrowsException<ArgumentException>(() =>
{
const byte value = 0b11010100;
Span<bool> bits = stackalloc bool[0];
((byte)0b11010100).Unpack(bits);
value.Unpack(bits);
});
}
}
49 changes: 39 additions & 10 deletions X10D.Tests/src/Collections/Int16Tests.cs
@@ -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;
Expand All @@ -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);

Expand All @@ -32,8 +33,9 @@ public void Unpack_ShouldUnpackToArrayCorrectly()
[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]);
Expand All @@ -50,15 +52,40 @@ public void Unpack_ShouldUnpackToSpanCorrectly()
}
}

#if NET5_0_OR_GREATER
[TestMethod]
public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
{
var mock = new Mock<ISsse3SupportProvider>();
mock.Setup(provider => provider.IsSupported).Returns(false);
const short value = 0b11010100;
Span<bool> bits = stackalloc bool[16];
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]);

for (var index = 8; index < 16; index++)
{
Assert.IsFalse(bits[index]);
}
}

#if NET5_0_OR_GREATER
[TestMethod]
public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
{
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]);
Expand All @@ -79,16 +106,18 @@ public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
[TestMethod]
public void Unpack_ShouldRepackEqually()
{
Assert.AreEqual(0b11010100, ((short)0b11010100).Unpack().PackInt16());
const short value = 0b11010100;
Assert.AreEqual(value, value.Unpack().PackInt16());
}

[TestMethod]
public void Unpack_ShouldThrow_GivenTooSmallSpan()
{
Assert.ThrowsException<ArgumentException>(() =>
{
const short value = 0b11010100;
Span<bool> bits = stackalloc bool[0];
((short)0b11010100).Unpack(bits);
value.Unpack(bits);
});
}
}
65 changes: 46 additions & 19 deletions X10D.Tests/src/Collections/Int32Tests.cs
@@ -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;
Expand All @@ -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);

Expand All @@ -32,8 +33,31 @@ public void Unpack_ShouldUnpackToArrayCorrectly()
[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]);
Expand All @@ -52,15 +76,16 @@ public void Unpack_ShouldUnpackToSpanCorrectly()

#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]);
Expand All @@ -78,15 +103,16 @@ public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackFromAvx2()
}

[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]);
Expand All @@ -102,22 +128,23 @@ public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallback()
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]
public void Unpack_ShouldThrow_GivenTooSmallSpan()
{
Assert.ThrowsException<ArgumentException>(() =>
{
const int value = 0b11010100;
Span<bool> bits = stackalloc bool[0];
0b11010100.Unpack(bits);
value.Unpack(bits);
});
}
}

0 comments on commit fdc0c6a

Please sign in to comment.