From f52a49d5f7c3d0fd084c7f8384b52b8c941a162d Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Wed, 13 Jan 2016 10:32:10 -0800 Subject: [PATCH] Adding Buffer Pooling to System.IO --- src/System.IO/src/System.IO.csproj | 7 +++- src/System.IO/src/System/IO/BinaryReader.cs | 32 +++++++++++++------ src/System.IO/src/System/IO/BinaryWriter.cs | 21 +++++++++--- src/System.IO/src/System/IO/BufferedStream.cs | 16 +++++++--- src/System.IO/src/System/IO/Stream.cs | 31 +++++++++++++----- src/System.IO/src/project.json | 4 ++- 6 files changed, 83 insertions(+), 28 deletions(-) diff --git a/src/System.IO/src/System.IO.csproj b/src/System.IO/src/System.IO.csproj index 6e99f7028dd8..37b07feb5986 100644 --- a/src/System.IO/src/System.IO.csproj +++ b/src/System.IO/src/System.IO.csproj @@ -5,8 +5,12 @@ System.IO {07390899-C8F6-4e83-A3A9-6867B8CB46A0} 4.1.0.0 - true + true dotnet5.4 + + true @@ -34,6 +38,7 @@ System.Diagnostics.Debug Windows_NT + diff --git a/src/System.IO/src/System/IO/BinaryReader.cs b/src/System.IO/src/System/IO/BinaryReader.cs index 27dfb3ca6694..dd68918efef3 100644 --- a/src/System.IO/src/System/IO/BinaryReader.cs +++ b/src/System.IO/src/System/IO/BinaryReader.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Text; +using System.Buffers; using System.Diagnostics; +using System.Text; namespace System.IO { @@ -56,7 +57,7 @@ public BinaryReader(Stream input, Encoding encoding, bool leaveOpen) minBufferSize = 16; } - _buffer = new byte[minBufferSize]; + _buffer = ArrayPool.Shared.Rent(minBufferSize); // _charBuffer and _charBytes will be left null. // For Encodings that always use 2 bytes per char (or more), @@ -89,12 +90,25 @@ protected virtual void Dispose(bool disposing) copyOfStream.Dispose(); } } + _stream = null; - _buffer = null; _decoder = null; - _charBytes = null; _singleChar = null; - _charBuffer = null; + if (_buffer != null) + { + ArrayPool.Shared.Return(_buffer); + _buffer = null; + } + if (_charBytes != null) + { + ArrayPool.Shared.Return(_charBytes); + _charBytes = null; + } + if (_charBuffer != null) + { + ArrayPool.Shared.Return(_charBuffer); + _charBuffer = null; + } } public void Dispose() @@ -294,12 +308,12 @@ public virtual string ReadString() if (_charBytes == null) { - _charBytes = new byte[MaxCharBytesSize]; + _charBytes = ArrayPool.Shared.Rent(MaxCharBytesSize); } if (_charBuffer == null) { - _charBuffer = new char[_maxCharsSize]; + _charBuffer = ArrayPool.Shared.Rent(_maxCharsSize); } StringBuilder sb = null; @@ -370,7 +384,7 @@ private int InternalReadChars(char[] buffer, int index, int count) if (_charBytes == null) { - _charBytes = new byte[MaxCharBytesSize]; + _charBytes = ArrayPool.Shared.Rent(MaxCharBytesSize); } while (charsRemaining > 0) @@ -451,7 +465,7 @@ private int InternalReadOneChar() if (_charBytes == null) { - _charBytes = new byte[MaxCharBytesSize]; //REVIEW: We need atmost 2 bytes/char here? + _charBytes = ArrayPool.Shared.Rent(MaxCharBytesSize); } if (_singleChar == null) { diff --git a/src/System.IO/src/System/IO/BinaryWriter.cs b/src/System.IO/src/System/IO/BinaryWriter.cs index fabe1a6268cc..b9bf4ef0327e 100644 --- a/src/System.IO/src/System/IO/BinaryWriter.cs +++ b/src/System.IO/src/System/IO/BinaryWriter.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Text; +using System.Buffers; using System.Diagnostics; +using System.Text; namespace System.IO { @@ -33,7 +34,7 @@ public class BinaryWriter : IDisposable protected BinaryWriter() { OutStream = Stream.Null; - _buffer = new byte[16]; + _buffer = ArrayPool.Shared.Rent(16); _encoding = new UTF8Encoding(false, true); _encoder = _encoding.GetEncoder(); } @@ -62,7 +63,7 @@ public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen) } OutStream = output; - _buffer = new byte[16]; + _buffer = ArrayPool.Shared.Rent(16); _encoding = encoding; _encoder = _encoding.GetEncoder(); _leaveOpen = leaveOpen; @@ -81,6 +82,18 @@ protected virtual void Dispose(bool disposing) OutStream.Dispose(); } } + + if (_buffer != null) + { + ArrayPool.Shared.Return(_buffer); + _buffer = null; + } + + if (_largeByteBuffer != null) + { + ArrayPool.Shared.Return(_largeByteBuffer); + _largeByteBuffer = null; + } } public void Dispose() @@ -371,7 +384,7 @@ public unsafe virtual void Write(string value) if (_largeByteBuffer == null) { - _largeByteBuffer = new byte[LargeByteBufferSize]; + _largeByteBuffer = ArrayPool.Shared.Rent(LargeByteBufferSize); _maxChars = LargeByteBufferSize / _encoding.GetMaxByteCount(1); } diff --git a/src/System.IO/src/System/IO/BufferedStream.cs b/src/System.IO/src/System/IO/BufferedStream.cs index b3666cb86b65..eb3b74706195 100644 --- a/src/System.IO/src/System/IO/BufferedStream.cs +++ b/src/System.IO/src/System/IO/BufferedStream.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.InteropServices; +using System.Buffers; using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Diagnostics.Contracts; namespace System.IO { @@ -133,8 +134,9 @@ private void EnsureShadowBufferAllocated() if (_buffer.Length != _bufferSize || _bufferSize >= MaxShadowBufferSize) return; - byte[] shadowBuffer = new byte[Math.Min(_bufferSize + _bufferSize, MaxShadowBufferSize)]; + byte[] shadowBuffer = ArrayPool.Shared.Rent(Math.Min(_bufferSize + _bufferSize, MaxShadowBufferSize)); Array.Copy(_buffer, 0, shadowBuffer, 0, _writePos); + ArrayPool.Shared.Return(_buffer); _buffer = shadowBuffer; } @@ -144,7 +146,7 @@ private void EnsureBufferAllocated() // BufferedStream is not intended for multi-threaded use, so no worries about the get/set race on _buffer. if (_buffer == null) - _buffer = new byte[_bufferSize]; + _buffer = ArrayPool.Shared.Rent(_bufferSize); } public override bool CanRead @@ -233,7 +235,11 @@ protected override void Dispose(bool disposing) finally { _stream = null; - _buffer = null; + if (_buffer != null) + { + ArrayPool.Shared.Return(_buffer); + _buffer = null; + } // Call base.Dispose(bool) to cleanup async IO resources base.Dispose(disposing); diff --git a/src/System.IO/src/System/IO/Stream.cs b/src/System.IO/src/System/IO/Stream.cs index 0746370e95bf..38d226a193ea 100644 --- a/src/System.IO/src/System/IO/Stream.cs +++ b/src/System.IO/src/System/IO/Stream.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Threading; @@ -139,11 +140,18 @@ private async Task CopyToAsyncInternal(Stream destination, int bufferSize, Cance Debug.Assert(CanRead); Debug.Assert(destination.CanWrite); - byte[] buffer = new byte[bufferSize]; - int bytesRead; - while ((bytesRead = await ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try { - await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + int bytesRead; + while ((bytesRead = await ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + } + } + finally + { + ArrayPool.Shared.Return(buffer); } } @@ -213,11 +221,18 @@ private void InternalCopyTo(Stream destination, int bufferSize) Debug.Assert(destination.CanWrite); Debug.Assert(bufferSize > 0); - byte[] buffer = new byte[bufferSize]; - int read; - while ((read = Read(buffer, 0, buffer.Length)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int read; + while ((read = Read(buffer, 0, buffer.Length)) != 0) + { + destination.Write(buffer, 0, read); + } + } + finally { - destination.Write(buffer, 0, read); + ArrayPool.Shared.Return(buffer); } } diff --git a/src/System.IO/src/project.json b/src/System.IO/src/project.json index a6188837b582..b140102a3423 100644 --- a/src/System.IO/src/project.json +++ b/src/System.IO/src/project.json @@ -2,11 +2,13 @@ "frameworks": { "dnxcore50": { "dependencies": { - "Microsoft.TargetingPack.Private.CoreCLR": "1.0.0-rc3-23808" + "Microsoft.TargetingPack.Private.CoreCLR": "1.0.0-rc3-23808", + "System.Buffers" : "4.0.0-rc3-23808" } }, "netcore50": { "dependencies": { + "System.Buffers" : "4.0.0-rc3-23808", "System.Diagnostics.Contracts": "4.0.0", "System.Diagnostics.Debug": "4.0.10", "System.Diagnostics.Tools": "4.0.0",