1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 17:44:48 +09:00

Improve Ascii.FromUtf16 and FromHexString (#102735)

* Use cheaper Vector.Narrow altenatives in Ascii

* Use existing Ascii helper in HexConverter
This commit is contained in:
Miha Zupan 2024-05-29 10:42:00 -07:00 committed by GitHub
parent 0fe2e9a6c5
commit 5c74f63871
Signed by: github
GPG key ID: B5690EEEBB952194
2 changed files with 32 additions and 10 deletions

View file

@ -9,6 +9,7 @@ using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using System.Text;
using System.Text.Unicode;
#endif
@ -261,7 +262,7 @@ namespace System
// single UTF8 ASCII vector - the implementation can be shared with UTF8 paths.
Vector128<ushort> vec1 = Vector128.LoadUnsafe(ref srcRef, offset);
Vector128<ushort> vec2 = Vector128.LoadUnsafe(ref srcRef, offset + (nuint)Vector128<ushort>.Count);
Vector128<byte> vec = Vector128.Narrow(vec1, vec2);
Vector128<byte> vec = Ascii.ExtractAsciiVector(vec1, vec2);
// Based on "Algorithm #3" https://github.com/WojciechMula/toys/blob/master/simd-parse-hex/geoff_algorithm.cpp
// by Geoff Langdale and Wojciech Mula

View file

@ -6,6 +6,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.Wasm;
using System.Runtime.Intrinsics.X86;
namespace System.Text
@ -1651,7 +1652,7 @@ namespace System.Text
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<byte> ExtractAsciiVector(Vector128<ushort> vectorFirst, Vector128<ushort> vectorSecond)
internal static Vector128<byte> ExtractAsciiVector(Vector128<ushort> vectorFirst, Vector128<ushort> vectorSecond)
{
// Narrows two vectors of words [ w7 w6 w5 w4 w3 w2 w1 w0 ] and [ w7' w6' w5' w4' w3' w2' w1' w0' ]
// to a vector of bytes [ b7 ... b0 b7' ... b0'].
@ -1665,12 +1666,32 @@ namespace System.Text
{
return AdvSimd.Arm64.UnzipEven(vectorFirst.AsByte(), vectorSecond.AsByte());
}
else if (PackedSimd.IsSupported)
{
return PackedSimd.ConvertNarrowingSaturateUnsigned(vectorFirst.AsInt16(), vectorSecond.AsInt16());
}
else
{
return Vector128.Narrow(vectorFirst, vectorSecond);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<byte> ExtractAsciiVector(Vector256<ushort> vectorFirst, Vector256<ushort> vectorSecond)
{
return Avx2.IsSupported
? PackedSpanHelpers.FixUpPackedVector256Result(Avx2.PackUnsignedSaturate(vectorFirst.AsInt16(), vectorSecond.AsInt16()))
: Vector256.Narrow(vectorFirst, vectorSecond);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<byte> ExtractAsciiVector(Vector512<ushort> vectorFirst, Vector512<ushort> vectorSecond)
{
return Avx512BW.IsSupported
? PackedSpanHelpers.FixUpPackedVector512Result(Avx512BW.PackUnsignedSaturate(vectorFirst.AsInt16(), vectorSecond.AsInt16()))
: Vector512.Narrow(vectorFirst, vectorSecond);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe nuint NarrowUtf16ToAscii_Intrinsified(char* pUtf16Buffer, byte* pAsciiBuffer, nuint elementCount)
{
@ -1821,7 +1842,7 @@ namespace System.Text
// Turn the 16 ASCII chars we just read into 16 ASCII bytes, then copy it to the destination.
ref byte asciiBuffer = ref *pAsciiBuffer;
Vector256<byte> asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorFirst);
Vector256<byte> asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst);
asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, 0);
nuint currentOffsetInElements = Vector256.Size / 2; // we processed 16 elements so far
@ -1847,7 +1868,7 @@ namespace System.Text
}
// Turn the 16 ASCII chars we just read into 16 ASCII bytes, then copy it to the destination.
asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorFirst);
asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst);
asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements);
}
@ -1877,7 +1898,7 @@ namespace System.Text
// Build up the ASCII vector and perform the store.
Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector256.Size == 0, "Write should be aligned.");
asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorSecond);
asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorSecond);
asciiVector.StoreUnsafe(ref asciiBuffer, currentOffsetInElements);
currentOffsetInElements += Vector256.Size;
@ -1900,7 +1921,7 @@ namespace System.Text
// First part was all ASCII, narrow and aligned write. Note we're only filling in the low half of the vector.
Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector128.Size == 0, "Destination should be 128-bit-aligned.");
asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorFirst);
asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst);
asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements);
currentOffsetInElements += Vector256.Size / 2;
@ -1938,7 +1959,7 @@ namespace System.Text
// Turn the 32 ASCII chars we just read into 32 ASCII bytes, then copy it to the destination.
ref byte asciiBuffer = ref *pAsciiBuffer;
Vector512<byte> asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorFirst);
Vector512<byte> asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst);
asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, 0); // how to store the lower part of a avx512
nuint currentOffsetInElements = Vector512.Size / 2; // we processed 32 elements so far
@ -1965,7 +1986,7 @@ namespace System.Text
}
// Turn the 32 ASCII chars we just read into 32 ASCII bytes, then copy it to the destination.
asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorFirst);
asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst);
asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements);
}
@ -1995,7 +2016,7 @@ namespace System.Text
// Build up the ASCII vector and perform the store.
Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector512.Size == 0, "Write should be aligned.");
asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorSecond);
asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorSecond);
asciiVector.StoreUnsafe(ref asciiBuffer, currentOffsetInElements);
currentOffsetInElements += Vector512.Size;
@ -2018,7 +2039,7 @@ namespace System.Text
// First part was all ASCII, narrow and aligned write. Note we're only filling in the low half of the vector.
Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector256.Size == 0, "Destination should be 256-bit-aligned.");
asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorFirst);
asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst);
asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements);
currentOffsetInElements += Vector512.Size / 2;