[Mono.Profiler.Log] Fix excessive byte array allocations.
authorAlex Rønne Petersen <alpeters@microsoft.com>
Tue, 19 Sep 2017 18:32:26 +0000 (20:32 +0200)
committerAlex Rønne Petersen <alpeters@microsoft.com>
Wed, 20 Sep 2017 12:17:26 +0000 (14:17 +0200)
The problem was that the base Stream.ReadByte () method allocates a temporary
byte array and then calls Read (..., 1) on it. To solve this, we override
ReadByte () in LogStream and use a private buffer to hold the result.

It's a bit of a mystery to me why Stream.ReadByte () does it this way. The
documentation for Stream explicitly says that instance methods aren't thread
safe, so it would be perfectly fine for Stream.ReadByte () to do what we do
here...

mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStream.cs

index 4fd0daf227d415e4cdfd69fa2f5e6a910a31a95b..b52402a91d43e23a359acfadb0fb14a61d7ffae2 100644 (file)
@@ -26,6 +26,8 @@ namespace Mono.Profiler.Log {
                        set => throw new NotSupportedException ();
                }
 
+               readonly byte[] _byteBuffer = new byte [1];
+
                public LogStream (Stream baseStream)
                {
                        if (baseStream == null)
@@ -48,6 +50,14 @@ namespace Mono.Profiler.Log {
                        throw new NotSupportedException ();
                }
 
+               public override int ReadByte ()
+               {
+                       // The base method on Stream is extremely inefficient in that it
+                       // allocates a 1-byte array for every call. Simply use a private
+                       // buffer instead.
+                       return Read (_byteBuffer, 0, sizeof (byte)) == 0 ? -1 : _byteBuffer [0];
+               }
+
                public override int Read (byte[] buffer, int offset, int count)
                {
                        return BaseStream.Read (buffer, offset, count);