From 69c3475da115fb446aaac14ba952e0e1c197dc0a Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 26 May 2022 16:29:24 -0500 Subject: [PATCH] Fix Stream.ReadAtLeast perf regression in DataContractSerializer #69272 changed DCS to no longer call Stream.Read inside a loop, but instead call the new ReadAtLeast method. ReadAtLeast only takes a Span, and not a byte[]. This caused a regression because the internal encoding stream wrapper classes don't override Read(Span), so the base implementation is used. The base implementation is slower because it needs to rent a byte[] from the pool, and do 2 copies. Overriding Read(Span) on the internal encoding stream wrapper classes allows ReadAtLeast to be just as fast. Fix #69730 --- .../Serialization/Json/JsonEncodingStreamWrapper.cs | 11 ++++++++--- .../src/System/Xml/EncodingStreamWrapper.cs | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs index 8f30f153be140..f6a0516d24bac 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs @@ -174,7 +174,10 @@ public override void Flush() _stream.Flush(); } - public override int Read(byte[] buffer, int offset, int count) + public override int Read(byte[] buffer, int offset, int count) => + Read(new Span(buffer, offset, count)); + + public override int Read(Span buffer) { try { @@ -182,7 +185,7 @@ public override int Read(byte[] buffer, int offset, int count) { if (_encodingCode == SupportedEncoding.UTF8) { - return _stream.Read(buffer, offset, count); + return _stream.Read(buffer); } Debug.Assert(_bytes != null); @@ -206,11 +209,13 @@ public override int Read(byte[] buffer, int offset, int count) } // Give them bytes + int count = buffer.Length; if (_byteCount < count) { count = _byteCount; } - Buffer.BlockCopy(_bytes!, _byteOffset, buffer, offset, count); + + _bytes.AsSpan(_byteOffset, count).CopyTo(buffer); _byteOffset += count; _byteCount -= count; return count; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs index e0fe24ba58163..7d9bab87855fd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs @@ -593,14 +593,17 @@ public override int ReadByte() return _byteBuffer[0]; } - public override int Read(byte[] buffer, int offset, int count) + public override int Read(byte[] buffer, int offset, int count) => + Read(new Span(buffer, offset, count)); + + public override int Read(Span buffer) { try { if (_byteCount == 0) { if (_encodingCode == SupportedEncoding.UTF8) - return _stream.Read(buffer, offset, count); + return _stream.Read(buffer); Debug.Assert(_bytes != null); Debug.Assert(_chars != null); @@ -622,9 +625,11 @@ public override int Read(byte[] buffer, int offset, int count) } // Give them bytes + int count = buffer.Length; if (_byteCount < count) count = _byteCount; - Buffer.BlockCopy(_bytes!, _byteOffset, buffer, offset, count); + + _bytes.AsSpan(_byteOffset, count).CopyTo(buffer); _byteOffset += count; _byteCount -= count; return count;