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]
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]
public void Pack8Bit_Should_Pack_Correctly()
{

View File

@ -22,7 +22,7 @@ public static class ByteExtensions
[Pure]
public static bool[] Unpack(this byte value)
{
bool[] buffer = new bool[Size];
var buffer = new bool[Size];
value.Unpack(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
);
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 shuffle = Ssse3.Shuffle(vec, mask1Lo);
var shuffle = Ssse3.Shuffle(vec, mask1);
var and = Sse2.AndNot(shuffle, mask2);
var cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero);
var correctness = Sse2.And(cmp, Vector128.Create((byte)0x01));

View File

@ -66,12 +66,12 @@ public static class DictionaryExtensions
return value;
#else
if (dictionary.TryGetValue(key, out var old))
if (dictionary.TryGetValue(key, out TValue? old))
{
var newValue = updateValueFactory(key, old);
dictionary[key] = newValue;
var updated = updateValueFactory(key, old);
dictionary[key] = updated;
return newValue;
return updated;
}
else
{
@ -124,12 +124,12 @@ public static class DictionaryExtensions
}
#endif
if (dictionary.TryGetValue(key, out var old))
if (dictionary.TryGetValue(key, out TValue? old))
{
var newValue = updateValueFactory(key, old);
dictionary[key] = newValue;
var updated = updateValueFactory(key, old);
dictionary[key] = updated;
return newValue;
return updated;
}
else
{
@ -192,7 +192,7 @@ public static class DictionaryExtensions
#endif
#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)
{
value = updateValueFactory(key, value!);
@ -204,12 +204,12 @@ public static class DictionaryExtensions
return value;
#else
if (dictionary.TryGetValue(key, out var old))
if (dictionary.TryGetValue(key, out TValue? old))
{
var update = updateValueFactory(key, old);
dictionary[key] = update;
var updated = updateValueFactory(key, old);
dictionary[key] = updated;
return update;
return updated;
}
else
{
@ -274,12 +274,12 @@ public static class DictionaryExtensions
}
#endif
if (dictionary.TryGetValue(key, out var old))
if (dictionary.TryGetValue(key, out TValue? old))
{
var update = updateValueFactory(key, old);
dictionary[key] = update;
var updated = updateValueFactory(key, old);
dictionary[key] = updated;
return update;
return updated;
}
else
{
@ -350,7 +350,7 @@ public static class DictionaryExtensions
#endif
#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)
{
value = updateValueFactory(key, value!, factoryArgument);
@ -362,12 +362,12 @@ public static class DictionaryExtensions
return value;
#else
if (dictionary.TryGetValue(key, out var old))
if (dictionary.TryGetValue(key, out TValue? old))
{
var update = updateValueFactory(key, old, factoryArgument);
dictionary[key] = update;
var updated = updateValueFactory(key, old, factoryArgument);
dictionary[key] = updated;
return update;
return updated;
}
else
{
@ -438,12 +438,12 @@ public static class DictionaryExtensions
}
#endif
if (dictionary.TryGetValue(key, out var old))
if (dictionary.TryGetValue(key, out TValue? old))
{
var update = updateValueFactory(key, old, factoryArgument);
dictionary[key] = update;
var updated = updateValueFactory(key, old, factoryArgument);
dictionary[key] = updated;
return update;
return updated;
}
else
{

View File

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

View File

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

View File

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

View File

@ -2,6 +2,12 @@
using System.Runtime.CompilerServices;
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;
/// <summary>
@ -100,4 +106,93 @@ public static class SpanExtensions
return false;
#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.Drawing;
using System.Runtime.CompilerServices;
using X10D.Collections;
namespace X10D.Drawing;

View File

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

View File

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