Faster CopyFrom
This commit is contained in:
parent
d9f6ac70a0
commit
caaf9d473b
|
|
@ -144,7 +144,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
return handle.Libuv.buf_init(
|
return handle.Libuv.buf_init(
|
||||||
result.Pin() + result.End,
|
result.Pin() + result.End,
|
||||||
result.Data.Offset + result.Data.Count - result.End);
|
result.BlockEndOffset - result.End);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReadCallback(UvStreamHandle handle, int status, object state)
|
private static void ReadCallback(UvStreamHandle handle, int status, object state)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
const int minimumSize = 2048;
|
const int minimumSize = 2048;
|
||||||
|
|
||||||
if (_tail != null && minimumSize <= _tail.Data.Offset + _tail.Data.Count - _tail.End)
|
if (_tail != null && minimumSize <= _tail.BlockEndOffset - _tail.End)
|
||||||
{
|
{
|
||||||
_pinned = _tail;
|
_pinned = _tail;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes = start.Block.Data.Offset + start.Block.Data.Count - start.Index;
|
bytes = start.Block.BlockEndOffset - start.Index;
|
||||||
buffers = 1;
|
buffers = 1;
|
||||||
|
|
||||||
for (var block = start.Block.Next; block != end.Block; block = block.Next)
|
for (var block = start.Block.Next; block != end.Block; block = block.Next)
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Array => Data.Array;
|
public byte[] Array => Data.Array;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fixed end offset of the block
|
||||||
|
/// </summary>
|
||||||
|
public int BlockEndOffset { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Start represents the offset into Array where the range of "active" bytes begins. At the point when the block is leased
|
/// The Start represents the offset into Array where the range of "active" bytes begins. At the point when the block is leased
|
||||||
/// the Start is guaranteed to be equal to Array.Offset. The value of Start may be assigned anywhere between Data.Offset and
|
/// the Start is guaranteed to be equal to Array.Offset. The value of Start may be assigned anywhere between Data.Offset and
|
||||||
|
|
@ -144,6 +149,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
|
||||||
Slab = slab,
|
Slab = slab,
|
||||||
Start = data.Offset,
|
Start = data.Offset,
|
||||||
End = data.Offset,
|
End = data.Offset,
|
||||||
|
BlockEndOffset = data.Offset + data.Count
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -692,46 +692,52 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
|
||||||
CopyFrom(buffer.Array, buffer.Offset, buffer.Count);
|
CopyFrom(buffer.Array, buffer.Offset, buffer.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyFrom(byte[] data, int offset, int count)
|
public unsafe void CopyFrom(byte[] data, int offset, int count)
|
||||||
{
|
{
|
||||||
Debug.Assert(_block != null);
|
Debug.Assert(_block != null);
|
||||||
Debug.Assert(_block.Next == null);
|
Debug.Assert(_block.Next == null);
|
||||||
Debug.Assert(_block.End == _index);
|
Debug.Assert(_block.End == _index);
|
||||||
|
|
||||||
var pool = _block.Pool;
|
|
||||||
var block = _block;
|
var block = _block;
|
||||||
var blockIndex = _index;
|
var blockIndex = _index;
|
||||||
|
var bytesLeftInBlock = block.BlockEndOffset - blockIndex;
|
||||||
|
|
||||||
var bufferIndex = offset;
|
if (bytesLeftInBlock >= count)
|
||||||
var remaining = count;
|
{
|
||||||
var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex;
|
_index = blockIndex + count;
|
||||||
|
Buffer.BlockCopy(data, offset, block.Array, blockIndex, count);
|
||||||
|
block.End = _index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (remaining > 0)
|
do
|
||||||
{
|
{
|
||||||
if (bytesLeftInBlock == 0)
|
if (bytesLeftInBlock == 0)
|
||||||
{
|
{
|
||||||
var nextBlock = pool.Lease();
|
var nextBlock = block.Pool.Lease();
|
||||||
block.End = blockIndex;
|
blockIndex = nextBlock.Data.Offset;
|
||||||
|
bytesLeftInBlock = nextBlock.Data.Count;
|
||||||
block.Next = nextBlock;
|
block.Next = nextBlock;
|
||||||
block = nextBlock;
|
block = nextBlock;
|
||||||
|
|
||||||
blockIndex = block.Data.Offset;
|
|
||||||
bytesLeftInBlock = block.Data.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytesToCopy = remaining < bytesLeftInBlock ? remaining : bytesLeftInBlock;
|
if (count > bytesLeftInBlock)
|
||||||
|
{
|
||||||
Buffer.BlockCopy(data, bufferIndex, block.Array, blockIndex, bytesToCopy);
|
count -= bytesLeftInBlock;
|
||||||
|
Buffer.BlockCopy(data, offset, block.Array, blockIndex, bytesLeftInBlock);
|
||||||
blockIndex += bytesToCopy;
|
offset += bytesLeftInBlock;
|
||||||
bufferIndex += bytesToCopy;
|
block.End = blockIndex + bytesLeftInBlock;
|
||||||
remaining -= bytesToCopy;
|
bytesLeftInBlock = 0;
|
||||||
bytesLeftInBlock -= bytesToCopy;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
block.End = blockIndex;
|
_index = blockIndex + count;
|
||||||
_block = block;
|
Buffer.BlockCopy(data, offset, block.Array, blockIndex, count);
|
||||||
_index = blockIndex;
|
block.End = _index;
|
||||||
|
_block = block;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void CopyFromAscii(string data)
|
public unsafe void CopyFromAscii(string data)
|
||||||
|
|
@ -740,64 +746,123 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
|
||||||
Debug.Assert(_block.Next == null);
|
Debug.Assert(_block.Next == null);
|
||||||
Debug.Assert(_block.End == _index);
|
Debug.Assert(_block.End == _index);
|
||||||
|
|
||||||
var pool = _block.Pool;
|
|
||||||
var block = _block;
|
var block = _block;
|
||||||
var blockIndex = _index;
|
var blockIndex = _index;
|
||||||
var length = data.Length;
|
var count = data.Length;
|
||||||
|
|
||||||
var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex;
|
var blockRemaining = block.BlockEndOffset - blockIndex;
|
||||||
var bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3;
|
|
||||||
|
|
||||||
fixed (char* pData = data)
|
fixed (char* pInput = data)
|
||||||
{
|
{
|
||||||
var input = pData;
|
if (blockRemaining >= count)
|
||||||
var inputEnd = pData + length;
|
|
||||||
var inputEndMinusSpan = inputEnd - 3;
|
|
||||||
|
|
||||||
while (input < inputEnd)
|
|
||||||
{
|
{
|
||||||
if (bytesLeftInBlock == 0)
|
_index = blockIndex + count;
|
||||||
|
|
||||||
|
fixed (byte* pOutput = &block.Data.Array[blockIndex])
|
||||||
{
|
{
|
||||||
var nextBlock = pool.Lease();
|
CopyFromAscii(pInput, pOutput, count);
|
||||||
block.End = blockIndex;
|
}
|
||||||
|
|
||||||
|
block.End = _index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var input = pInput;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (blockRemaining == 0)
|
||||||
|
{
|
||||||
|
var nextBlock = block.Pool.Lease();
|
||||||
|
blockIndex = nextBlock.Data.Offset;
|
||||||
|
blockRemaining = nextBlock.Data.Count;
|
||||||
block.Next = nextBlock;
|
block.Next = nextBlock;
|
||||||
block = nextBlock;
|
block = nextBlock;
|
||||||
|
|
||||||
blockIndex = block.Data.Offset;
|
|
||||||
bytesLeftInBlock = block.Data.Count;
|
|
||||||
bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* pOutput = &block.Data.Array[block.End])
|
if (count > blockRemaining)
|
||||||
{
|
{
|
||||||
//this line is needed to allow output be an register var
|
count -= blockRemaining;
|
||||||
var output = pOutput;
|
|
||||||
|
|
||||||
var copied = 0;
|
fixed (byte* pOutput = &block.Data.Array[blockIndex])
|
||||||
for (; input < inputEndMinusSpan && copied < bytesLeftInBlockMinusSpan; copied += 4)
|
|
||||||
{
|
{
|
||||||
*(output) = (byte)*(input);
|
CopyFromAscii(input, pOutput, blockRemaining);
|
||||||
*(output + 1) = (byte)*(input + 1);
|
|
||||||
*(output + 2) = (byte)*(input + 2);
|
|
||||||
*(output + 3) = (byte)*(input + 3);
|
|
||||||
output += 4;
|
|
||||||
input += 4;
|
|
||||||
}
|
|
||||||
for (; input < inputEnd && copied < bytesLeftInBlock; copied++)
|
|
||||||
{
|
|
||||||
*(output++) = (byte)*(input++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockIndex += copied;
|
block.End = blockIndex + blockRemaining;
|
||||||
bytesLeftInBlockMinusSpan -= copied;
|
input += blockRemaining;
|
||||||
bytesLeftInBlock -= copied;
|
blockRemaining = 0;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
_index = blockIndex + count;
|
||||||
|
|
||||||
block.End = blockIndex;
|
fixed (byte* pOutput = &block.Data.Array[blockIndex])
|
||||||
_block = block;
|
{
|
||||||
_index = blockIndex;
|
CopyFromAscii(input, pOutput, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.End = _index;
|
||||||
|
_block = block;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static void CopyFromAscii(char* pInput, byte* pOutput, int count)
|
||||||
|
{
|
||||||
|
var i = 0;
|
||||||
|
//these line is needed to allow input/output be an register var
|
||||||
|
var input = pInput;
|
||||||
|
var output = pOutput;
|
||||||
|
|
||||||
|
while (i < count - 11)
|
||||||
|
{
|
||||||
|
i += 12;
|
||||||
|
*(output) = (byte)*(input);
|
||||||
|
*(output + 1) = (byte)*(input + 1);
|
||||||
|
*(output + 2) = (byte)*(input + 2);
|
||||||
|
*(output + 3) = (byte)*(input + 3);
|
||||||
|
*(output + 4) = (byte)*(input + 4);
|
||||||
|
*(output + 5) = (byte)*(input + 5);
|
||||||
|
*(output + 6) = (byte)*(input + 6);
|
||||||
|
*(output + 7) = (byte)*(input + 7);
|
||||||
|
*(output + 8) = (byte)*(input + 8);
|
||||||
|
*(output + 9) = (byte)*(input + 9);
|
||||||
|
*(output + 10) = (byte)*(input + 10);
|
||||||
|
*(output + 11) = (byte)*(input + 11);
|
||||||
|
output += 12;
|
||||||
|
input += 12;
|
||||||
|
}
|
||||||
|
if (i < count - 5)
|
||||||
|
{
|
||||||
|
i += 6;
|
||||||
|
*(output) = (byte)*(input);
|
||||||
|
*(output + 1) = (byte)*(input + 1);
|
||||||
|
*(output + 2) = (byte)*(input + 2);
|
||||||
|
*(output + 3) = (byte)*(input + 3);
|
||||||
|
*(output + 4) = (byte)*(input + 4);
|
||||||
|
*(output + 5) = (byte)*(input + 5);
|
||||||
|
output += 6;
|
||||||
|
input += 6;
|
||||||
|
}
|
||||||
|
if (i < count - 3)
|
||||||
|
{
|
||||||
|
i += 4;
|
||||||
|
*(output) = (byte)*(input);
|
||||||
|
*(output + 1) = (byte)*(input + 1);
|
||||||
|
*(output + 2) = (byte)*(input + 2);
|
||||||
|
*(output + 3) = (byte)*(input + 3);
|
||||||
|
output += 4;
|
||||||
|
input += 4;
|
||||||
|
}
|
||||||
|
while (i < count)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
*output = (byte)*input;
|
||||||
|
output++;
|
||||||
|
input++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
for (var index = 0; index < nBuffers; index++)
|
for (var index = 0; index < nBuffers; index++)
|
||||||
{
|
{
|
||||||
var blockStart = block == start.Block ? start.Index : block.Data.Offset;
|
var blockStart = block == start.Block ? start.Index : block.Data.Offset;
|
||||||
var blockEnd = block == end.Block ? end.Index : block.Data.Offset + block.Data.Count;
|
var blockEnd = block == end.Block ? end.Index : block.BlockEndOffset;
|
||||||
|
|
||||||
// create and pin each segment being written
|
// create and pin each segment being written
|
||||||
pBuffers[index] = Libuv.buf_init(
|
pBuffers[index] = Libuv.buf_init(
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
|
||||||
Assert.Null(block1.Next);
|
Assert.Null(block1.Next);
|
||||||
|
|
||||||
end.CopyFrom(new ArraySegment<byte>(buffer));
|
end.CopyFrom(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
Assert.NotNull(block1.Next);
|
Assert.NotNull(block1.Next);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -308,11 +308,11 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
|
||||||
// block 1
|
// block 1
|
||||||
var start = socketOutput.ProducingStart();
|
var start = socketOutput.ProducingStart();
|
||||||
start.Block.End = start.Block.Data.Offset + start.Block.Data.Count;
|
start.Block.End = start.Block.BlockEndOffset;
|
||||||
|
|
||||||
// block 2
|
// block 2
|
||||||
var block2 = memory.Lease();
|
var block2 = memory.Lease();
|
||||||
block2.End = block2.Data.Offset + block2.Data.Count;
|
block2.End = block2.BlockEndOffset;
|
||||||
start.Block.Next = block2;
|
start.Block.Next = block2;
|
||||||
|
|
||||||
var end = new MemoryPoolIterator2(block2, block2.End);
|
var end = new MemoryPoolIterator2(block2, block2.End);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue