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

Commit c830dc2

Browse files
committed
Use ArrayPool in FileStream.CopyToAsync
Rather than allocating a new buffer in CopyToAsync, take one from the shared array pool. This drastically reduces the memory allocation from a FileStream.CopyToAsync operation, and helps to improve throughput as a result. In one microbenchmark that repeatedly copied an 80K file with CopyToAsync's default args, throughput increased by 20% and gen0 GCs decreased by 100x.
1 parent 775331b commit c830dc2

File tree

4 files changed

+8
-3
lines changed

4 files changed

+8
-3
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Buffers;
56
using System.Diagnostics;
67
using System.Diagnostics.Contracts;
78
using System.Runtime.InteropServices;
@@ -1775,13 +1776,13 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc
17751776
readAwaitable._position = _pos;
17761777
}
17771778

1778-
// Create the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use
1779+
// Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use
17791780
// _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may
17801781
// actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically
17811782
// CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized.
17821783
// Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that
1783-
// we'd likely be unable to use it anyway. A better option than using _buffer would be a future pooling solution.
1784-
byte[] copyBuffer = new byte[bufferSize];
1784+
// we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool.
1785+
byte[] copyBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
17851786

17861787
// Allocate an Overlapped we can use repeatedly for all operations
17871788
var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer);
@@ -1914,6 +1915,7 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc
19141915
// Cleanup from the whole copy operation
19151916
cancellationReg.Dispose();
19161917
awaitableOverlapped.Dispose();
1918+
ArrayPool<byte>.Shared.Return(copyBuffer, clearArray: true);
19171919

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

src/System.IO.FileSystem/src/netcore50/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"dependencies": {
33
"Microsoft.NETCore.Platforms": "1.0.1",
4+
"System.Buffers": "4.0.0",
45
"System.Collections": "4.0.10",
56
"System.Diagnostics.Contracts": "4.0.0",
67
"System.Diagnostics.Debug": "4.0.10",

src/System.IO.FileSystem/src/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"netstandard1.3": {
44
"dependencies": {
55
"Microsoft.NETCore.Platforms": "1.0.1",
6+
"System.Buffers": "4.0.0",
67
"System.Collections": "4.0.10",
78
"System.Diagnostics.Contracts": "4.0.0",
89
"System.Diagnostics.Debug": "4.0.10",

src/System.IO.FileSystem/src/win/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"netstandard1.3": {
44
"dependencies": {
55
"Microsoft.NETCore.Platforms": "1.0.1",
6+
"System.Buffers": "4.0.0",
67
"System.Collections": "4.0.10",
78
"System.Diagnostics.Contracts": "4.0.0",
89
"System.Diagnostics.Debug": "4.0.10",

0 commit comments

Comments
 (0)