Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit b88a697

Browse files
committed
Faster CopyFrom
1 parent d9f6ac7 commit b88a697

File tree

8 files changed

+142
-68
lines changed

8 files changed

+142
-68
lines changed

src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ private Libuv.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize)
144144

145145
return handle.Libuv.buf_init(
146146
result.Pin() + result.End,
147-
result.Data.Offset + result.Data.Count - result.End);
147+
result.BlockEndOffset - result.End);
148148
}
149149

150150
private static void ReadCallback(UvStreamHandle handle, int status, object state)

src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public SocketInput(MemoryPool2 memory, IThreadPool threadPool)
4141
public MemoryPoolBlock2 IncomingStart()
4242
{
4343
const int minimumSize = 2048;
44-
45-
if (_tail != null && minimumSize <= _tail.Data.Offset + _tail.Data.Count - _tail.End)
44+
45+
if (_tail != null && minimumSize <= _tail.BlockEndOffset - _tail.End)
4646
{
4747
_pinned = _tail;
4848
}

src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ private static void BytesBetween(MemoryPoolIterator2 start, MemoryPoolIterator2
400400
return;
401401
}
402402

403-
bytes = start.Block.Data.Offset + start.Block.Data.Count - start.Index;
403+
bytes = start.Block.BlockEndOffset - start.Index;
404404
buffers = 1;
405405

406406
for (var block = start.Block.Next; block != end.Block; block = block.Next)

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ protected MemoryPoolBlock2()
5252
/// </summary>
5353
public byte[] Array => Data.Array;
5454

55+
/// <summary>
56+
/// Fixed end offset of the block
57+
/// </summary>
58+
public int BlockEndOffset { get; private set; }
59+
5560
/// <summary>
5661
/// The Start represents the offset into Array where the range of "active" bytes begins. At the point when the block is leased
5762
/// 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 @@ public static MemoryPoolBlock2 Create(
144149
Slab = slab,
145150
Start = data.Offset,
146151
End = data.Offset,
152+
BlockEndOffset = data.Offset + data.Count
147153
};
148154
}
149155

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs

Lines changed: 128 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -692,46 +692,52 @@ public void CopyFrom(ArraySegment<byte> buffer)
692692
CopyFrom(buffer.Array, buffer.Offset, buffer.Count);
693693
}
694694

695-
public void CopyFrom(byte[] data, int offset, int count)
695+
public unsafe void CopyFrom(byte[] data, int offset, int count)
696696
{
697697
Debug.Assert(_block != null);
698698
Debug.Assert(_block.Next == null);
699699
Debug.Assert(_block.End == _index);
700-
701-
var pool = _block.Pool;
700+
702701
var block = _block;
703702
var blockIndex = _index;
703+
var bytesLeftInBlock = block.BlockEndOffset - blockIndex;
704704

705-
var bufferIndex = offset;
706-
var remaining = count;
707-
var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex;
705+
if (bytesLeftInBlock >= count)
706+
{
707+
_index = blockIndex + count;
708+
Buffer.BlockCopy(data, offset, block.Array, blockIndex, count);
709+
block.End = _index;
710+
return;
711+
}
708712

709-
while (remaining > 0)
713+
do
710714
{
711715
if (bytesLeftInBlock == 0)
712716
{
713-
var nextBlock = pool.Lease();
714-
block.End = blockIndex;
717+
var nextBlock = block.Pool.Lease();
718+
blockIndex = nextBlock.Data.Offset;
719+
bytesLeftInBlock = nextBlock.Data.Count;
715720
block.Next = nextBlock;
716721
block = nextBlock;
717-
718-
blockIndex = block.Data.Offset;
719-
bytesLeftInBlock = block.Data.Count;
720722
}
721723

722-
var bytesToCopy = remaining < bytesLeftInBlock ? remaining : bytesLeftInBlock;
723-
724-
Buffer.BlockCopy(data, bufferIndex, block.Array, blockIndex, bytesToCopy);
725-
726-
blockIndex += bytesToCopy;
727-
bufferIndex += bytesToCopy;
728-
remaining -= bytesToCopy;
729-
bytesLeftInBlock -= bytesToCopy;
730-
}
731-
732-
block.End = blockIndex;
733-
_block = block;
734-
_index = blockIndex;
724+
if (count > bytesLeftInBlock)
725+
{
726+
count -= bytesLeftInBlock;
727+
Buffer.BlockCopy(data, offset, block.Array, blockIndex, bytesLeftInBlock);
728+
offset += bytesLeftInBlock;
729+
block.End = blockIndex + bytesLeftInBlock;
730+
bytesLeftInBlock = 0;
731+
}
732+
else
733+
{
734+
_index = blockIndex + count;
735+
Buffer.BlockCopy(data, offset, block.Array, blockIndex, count);
736+
block.End = _index;
737+
_block = block;
738+
return;
739+
}
740+
} while (true);
735741
}
736742

737743
public unsafe void CopyFromAscii(string data)
@@ -740,64 +746,126 @@ public unsafe void CopyFromAscii(string data)
740746
Debug.Assert(_block.Next == null);
741747
Debug.Assert(_block.End == _index);
742748

743-
var pool = _block.Pool;
744749
var block = _block;
745750
var blockIndex = _index;
746-
var length = data.Length;
751+
var count = data.Length;
747752

748-
var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex;
749-
var bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3;
753+
var blockRemaining = block.BlockEndOffset - blockIndex;
750754

751755
fixed (char* pData = data)
752756
{
753-
var input = pData;
754-
var inputEnd = pData + length;
755-
var inputEndMinusSpan = inputEnd - 3;
757+
if (blockRemaining >= count)
758+
{
759+
_index = blockIndex + count;
760+
761+
fixed (byte* pOutput = &block.Data.Array[blockIndex])
762+
{
763+
//this line is needed to allow output be an register var
764+
var output = pOutput;
765+
CopyFromAscii(pData, output, count);
766+
}
756767

757-
while (input < inputEnd)
768+
block.End = _index;
769+
return;
770+
}
771+
772+
var input = pData;
773+
do
758774
{
759-
if (bytesLeftInBlock == 0)
775+
if (blockRemaining == 0)
760776
{
761-
var nextBlock = pool.Lease();
762-
block.End = blockIndex;
777+
var nextBlock = block.Pool.Lease();
778+
blockIndex = nextBlock.Data.Offset;
779+
blockRemaining = nextBlock.Data.Count;
763780
block.Next = nextBlock;
764781
block = nextBlock;
765-
766-
blockIndex = block.Data.Offset;
767-
bytesLeftInBlock = block.Data.Count;
768-
bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3;
769782
}
770783

771-
fixed (byte* pOutput = &block.Data.Array[block.End])
784+
if (count > blockRemaining)
772785
{
773-
//this line is needed to allow output be an register var
774-
var output = pOutput;
786+
count -= blockRemaining;
775787

776-
var copied = 0;
777-
for (; input < inputEndMinusSpan && copied < bytesLeftInBlockMinusSpan; copied += 4)
788+
fixed (byte* pOutput = &block.Data.Array[blockIndex])
778789
{
779-
*(output) = (byte)*(input);
780-
*(output + 1) = (byte)*(input + 1);
781-
*(output + 2) = (byte)*(input + 2);
782-
*(output + 3) = (byte)*(input + 3);
783-
output += 4;
784-
input += 4;
790+
//this line is needed to allow output be an register var
791+
var output = pOutput;
792+
CopyFromAscii(input, output, blockRemaining);
785793
}
786-
for (; input < inputEnd && copied < bytesLeftInBlock; copied++)
794+
795+
block.End = blockIndex + blockRemaining;
796+
input += blockRemaining;
797+
blockRemaining = 0;
798+
}
799+
else
800+
{
801+
_index = blockIndex + count;
802+
803+
fixed (byte* pOutput = &block.Data.Array[blockIndex])
787804
{
788-
*(output++) = (byte)*(input++);
805+
//this line is needed to allow output be an register var
806+
var output = pOutput;
807+
CopyFromAscii(input, output, count);
789808
}
790809

791-
blockIndex += copied;
792-
bytesLeftInBlockMinusSpan -= copied;
793-
bytesLeftInBlock -= copied;
810+
block.End = _index;
811+
_block = block;
812+
return;
794813
}
795-
}
814+
} while (true);
796815
}
816+
}
797817

798-
block.End = blockIndex;
799-
_block = block;
800-
_index = blockIndex;
818+
private unsafe static void CopyFromAscii(char* input, byte* output, int count)
819+
{
820+
var i = 0;
821+
822+
while (i < count - 11)
823+
{
824+
i += 12;
825+
*(output) = (byte)*(input);
826+
*(output + 1) = (byte)*(input + 1);
827+
*(output + 2) = (byte)*(input + 2);
828+
*(output + 3) = (byte)*(input + 3);
829+
*(output + 4) = (byte)*(input + 4);
830+
*(output + 5) = (byte)*(input + 5);
831+
*(output + 6) = (byte)*(input + 6);
832+
*(output + 7) = (byte)*(input + 7);
833+
*(output + 8) = (byte)*(input + 8);
834+
*(output + 9) = (byte)*(input + 9);
835+
*(output + 10) = (byte)*(input + 10);
836+
*(output + 11) = (byte)*(input + 11);
837+
output += 12;
838+
input += 12;
839+
}
840+
if (i < count - 5)
841+
{
842+
i += 6;
843+
*(output) = (byte)*(input);
844+
*(output + 1) = (byte)*(input + 1);
845+
*(output + 2) = (byte)*(input + 2);
846+
*(output + 3) = (byte)*(input + 3);
847+
*(output + 4) = (byte)*(input + 4);
848+
*(output + 5) = (byte)*(input + 5);
849+
output += 6;
850+
input += 6;
851+
}
852+
if (i < count - 3)
853+
{
854+
i += 4;
855+
*(output) = (byte)*(input);
856+
*(output + 1) = (byte)*(input + 1);
857+
*(output + 2) = (byte)*(input + 2);
858+
*(output + 3) = (byte)*(input + 3);
859+
output += 4;
860+
input += 4;
861+
}
862+
while (i < count)
863+
{
864+
i++;
865+
*output = (byte)*input;
866+
output++;
867+
input++;
868+
}
801869
}
802870
}
803871
}

src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public unsafe void Write(
6666
for (var index = 0; index < nBuffers; index++)
6767
{
6868
var blockStart = block == start.Block ? start.Index : block.Data.Offset;
69-
var blockEnd = block == end.Block ? end.Index : block.Data.Offset + block.Data.Count;
69+
var blockEnd = block == end.Block ? end.Index : block.BlockEndOffset;
7070

7171
// create and pin each segment being written
7272
pBuffers[index] = Libuv.buf_init(

test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public void CopyFromCorrectlyTraversesBlocks()
248248

249249
Assert.Null(block1.Next);
250250

251-
end.CopyFrom(new ArraySegment<byte>(buffer));
251+
end.CopyFrom(buffer, 0, buffer.Length);
252252

253253
Assert.NotNull(block1.Next);
254254

test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,11 @@ public void ProducingStartAndProducingCompleteCanBeUsedDirectly()
308308

309309
// block 1
310310
var start = socketOutput.ProducingStart();
311-
start.Block.End = start.Block.Data.Offset + start.Block.Data.Count;
311+
start.Block.End = start.Block.BlockEndOffset;
312312

313313
// block 2
314314
var block2 = memory.Lease();
315-
block2.End = block2.Data.Offset + block2.Data.Count;
315+
block2.End = block2.BlockEndOffset;
316316
start.Block.Next = block2;
317317

318318
var end = new MemoryPoolIterator2(block2, block2.End);

0 commit comments

Comments
 (0)