Code fixup, fast Int8 packing

This commit is contained in:
RealityProgrammer 2023-03-06 10:39:58 +07:00
parent a808cab37f
commit 5e4044f965
10 changed files with 136 additions and 36 deletions

View File

@ -6,6 +6,13 @@ namespace X10D.Tests.Collections;
[TestClass] [TestClass]
public class BoolListTests public class BoolListTests
{ {
[TestMethod]
public void Pack8BitSpan_Should_Pack_Correctly()
{
Span<bool> span = stackalloc bool[8] { true, false, true, false, true, false, true, false };
Assert.AreEqual(85, span.PackByte()); // 01010101
}
[TestMethod] [TestMethod]
public void Pack8Bit_Should_Pack_Correctly() public void Pack8Bit_Should_Pack_Correctly()
{ {

View File

@ -22,7 +22,7 @@ public static class ByteExtensions
[Pure] [Pure]
public static bool[] Unpack(this byte value) public static bool[] Unpack(this byte value)
{ {
bool[] buffer = new bool[Size]; var buffer = new bool[Size];
value.Unpack(buffer); value.Unpack(buffer);
return buffer; return buffer;
} }
@ -59,10 +59,10 @@ public static class ByteExtensions
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
); );
var mask1Lo = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); var mask1 = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
var vec = Vector128.Create(value).AsByte(); var vec = Vector128.Create(value).AsByte();
var shuffle = Ssse3.Shuffle(vec, mask1Lo); var shuffle = Ssse3.Shuffle(vec, mask1);
var and = Sse2.AndNot(shuffle, mask2); var and = Sse2.AndNot(shuffle, mask2);
var cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero); var cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero);
var correctness = Sse2.And(cmp, Vector128.Create((byte)0x01)); var correctness = Sse2.And(cmp, Vector128.Create((byte)0x01));

View File

