mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-09 23:45:42 +00:00
Code fixup, fast Int8 packing
This commit is contained in:
parent
a808cab37f
commit
5e4044f965
@ -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()
|
||||
{
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
@ -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 one = Vector128.Create((byte)0x01);
|
||||
|
||||
|
||||
var vec = Vector128.Create(value).AsByte();
|
||||
var shuffle = Ssse3.Shuffle(vec, mask1Lo);
|
||||
var and = Sse2.AndNot(shuffle, mask2);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using X10D.Collections;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@ public static class MemberInfoExtensions
|
||||
throw new ArgumentNullException(nameof(member));
|
||||
}
|
||||
#endif
|
||||
|
||||
return Attribute.IsDefined(member, typeof(T));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user