-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Increase the default value of PipeOptions.MinimumSegmentSize #43480
Comments
Triage: Alternatively, we can add an API to pass a memory hint to |
We could do both. |
I don't really understand what we'd change the default to based on this since it's scenario specific. I do think we could change the defaults for CopyToAsync and have a way to pass in a sizehint for CopyToAsync and WriteAsync. |
Is that doing multiple smaller async reads from a stream rather than repeated smaller copies? e.g. if you read to a large chunk (65k); then copy that to the small 4k blocks what's the time difference? |
When experimenting with file copying on Windows, I tested that by repeatedly calling GetMemory()/Advance() without flushing every time in my flush-less branch. That only yielded an ~8% improvement. Then I created a cheat branch that went even further and Advanced prior to receiving data so it could do concurrent reads into smaller buffers. This was yielded a ~48% improvement which was equivalent to using a larger buffer (where larger buffer size = concurrent reads * smaller buffer size). But since you can only really read into one buffer at a time with a PipeWriter, that's not an option unless we add PipeWriter APIs. You can see all the branches I experimented with at https://github.com/halter73/PipeTest/branches. I've included my local perf measurements in my commit messages. |
Ah, Though it would cause by default allocations from the ArrayPool rather than the Memory pool for Kestrel (if the size is not specified)? |
If we do this, Kestrel likely wouldn't use the |
Thank you guys for discussing this issue. |
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
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.
runtime/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeOptions.cs
Line 14 in c788fe1
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.
Regression?
No.
Data
Before (4096)
After (655350)
The text was updated successfully, but these errors were encountered: