Use PowerOfTwoToHighByte for FindFirstEqualByte
This commit is contained in:
parent
7bd027cb6b
commit
4f7977d440
|
|
@ -4,12 +4,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
{
|
{
|
||||||
public struct MemoryPoolIterator
|
public struct MemoryPoolIterator
|
||||||
{
|
{
|
||||||
|
private static readonly ulong _powerOfTwoToHighByte = PowerOfTwoToHighByte();
|
||||||
private static readonly int _vectorSpan = Vector<byte>.Count;
|
private static readonly int _vectorSpan = Vector<byte>.Count;
|
||||||
|
|
||||||
private MemoryPoolBlock _block;
|
private MemoryPoolBlock _block;
|
||||||
|
|
@ -307,7 +309,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
|
|
||||||
_block = block;
|
_block = block;
|
||||||
|
|
||||||
var firstEqualByteIndex = FindFirstEqualByte(ref byte0Equals);
|
var firstEqualByteIndex = LocateFirstFoundByte(ref byte0Equals);
|
||||||
var vectorBytesScanned = firstEqualByteIndex + 1;
|
var vectorBytesScanned = firstEqualByteIndex + 1;
|
||||||
|
|
||||||
if (bytesScanned + vectorBytesScanned > limit)
|
if (bytesScanned + vectorBytesScanned > limit)
|
||||||
|
|
@ -413,7 +415,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
|
|
||||||
_block = block;
|
_block = block;
|
||||||
|
|
||||||
var firstEqualByteIndex = FindFirstEqualByte(ref byte0Equals);
|
var firstEqualByteIndex = LocateFirstFoundByte(ref byte0Equals);
|
||||||
|
|
||||||
if (_block == limit.Block && index + firstEqualByteIndex > limit.Index)
|
if (_block == limit.Block && index + firstEqualByteIndex > limit.Index)
|
||||||
{
|
{
|
||||||
|
|
@ -512,11 +514,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
|
|
||||||
if (!byte0Equals.Equals(Vector<byte>.Zero))
|
if (!byte0Equals.Equals(Vector<byte>.Zero))
|
||||||
{
|
{
|
||||||
byte0Index = FindFirstEqualByte(ref byte0Equals);
|
byte0Index = LocateFirstFoundByte(ref byte0Equals);
|
||||||
}
|
}
|
||||||
if (!byte1Equals.Equals(Vector<byte>.Zero))
|
if (!byte1Equals.Equals(Vector<byte>.Zero))
|
||||||
{
|
{
|
||||||
byte1Index = FindFirstEqualByte(ref byte1Equals);
|
byte1Index = LocateFirstFoundByte(ref byte1Equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte0Index == int.MaxValue && byte1Index == int.MaxValue)
|
if (byte0Index == int.MaxValue && byte1Index == int.MaxValue)
|
||||||
|
|
@ -656,15 +658,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
|
|
||||||
if (!byte0Equals.Equals(Vector<byte>.Zero))
|
if (!byte0Equals.Equals(Vector<byte>.Zero))
|
||||||
{
|
{
|
||||||
byte0Index = FindFirstEqualByte(ref byte0Equals);
|
byte0Index = LocateFirstFoundByte(ref byte0Equals);
|
||||||
}
|
}
|
||||||
if (!byte1Equals.Equals(Vector<byte>.Zero))
|
if (!byte1Equals.Equals(Vector<byte>.Zero))
|
||||||
{
|
{
|
||||||
byte1Index = FindFirstEqualByte(ref byte1Equals);
|
byte1Index = LocateFirstFoundByte(ref byte1Equals);
|
||||||
}
|
}
|
||||||
if (!byte2Equals.Equals(Vector<byte>.Zero))
|
if (!byte2Equals.Equals(Vector<byte>.Zero))
|
||||||
{
|
{
|
||||||
byte2Index = FindFirstEqualByte(ref byte2Equals);
|
byte2Index = LocateFirstFoundByte(ref byte2Equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte0Index == int.MaxValue && byte1Index == int.MaxValue && byte2Index == int.MaxValue)
|
if (byte0Index == int.MaxValue && byte1Index == int.MaxValue && byte2Index == int.MaxValue)
|
||||||
|
|
@ -761,60 +763,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find first byte
|
/// Locate the first of the found bytes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="byteEquals"></param >
|
/// <param name="byteEquals"></param >
|
||||||
/// <returns>The first index of the result vector</returns>
|
/// <returns>The first index of the result vector</returns>
|
||||||
/// <exception cref="InvalidOperationException">byteEquals = 0</exception>
|
// Force inlining (64 IL bytes, 91 bytes asm) Issue: https://github.com/dotnet/coreclr/issues/7386
|
||||||
internal static int FindFirstEqualByte(ref Vector<byte> byteEquals)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static int LocateFirstFoundByte(ref Vector<byte> byteEquals)
|
||||||
{
|
{
|
||||||
if (!BitConverter.IsLittleEndian) return FindFirstEqualByteSlow(ref byteEquals);
|
|
||||||
|
|
||||||
// Quasi-tree search
|
|
||||||
var vector64 = Vector.AsVectorInt64(byteEquals);
|
var vector64 = Vector.AsVectorInt64(byteEquals);
|
||||||
for (var i = 0; i < Vector<long>.Count; i++)
|
var i = 0;
|
||||||
|
long longValue = 0;
|
||||||
|
for (; i < Vector<long>.Count; i++)
|
||||||
{
|
{
|
||||||
var longValue = vector64[i];
|
longValue = vector64[i];
|
||||||
if (longValue == 0) continue;
|
if (longValue == 0) continue;
|
||||||
|
break;
|
||||||
return (i << 3) +
|
|
||||||
((longValue & 0x00000000ffffffff) > 0
|
|
||||||
? (longValue & 0x000000000000ffff) > 0
|
|
||||||
? (longValue & 0x00000000000000ff) > 0 ? 0 : 1
|
|
||||||
: (longValue & 0x0000000000ff0000) > 0 ? 2 : 3
|
|
||||||
: (longValue & 0x0000ffff00000000) > 0
|
|
||||||
? (longValue & 0x000000ff00000000) > 0 ? 4 : 5
|
|
||||||
: (longValue & 0x00ff000000000000) > 0 ? 6 : 7);
|
|
||||||
}
|
}
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal for testing
|
// Flag least significant power of two bit
|
||||||
internal static int FindFirstEqualByteSlow(ref Vector<byte> byteEquals)
|
var powerOfTwoFlag = (ulong)(longValue & -longValue);
|
||||||
{
|
// Shift all powers of two into the high byte and extract
|
||||||
// Quasi-tree search
|
var foundByteIndex = (int)((powerOfTwoFlag * _powerOfTwoToHighByte) >> 61);
|
||||||
var vector64 = Vector.AsVectorInt64(byteEquals);
|
// Single LEA instruction with jitted const (using function result)
|
||||||
for (var i = 0; i < Vector<long>.Count; i++)
|
return i * 8 + foundByteIndex;
|
||||||
{
|
|
||||||
var longValue = vector64[i];
|
|
||||||
if (longValue == 0) continue;
|
|
||||||
|
|
||||||
var shift = i << 1;
|
|
||||||
var offset = shift << 2;
|
|
||||||
var vector32 = Vector.AsVectorInt32(byteEquals);
|
|
||||||
if (vector32[shift] != 0)
|
|
||||||
{
|
|
||||||
if (byteEquals[offset] != 0) return offset;
|
|
||||||
if (byteEquals[offset + 1] != 0) return offset + 1;
|
|
||||||
if (byteEquals[offset + 2] != 0) return offset + 2;
|
|
||||||
return offset + 3;
|
|
||||||
}
|
|
||||||
if (byteEquals[offset + 4] != 0) return offset + 4;
|
|
||||||
if (byteEquals[offset + 5] != 0) return offset + 5;
|
|
||||||
if (byteEquals[offset + 6] != 0) return offset + 6;
|
|
||||||
return offset + 7;
|
|
||||||
}
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1059,5 +1031,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
_block = block;
|
_block = block;
|
||||||
_index = blockIndex;
|
_index = blockIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ulong PowerOfTwoToHighByte()
|
||||||
|
{
|
||||||
|
return BitConverter.IsLittleEndian ? 0x20406080A0C0E0ul : 0xE0C0A080604020ul;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
for (int i = 0; i < Vector<byte>.Count; i++)
|
for (int i = 0; i < Vector<byte>.Count; i++)
|
||||||
{
|
{
|
||||||
Vector<byte> vector = new Vector<byte>(bytes);
|
Vector<byte> vector = new Vector<byte>(bytes);
|
||||||
Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByte(ref vector));
|
Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(ref vector));
|
||||||
bytes[i] = 0;
|
bytes[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,27 +40,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
bytes[i] = 1;
|
bytes[i] = 1;
|
||||||
Vector<byte> vector = new Vector<byte>(bytes);
|
Vector<byte> vector = new Vector<byte>(bytes);
|
||||||
Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByte(ref vector));
|
Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(ref vector));
|
||||||
bytes[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestFindFirstEqualByteSlow()
|
|
||||||
{
|
|
||||||
var bytes = Enumerable.Repeat<byte>(0xff, Vector<byte>.Count).ToArray();
|
|
||||||
for (int i = 0; i < Vector<byte>.Count; i++)
|
|
||||||
{
|
|
||||||
Vector<byte> vector = new Vector<byte>(bytes);
|
|
||||||
Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByteSlow(ref vector));
|
|
||||||
bytes[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Vector<byte>.Count; i++)
|
|
||||||
{
|
|
||||||
bytes[i] = 1;
|
|
||||||
Vector<byte> vector = new Vector<byte>(bytes);
|
|
||||||
Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByteSlow(ref vector));
|
|
||||||
bytes[i] = 0;
|
bytes[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue