未验证 提交 c215a3b1 编写于 作者: B Benjamin Bartels 提交者: GitHub

Added fast path to BinaryWriter.Write(string) (#37705)

上级 fe35942e
...@@ -351,7 +351,7 @@ public virtual unsafe void Write(float value) ...@@ -351,7 +351,7 @@ public virtual unsafe void Write(float value)
// Writes a length-prefixed string to this stream in the BinaryWriter's // Writes a length-prefixed string to this stream in the BinaryWriter's
// current Encoding. This method first writes the length of the string as // current Encoding. This method first writes the length of the string as
// a four-byte unsigned integer, and then writes that many characters // an encoded unsigned integer with variable length, and then writes that many characters
// to the stream. // to the stream.
// //
public virtual unsafe void Write(string value) public virtual unsafe void Write(string value)
...@@ -359,8 +359,8 @@ public virtual unsafe void Write(string value) ...@@ -359,8 +359,8 @@ public virtual unsafe void Write(string value)
if (value == null) if (value == null)
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
int len = _encoding.GetByteCount(value); int totalBytes = _encoding.GetByteCount(value);
Write7BitEncodedInt(len); Write7BitEncodedInt(totalBytes);
if (_largeByteBuffer == null) if (_largeByteBuffer == null)
{ {
...@@ -368,54 +368,88 @@ public virtual unsafe void Write(string value) ...@@ -368,54 +368,88 @@ public virtual unsafe void Write(string value)
_maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1); _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1);
} }
if (len <= _largeByteBuffer.Length) if (totalBytes <= _largeByteBuffer.Length)
{ {
_encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0); _encoding.GetBytes(value, _largeByteBuffer);
OutStream.Write(_largeByteBuffer, 0, len); OutStream.Write(_largeByteBuffer, 0, totalBytes);
return;
} }
int numLeft = value.Length;
int charStart = 0;
ReadOnlySpan<char> str = value;
// The previous implementation had significant issues packing encoded
// characters efficiently into the byte buffer. This was due to the assumption,
// that every input character will take up the maximum possible size of a character in any given encoding,
// thus resulting in a lot of unused space within the byte buffer.
// However, in scenarios where the number of characters aligns perfectly with the buffer size the new
// implementation saw some performance regressions, therefore in such scenarios (ASCIIEncoding)
// work will be delegated to the previous implementation.
if (_encoding.GetType() == typeof(UTF8Encoding))
{
while (numLeft > 0)
{
_encoder.Convert(str.Slice(charStart), _largeByteBuffer, numLeft <= _maxChars, out int charCount, out int byteCount, out bool _);
OutStream.Write(_largeByteBuffer, 0, byteCount);
charStart += charCount;
numLeft -= charCount;
}
}
else else
{ {
// Aggressively try to not allocate memory in this loop for WriteWhenEncodingIsNotUtf8(value, totalBytes);
// runtime performance reasons. Use an Encoder to write out }
// the string correctly (handling surrogates crossing buffer }
// boundaries properly).
int charStart = 0; private unsafe void WriteWhenEncodingIsNotUtf8(string value, int len)
int numLeft = value.Length; {
// This method should only be called from BinaryWriter(string), which does a null-check
Debug.Assert(_largeByteBuffer != null);
int numLeft = value.Length;
int charStart = 0;
// Aggressively try to not allocate memory in this loop for
// runtime performance reasons. Use an Encoder to write out
// the string correctly (handling surrogates crossing buffer
// boundaries properly).
#if DEBUG #if DEBUG
int totalBytes = 0; int totalBytes = 0;
#endif #endif
while (numLeft > 0) while (numLeft > 0)
{ {
// Figure out how many chars to process this round. // Figure out how many chars to process this round.
int charCount = (numLeft > _maxChars) ? _maxChars : numLeft; int charCount = (numLeft > _maxChars) ? _maxChars : numLeft;
int byteLen; int byteLen;
checked checked
{
if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount)
{ {
if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount) throw new ArgumentOutOfRangeException(nameof(value));
{ }
throw new ArgumentOutOfRangeException(nameof(value)); fixed (char* pChars = value)
} {
fixed (char* pChars = value) fixed (byte* pBytes = &_largeByteBuffer[0])
{ {
fixed (byte* pBytes = &_largeByteBuffer[0]) byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft);
{
byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft);
}
} }
} }
#if DEBUG
totalBytes += byteLen;
Debug.Assert(totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!");
#endif
OutStream.Write(_largeByteBuffer, 0, byteLen);
charStart += charCount;
numLeft -= charCount;
} }
#if DEBUG #if DEBUG
Debug.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!"); totalBytes += byteLen;
Debug.Assert(totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!");
#endif #endif
OutStream.Write(_largeByteBuffer, 0, byteLen);
charStart += charCount;
numLeft -= charCount;
} }
#if DEBUG
Debug.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!");
#endif
} }
public virtual void Write(ReadOnlySpan<byte> buffer) public virtual void Write(ReadOnlySpan<byte> buffer)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册