mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-10 02:45:41 +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]
|
[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()
|
||||||
{
|
{
|
||||||
|
@ -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));
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user