-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Better defaults for the Pipe #35939
Better defaults for the Pipe #35939
Conversation
davidfowl
commented
Mar 11, 2019
- Use a 4K buffer instead of 2K (4K is a very common buffer size and usually maps to system page size)
- Use a stack for the buffer segment pool and allow pooling more than 16 segments for large writes (up to 256 max)
- Use a 4K buffer instead of 2K (4 is a very common buffer size and usually maps to system page size) - Use a stack for the buffer segment pool and allow pooling more than 16 segments for large writes
|
As an aside: A pattern useful to maintain to reduce syscalls for Pipe for file reading (that Stream avoids by defaulting to a very large buffer), is using ReadFileScatter on Windows and readv, preadv on Linux to write to multiple segments at once. This ends up being something like: GetMemory, Advance, GetMemory, Advance, GetMemory, Advance, GetMemory, ReadGather, FlushAsync Mentioning it as it doesn't work with the However, its needed to implement |
|
if it should be system page size why not use this as default? |
If you are using large pages then the page size can be 2MB to 256MB which would be an incredibility inefficient use of memory across multiple pipes. |
| private readonly PipeScheduler _writerScheduler; | ||
|
|
||
| private readonly BufferSegment[] _bufferSegmentPool; | ||
| private readonly Stack<BufferSegment> _bufferSegmentPool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for using the stack here? It's another allocation per pipe and it's 3 fields instead of 2 that we had before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can live with another field. Also we saved on one the other PR so it’s a trade
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But what are we gaining by spending a field and an allocation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearer code and a way to resize the initial buffer up to the max without writing that code manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's literally 3 lines of code:
corefx/src/System.Collections/src/System/Collections/Generic/Stack.cs
Lines 286 to 289 in 2a2d34d
| Array.Resize(ref _array, (_array.Length == 0) ? DefaultCapacity : 2 * _array.Length); | |
| _array[_size] = item; | |
| _version++; | |
| _size++; |
|
How big of an improvement does this give? What scenario is driving this change? |
|
Recently testing out benchmarks with JSON tokens bigger than 32K and also the fact that pipelines is exposed in ASP.NET Core means you can buffer more (and without the hard limit). This makes it so that we don’t start allocating segments until you reach a 1MB of buffered data by default. Also the 2K vs 4K by default felt like a better buffer size (we use it pretty much everywhere else). |
| { | ||
| internal const int SegmentPoolSize = 16; | ||
| internal const int InitialSegmentPoolSize = 16; // 65K | ||
| internal const int MaxPoolSize = 256; // 1MB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: MaxSegmentPoolSize.
- Use a 4K buffer instead of 2K (4K is a very common buffer size and usually maps to system page size) - Use a stack for the buffer segment pool and allow pooling more than 16 segments for large writes Commit migrated from dotnet/corefx@b49b512