Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b01a16f

Browse files
committed
Avoid unnecessary clearing in CopyToAsync
With getting buffers from the ArrayPool, we want to clear the buffers before we put them back into the pool. But we only need to clear the max amount we used, and we're very likely to be using much larger buffers than we actually need. This change keeps track of how much space we used and only clears that much.
1 parent 3fe9860 commit b01a16f

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

src/Common/src/System/IO/StreamHelpers.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public static Task ArrayPoolCopyToAsync(Stream source, Stream destination, int b
7171
private static async Task ArrayPoolCopyToAsyncCore(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken)
7272
{
7373
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
74+
bufferSize = 0; // reuse same field for high water mark to avoid needing another field in the state machine
7475
try
7576
{
7677
while (true)
@@ -80,12 +81,17 @@ private static async Task ArrayPoolCopyToAsyncCore(Stream source, Stream destina
8081
{
8182
break;
8283
}
84+
if (bytesRead > bufferSize)
85+
{
86+
bufferSize = bytesRead;
87+
}
8388
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
8489
}
8590
}
8691
finally
8792
{
88-
ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
93+
Array.Clear(buffer, 0, bufferSize); // clear only the most we used
94+
ArrayPool<byte>.Shared.Return(buffer, clearArray: false);
8995
}
9096
}
9197
}

src/System.IO.FileSystem/src/System/IO/Win32FileStream.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,7 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc
17641764
// Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that
17651765
// we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool.
17661766
byte[] copyBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
1767+
bufferSize = 0; // repurpose bufferSize to be the high water mark for the buffer, to avoid an extra field in the state machine
17671768

17681769
// Allocate an Overlapped we can use repeatedly for all operations
17691770
var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer);
@@ -1859,14 +1860,23 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc
18591860
}
18601861

18611862
// Successful operation. If we got zero bytes, we're done: exit the read/write loop.
1862-
// Otherwise, update the read position for next time accordingly.
1863-
if (readAwaitable._numBytes == 0)
1863+
int numBytesRead = (int)readAwaitable._numBytes;
1864+
if (numBytesRead == 0)
18641865
{
18651866
break;
18661867
}
1867-
else if (canSeek)
1868+
1869+
// Otherwise, update the read position for next time accordingly.
1870+
if (canSeek)
18681871
{
1869-
readAwaitable._position += (int)readAwaitable._numBytes;
1872+
readAwaitable._position += numBytesRead;
1873+
}
1874+
1875+
// (and keep track of the maximum number of bytes in the buffer we used, to avoid excessive and unnecessary
1876+
// clearing of the buffer before we return it to the pool)
1877+
if (numBytesRead > bufferSize)
1878+
{
1879+
bufferSize = numBytesRead;
18701880
}
18711881
}
18721882
finally
@@ -1896,7 +1906,9 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc
18961906
// Cleanup from the whole copy operation
18971907
cancellationReg.Dispose();
18981908
awaitableOverlapped.Dispose();
1899-
ArrayPool<byte>.Shared.Return(copyBuffer, clearArray: true);
1909+
1910+
Array.Clear(copyBuffer, 0, bufferSize);
1911+
ArrayPool<byte>.Shared.Return(copyBuffer, clearArray: false);
19001912

19011913
// Make sure the stream's current position reflects where we ended up
19021914
if (!_handle.IsClosed && _parent.CanSeek)

0 commit comments

Comments
 (0)