Skip to content

Commit

Permalink
Optimize parameter encoding. (mysql-net#1296)
Browse files Browse the repository at this point in the history
Some types of parameters encoded as "text" or "length encoded text" are guaranteed to be ASCII. Using the ASCII encoder provides a significant performance improvement.

Signed-off-by: rusher <[email protected]>
  • Loading branch information
rusher authored Mar 30, 2023
1 parent aac655c commit d04e24a
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 14 deletions.
28 changes: 14 additions & 14 deletions src/MySqlConnector/MySqlParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
}
else if (Value is decimal decimalValue)
{
writer.Write(decimalValue.ToString(CultureInfo.InvariantCulture));
writer.WriteAscii(decimalValue.ToString(CultureInfo.InvariantCulture));
}
else if (Value is short shortValue)
{
Expand Down Expand Up @@ -320,20 +320,20 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
else if (Value is float floatValue)
{
// NOTE: Utf8Formatter doesn't support "R"
writer.Write(floatValue.ToString("R", CultureInfo.InvariantCulture));
writer.WriteAscii(floatValue.ToString("R", CultureInfo.InvariantCulture));
}
else if (Value is double doubleValue)
{
// NOTE: Utf8Formatter doesn't support "R"
writer.Write(doubleValue.ToString("R", CultureInfo.InvariantCulture));
writer.WriteAscii(doubleValue.ToString("R", CultureInfo.InvariantCulture));
}
else if (Value is BigInteger bigInteger)
{
writer.Write(bigInteger.ToString(CultureInfo.InvariantCulture));
writer.WriteAscii(bigInteger.ToString(CultureInfo.InvariantCulture));
}
else if (Value is MySqlDecimal mySqlDecimal)
{
writer.Write(mySqlDecimal.ToString());
writer.WriteAscii(mySqlDecimal.ToString());
}
else if (Value is MySqlDateTime mySqlDateTimeValue)
{
Expand All @@ -344,7 +344,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
#else
var str = FormattableString.Invariant($"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
#endif
writer.Write(str);
writer.WriteAscii(str);
}
else
{
Expand All @@ -354,7 +354,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
#if NET6_0_OR_GREATER
else if (Value is DateOnly dateOnlyValue)
{
writer.Write(string.Create(CultureInfo.InvariantCulture, stackalloc char[23], $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')"));
writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[23], $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')"));
}
#endif
else if (Value is DateTime dateTimeValue)
Expand All @@ -369,7 +369,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
#else
var str = FormattableString.Invariant($"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
#endif
writer.Write(str);
writer.WriteAscii(str);
}
else if (Value is DateTimeOffset dateTimeOffsetValue)
{
Expand All @@ -379,12 +379,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
#else
var str = FormattableString.Invariant($"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
#endif
writer.Write(str);
writer.WriteAscii(str);
}
#if NET6_0_OR_GREATER
else if (Value is TimeOnly timeOnlyValue)
{
writer.Write(string.Create(CultureInfo.InvariantCulture, stackalloc char[22], $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'"));
writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[22], $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'"));
}
#endif
else if (Value is TimeSpan ts)
Expand All @@ -400,7 +400,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
#else
var str = FormattableString.Invariant($"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'");
#endif
writer.Write(str);
writer.WriteAscii(str);
}
else if (Value is Guid guidValue)
{
Expand Down Expand Up @@ -628,11 +628,11 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
}
else if (Value is decimal decimalValue)
{
writer.WriteLengthEncodedString(decimalValue.ToString(CultureInfo.InvariantCulture));
writer.WriteLengthEncodedAsciiString(decimalValue.ToString(CultureInfo.InvariantCulture));
}
else if (Value is BigInteger bigInteger)
{
writer.WriteLengthEncodedString(bigInteger.ToString(CultureInfo.InvariantCulture));
writer.WriteLengthEncodedAsciiString(bigInteger.ToString(CultureInfo.InvariantCulture));
}
else if (Value is MySqlDateTime mySqlDateTimeValue)
{
Expand All @@ -643,7 +643,7 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
}
else if (Value is MySqlDecimal mySqlDecimal)
{
writer.WriteLengthEncodedString(mySqlDecimal.ToString());
writer.WriteLengthEncodedAsciiString(mySqlDecimal.ToString());
}
#if NET6_0_OR_GREATER
else if (Value is DateOnly dateOnlyValue)
Expand Down
16 changes: 16 additions & 0 deletions src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public void Write(ReadOnlySpan<byte> span)
}

public void Write(string value) => Write(value.AsSpan(), flush: true);

public void WriteAscii(string value) => WriteAscii(value.AsSpan());

public void Write(string value, int offset, int length) => Write(value.AsSpan(offset, length), flush: true);

public void Write(ReadOnlySpan<char> chars, bool flush)
Expand All @@ -126,6 +129,13 @@ public void Write(ReadOnlySpan<char> chars, bool flush)
}
}

public void WriteAscii(ReadOnlySpan<char> chars)
{
if (m_output.Length < chars.Length)
Reallocate(chars.Length - m_output.Length);
m_output = m_output[Encoding.ASCII.GetBytes(chars, m_output.Span)..];
}

public void WriteLengthEncodedString(StringBuilder stringBuilder)
{
#if NETCOREAPP3_0_OR_GREATER
Expand Down Expand Up @@ -266,6 +276,12 @@ public static void WriteLengthEncodedString(this ByteBufferWriter writer, ReadOn
writer.Write(value, flush: true);
}

public static void WriteLengthEncodedAsciiString(this ByteBufferWriter writer, string value)
{
writer.WriteLengthEncodedInteger((ulong) value.Length);
writer.WriteAscii(value.AsSpan());
}

public static void WriteNullTerminatedString(this ByteBufferWriter writer, string value)
{
writer.Write(value);
Expand Down

0 comments on commit d04e24a

Please sign in to comment.