Skip to content
Permalink
Browse files

Better defaults for the Pipe (#35939)

- 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
  • Loading branch information...
davidfowl committed Mar 11, 2019
1 parent 6eedd6f commit b49b512a6288edd96d4fbb68bf23a4bd2f831545
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -16,7 +17,8 @@ namespace System.IO.Pipelines
/// </summary>
public sealed partial class Pipe
{
internal const int SegmentPoolSize = 16;
internal const int InitialSegmentPoolSize = 16; // 65K
internal const int MaxPoolSize = 256; // 1MB

private static readonly Action<object> s_signalReaderAwaitable = state => ((Pipe)state).ReaderCancellationRequested();
private static readonly Action<object> s_signalWriterAwaitable = state => ((Pipe)state).WriterCancellationRequested();
@@ -42,7 +44,7 @@ public sealed partial class Pipe
private readonly PipeScheduler _readerScheduler;
private readonly PipeScheduler _writerScheduler;

private readonly BufferSegment[] _bufferSegmentPool;
private readonly Stack<BufferSegment> _bufferSegmentPool;

private readonly DefaultPipeReader _reader;
private readonly DefaultPipeWriter _writer;
@@ -52,8 +54,6 @@ public sealed partial class Pipe
private long _length;
private long _currentWriteLength;

private int _pooledSegmentCount;

private PipeAwaitable _readerAwaitable;
private PipeAwaitable _writerAwaitable;

@@ -101,7 +101,7 @@ public Pipe(PipeOptions options)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.options);
}

_bufferSegmentPool = new BufferSegment[SegmentPoolSize];
_bufferSegmentPool = new Stack<BufferSegment>(InitialSegmentPoolSize);

_operationState = default;
_readerCompletion = default;
@@ -253,10 +253,9 @@ private int GetSegmentSize(int sizeHint, int maxBufferSize = int.MaxValue)

private BufferSegment CreateSegmentUnsynchronized()
{
if (_pooledSegmentCount > 0)
if (_bufferSegmentPool.Count > 0)
{
_pooledSegmentCount--;
return _bufferSegmentPool[_pooledSegmentCount];
return _bufferSegmentPool.Pop();
}

return new BufferSegment();
@@ -269,10 +268,9 @@ private void ReturnSegmentUnsynchronized(BufferSegment segment)
Debug.Assert(segment != _writingHead, "Returning _writingHead segment that's in use!");
Debug.Assert(segment != _lastExamined, "Returning _lastExamined segment that's in use!");

if (_pooledSegmentCount < _bufferSegmentPool.Length)
if (_bufferSegmentPool.Count < MaxPoolSize)
{
_bufferSegmentPool[_pooledSegmentCount] = segment;
_pooledSegmentCount++;
_bufferSegmentPool.Push(segment);
}
}

@@ -12,11 +12,11 @@ namespace System.IO.Pipelines
/// </summary>
public class PipeOptions
{
private const int DefaultMinimumSegmentSize = 2048;
private const int DefaultMinimumSegmentSize = 4096;

private const int DefaultResumeWriterThreshold = DefaultMinimumSegmentSize * Pipe.SegmentPoolSize / 2;
private const int DefaultResumeWriterThreshold = DefaultMinimumSegmentSize * Pipe.InitialSegmentPoolSize / 2;

private const int DefaultPauseWriterThreshold = DefaultMinimumSegmentSize * Pipe.SegmentPoolSize;
private const int DefaultPauseWriterThreshold = DefaultMinimumSegmentSize * Pipe.InitialSegmentPoolSize;

/// <summary>
/// Default instance of <see cref="PipeOptions"/>
@@ -12,13 +12,13 @@ public class PipeOptionsTests
[Fact]
public void DefaultPauseWriterThresholdIsSet()
{
Assert.Equal(32768, PipeOptions.Default.PauseWriterThreshold);
Assert.Equal(65536, PipeOptions.Default.PauseWriterThreshold);
}

[Fact]
public void DefaultResumeWriterThresholdIsSet()
{
Assert.Equal(16384, PipeOptions.Default.ResumeWriterThreshold);
Assert.Equal(32768, PipeOptions.Default.ResumeWriterThreshold);
}

[Fact]
@@ -206,7 +206,12 @@ public void ReturnsWriteHeadOnComplete()
public void ReturnsWriteHeadWhenRequestingLargerBlock()
{
var pool = new DisposeTrackingBufferPool();
var pipe = new Pipe(CreatePipeWithInlineSchedulers(pool));
var options = new PipeOptions(pool,
readerScheduler: PipeScheduler.Inline,
writerScheduler: PipeScheduler.Inline,
minimumSegmentSize: 2048);

var pipe = new Pipe(options);
pipe.Writer.GetMemory(512);
pipe.Writer.GetMemory(4096);

0 comments on commit b49b512

Please sign in to comment.
You can’t perform that action at this time.