-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
Copying data is a lot faster with larger buffers. And copying data is a large use case for System.IO.Pipelines. For example, in ASP.NET Core, we plan to use PipeWriter to write files to response bodies (dotnet/aspnetcore#24851).
@brporter got us looking at the performance of using Pipes to copy files, and this once again demonstrated how crucial large buffers are. This is why System.IO.Stream's DefaultCopyBufferSize is 81920 bytes.
runtime/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs
Lines 30 to 33 in c788fe1
| // We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K). | |
| // The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant | |
| // improvement in Copy performance. | |
| private const int DefaultCopyBufferSize = 81920; |
PipeOptions.DefaultMinimumSegmentSize is only 4096 bytes, and this leads to terrible performance when calling something like the default implementation of WriteAsync, CopyToAsync or anything that calls PipeWriter.GetMemory() or PipeWriter.GetSpan() without a sizeHint.
| private const int DefaultMinimumSegmentSize = 4096; |
In my testing, copying a 2GB file went from taking 8607ms to 1771ms by increasing PipeOptions.MinimumSegmentSize from 4096 bytes to 655350 bytes. Even with the in-between Stream.DefaultCopyBufferSize value of 81920 bytes the copy time dropped to 2458ms.
You
Configuration
You can find the benchmark app at https://github.com/halter73/PipeTest/blob/master/Program.cs.
F:\dev\halter73\PipeTest [master +3 ~1 -0 !]> dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.100-alpha.1.20472.11
Commit: e55929c5a5
Runtime Environment:
OS Name: Windows
OS Version: 10.0.20231
OS Platform: Windows
RID: win10-x64
Base Path: F:\dev\aspnet\AspNetCore\.dotnet\sdk\6.0.100-alpha.1.20472.11\
Host (useful for support):
Version: 6.0.0-alpha.1.20507.4
Commit: 4fef87c65e
.NET SDKs installed:
6.0.100-alpha.1.20472.11 [F:\dev\aspnet\AspNetCore\.dotnet\sdk]
.NET runtimes installed:
Microsoft.NETCore.App 6.0.0-alpha.1.20468.7 [F:\dev\aspnet\AspNetCore\.dotnet\shared\Microsoft.NETCore.App]
Regression?
No.
Data
Before (4096)
F:\dev\halter73\PipeTest [slice +3 ~1 -0 !]> dotnet run .\test.in .\test.out PipelinesSE
Copying .\test.in to .\test.out with method PipelinesSE...done!
GetTotalAllocatedBytes(true): [117,722,376] bytes
GetTotalMemory(false): [3,353,520] bytes
Executed for 8607ms.
After (655350)
F:\dev\halter73\PipeTest [master +3 ~1 -0 !]> dotnet run .\test.in .\test.out PipelinesSE
Copying .\test.in to .\test.out with method PipelinesSE...done!
GetTotalAllocatedBytes(true): [1,672,544] bytes
GetTotalMemory(false): [1,718,512] bytes
Executed for 1771ms.