@ -66,12 +66,12 @@ public static class DictionaryExtensions
return value; return value;
#else #else
if (dictionary.TryGetValue(key, out var old)) if (dictionary.TryGetValue(key, out TValue? old))
{ {
var newValue = updateValueFactory(key, old); var updated = updateValueFactory(key, old);
dictionary[key] = newValue; dictionary[key] = updated;
return newValue; return updated;
} }
else else
{ {
@ -124,12 +124,12 @@ public static class DictionaryExtensions
} }
#endif #endif
if (dictionary.TryGetValue(key, out var old)) if (dictionary.TryGetValue(key, out TValue? old))
{ {
var newValue = updateValueFactory(key, old); var updated = updateValueFactory(key, old);
dictionary[key] = newValue; dictionary[key] = updated;
return newValue; return updated;
} }
else else
{ {
@ -192,7 +192,7 @@ public static class DictionaryExtensions
#endif #endif
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists); ref TValue? value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists);
if (exists) if (exists)
{ {
value = updateValueFactory(key, value!); value = updateValueFactory(key, value!);
@ -204,12 +204,12 @@ public static class DictionaryExtensions
return value; return value;
#else #else
if (dictionary.TryGetValue(key, out var old)) if (dictionary.TryGetValue(key, out TValue? old))
{ {
var update = updateValueFactory(key, old); var updated = updateValueFactory(key, old);
dictionary[key] = update; dictionary[key] = updated;
return update; return updated;
} }
else else
{ {
@ -274,12 +274,12 @@ public static class DictionaryExtensions
} }
#endif #endif
if (dictionary.TryGetValue(key, out var old)) if (dictionary.TryGetValue(key, out TValue? old))
{ {
var update = updateValueFactory(key, old); var updated = updateValueFactory(key, old);
dictionary[key] = update; dictionary[key] = updated;
return update; return updated;
} }
else else
{ {
@ -350,7 +350,7 @@ public static class DictionaryExtensions
#endif #endif
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists); ref TValue? value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists);
if (exists) if (exists)
{ {
value = updateValueFactory(key, value!, factoryArgument); value = updateValueFactory(key, value!, factoryArgument);
@ -362,12 +362,12 @@ public static class DictionaryExtensions
return value; return value;
#else #else
if (dictionary.TryGetValue(key, out var old)) if (dictionary.TryGetValue(key, out TValue? old))
{ {
var update = updateValueFactory(key, old, factoryArgument); var updated = updateValueFactory(key, old, factoryArgument);
dictionary[key] = update; dictionary[key] = updated;
return update; return updated;
} }
else else
{ {
@ -438,12 +438,12 @@ public static class DictionaryExtensions
} }
#endif #endif
if (dictionary.TryGetValue(key, out var old)) if (dictionary.TryGetValue(key, out TValue? old))
{ {
var update = updateValueFactory(key, old, factoryArgument); var updated = updateValueFactory(key, old, factoryArgument);
dictionary[key] = update; dictionary[key] = updated;
return update; return updated;
} }
else else
{ {

View File

@ -22,7 +22,7 @@ public static class Int16Extensions
[Pure] [Pure]
public static bool[] Unpack(this short value) public static bool[] Unpack(this short value)
{ {
bool[] ret = new bool[Size]; var ret = new bool[Size];
value.Unpack(ret); value.Unpack(ret);
return ret; return ret;
} }

View File

@ -22,7 +22,7 @@ public static class Int32Extensions
[Pure] [Pure]
public static bool[] Unpack(this int value) public static bool[] Unpack(this int value)
{ {
bool[] ret = new bool[Size]; var ret = new bool[Size];
value.Unpack(ret); value.Unpack(ret);
return ret; return ret;
} }
@ -97,7 +97,7 @@ public static class Int32Extensions
var mask1Hi = Vector128.Create((byte)2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3); var mask1Hi = Vector128.Create((byte)2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
var one = Vector128.Create((byte)0x01); var one = Vector128.Create((byte)0x01);
var vec = Vector128.Create(value).AsByte(); var vec = Vector128.Create(value).AsByte();
var shuffle = Ssse3.Shuffle(vec, mask1Lo); var shuffle = Ssse3.Shuffle(vec, mask1Lo);
var and = Sse2.AndNot(shuffle, mask2); var and = Sse2.AndNot(shuffle, mask2);

View File

@ -17,7 +17,7 @@ public static class Int64Extensions
[Pure] [Pure]
public static bool[] Unpack(this long value) public static bool[] Unpack(this long value)
{ {
bool[] ret = new bool[Size]; var ret = new bool[Size];
value.Unpack(ret); value.Unpack(ret);
return ret; return ret;
} }

View File

@ -2,6 +2,12 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if NETCOREAPP3_0_OR_GREATER
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
#endif
namespace X10D.Core; namespace X10D.Core;
/// <summary> /// <summary>
@ -100,4 +106,93 @@ public static class SpanExtensions
return false; return false;
#endif // NET6_0_OR_GREATER #endif // NET6_0_OR_GREATER
} }
/// <summary>
/// Packs a <see cref="Span{T}"/> of booleans into a <see cref="byte" />.
/// </summary>
/// <param name="source">The span of booleans to pack.</param>
/// <returns>An 8-bit unsigned integer containing the packed booleans.</returns>
/// <exception cref="ArgumentException"><paramref name="source" /> contains more than 8 elements.</exception>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte PackByte(this Span<bool> source)
{
return PackByte((ReadOnlySpan<bool>)source);
}
/// <summary>
/// Packs a <see cref="ReadOnlySpan{T}"/> of booleans into a <see cref="byte" />.
/// </summary>
/// <param name="source">The span of booleans to pack.</param>
/// <returns>An 8-bit unsigned integer containing the packed booleans.</returns>
/// <exception cref="ArgumentException"><paramref name="source" /> contains more than 8 elements.</exception>
[Pure]
public static byte PackByte(this ReadOnlySpan<bool> source)
{
switch (source.Length)
{
case > 8: throw new ArgumentException("Source cannot contain more than 8 elements.", nameof(source));
case 8:
#if NETSTANDARD2_1
// TODO: Think of a way to do fast boolean correctness.
goto default;
#else
unsafe
{
ulong reinterpret;
// Boolean correctness.
if (Sse2.IsSupported)
{
fixed (bool* pSource = source)
{
var vec = Sse2.LoadScalarVector128((ulong*)pSource).AsByte();
var cmp = Sse2.CompareEqual(vec, Vector128<byte>.Zero);
var correctness = Sse2.AndNot(cmp, Vector128.Create((byte)1));
reinterpret = correctness.AsUInt64().GetElement(0);
}
}
else if (AdvSimd.IsSupported)
{
// Haven't tested since March 6th 2023 (Reason: Unavailable hardware).
fixed (bool* pSource = source)
{
var vec = AdvSimd.LoadVector64((byte*)pSource);
var cmp = AdvSimd.CompareEqual(vec, Vector64<byte>.Zero);
var correctness = AdvSimd.BitwiseSelect(cmp, vec, Vector64<byte>.Zero);
reinterpret = Unsafe.As<Vector64<byte>, ulong>(ref correctness);
}
}
else
{
goto default;
}
if (BitConverter.IsLittleEndian)
{
const ulong magic = 0b0000_0001_0000_0010_0000_0100_0000_1000_0001_0000_0010_0000_0100_0000_1000_0000;
return unchecked((byte)(magic * reinterpret >> 56));
}
else
{
// Haven't tested since March 6th 2023 (Reason: Unavailable hardware).
const ulong magic = 0b1000_0000_0100_0000_0010_0000_0001_0000_0000_1000_0000_0100_0000_0010_0000_0001;
return unchecked((byte)(magic * reinterpret >> 56));
}
}
#endif
default:
byte result = 0;
for (var i = 0; i < source.Length; i++)
{
result |= (byte)(source[i] ? 1 << i : 0);
}
return result;
}
}
} }

View File

@ -1,7 +1,6 @@
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Drawing; using System.Drawing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using X10D.Collections;
namespace X10D.Drawing; namespace X10D.Drawing;

View File

@ -133,7 +133,7 @@ public static class ComparableExtensions
if (lower.GreaterThan(upper)) if (lower.GreaterThan(upper))
{ {
throw new ArgumentException( throw new ArgumentException(
string.Format(null, ExceptionMessages.LowerCannotBeGreaterThanUpper, lower, upper), string.Format(CultureInfo.CurrentCulture, ExceptionMessages.LowerCannotBeGreaterThanUpper, lower, upper),
nameof(lower)); nameof(lower));
} }

View File

@ -37,7 +37,6 @@ public static class MemberInfoExtensions
throw new ArgumentNullException(nameof(member)); throw new ArgumentNullException(nameof(member));
} }
#endif #endif
return Attribute.IsDefined(member, typeof(T)); return Attribute.IsDefined(member, typeof(T));
} }