Use PowerOfTwoToHighByte for FindFirstEqualByte

This commit is contained in:
Ben Adams 2016-10-03 07:58:24 +01:00
parent 7bd027cb6b
commit 4f7977d440
2 changed files with 31 additions and 74 deletions

View File

@ -4,12 +4,14 @@
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
{
public struct MemoryPoolIterator
{
private static readonly ulong _powerOfTwoToHighByte = PowerOfTwoToHighByte();
private static readonly int _vectorSpan = Vector<byte>.Count;
private MemoryPoolBlock _block;
@ -307,7 +309,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
_block = block;
var firstEqualByteIndex = FindFirstEqualByte(ref byte0Equals);
var firstEqualByteIndex = LocateFirstFoundByte(ref byte0Equals);
var vectorBytesScanned = firstEqualByteIndex + 1;
if (bytesScanned + vectorBytesScanned > limit)
@ -413,7 +415,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
_block = block;
var firstEqualByteIndex = FindFirstEqualByte(ref byte0Equals);
var firstEqualByteIndex = LocateFirstFoundByte(ref byte0Equals);
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))
{
byte0Index = FindFirstEqualByte(ref byte0Equals);
byte0Index = LocateFirstFoundByte(ref byte0Equals);
}
if (!byte1Equals.Equals(Vector<byte>.Zero))
{
byte1Index = FindFirstEqualByte(ref byte1Equals);
byte1Index = LocateFirstFoundByte(ref byte1Equals);
}
if (byte0Index == int.MaxValue && byte1Index == int.MaxValue)
@ -656,15 +658,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
if (!byte0Equals.Equals(Vector<byte>.Zero))
{
byte0Index = FindFirstEqualByte(ref byte0Equals);
byte0Index = LocateFirstFoundByte(ref byte0Equals);
}
if (!byte1Equals.Equals(Vector<byte>.Zero))
{
byte1Index = FindFirstEqualByte(ref byte1Equals);
byte1Index = LocateFirstFoundByte(ref byte1Equals);
}
if (!byte2Equals.Equals(Vector<byte>.Zero))
{
byte2Index = FindFirstEqualByte(ref byte2Equals);
byte2Index = LocateFirstFoundByte(ref byte2Equals);
}
if (byte0Index == int.MaxValue && byte1Index == int.MaxValue && byte2Index == int.MaxValue)
@ -761,60 +763,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
/// <summary>
/// Find first byte
/// Locate the first of the found bytes
/// </summary>
/// <param name="byteEquals"></param >
/// <returns>The first index of the result vector</returns>
/// <exception cref="InvalidOperationException">byteEquals = 0</exception>
internal static int FindFirstEqualByte(ref Vector<byte> byteEquals)
// Force inlining (64 IL bytes, 91 bytes asm) Issue: https://github.com/dotnet/coreclr/issues/7386
[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);
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;
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);
break;
}
throw new InvalidOperationException();
}
// Internal for testing
internal static int FindFirstEqualByteSlow(ref Vector<byte> byteEquals)
{
// Quasi-tree search
var vector64 = Vector.AsVectorInt64(byteEquals);
for (var i = 0; i < Vector<long>.Count; i++)
{
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();
// Flag least significant power of two bit
var powerOfTwoFlag = (ulong)(longValue & -longValue);
// Shift all powers of two into the high byte and extract
var foundByteIndex = (int)((powerOfTwoFlag * _powerOfTwoToHighByte) >> 61);
// Single LEA instruction with jitted const (using function result)
return i * 8 + foundByteIndex;
}
/// <summary>
@ -1059,5 +1031,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
_block = block;
_index = blockIndex;
}
private static ulong PowerOfTwoToHighByte()
{
return BitConverter.IsLittleEndian ? 0x20406080A0C0E0ul : 0xE0C0A080604020ul;
}
}
}

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
for (int i = 0; i < Vector<byte>.Count; i++)
{
Vector<byte> vector = new Vector<byte>(bytes);
Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByte(ref vector));
Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(ref vector));
bytes[i] = 0;
}
@ -40,27 +40,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
bytes[i] = 1;
Vector<byte> vector = new Vector<byte>(bytes);
Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByte(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));
Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(ref vector));
bytes[i] = 0;
}
}