-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reduce heap allocation of the default log message formatting logic in…
… the FileLogger.Log (#74) * Reduce heap allocation of the default log message formatting logic in FileLogger.Log * Hide TryFormat extensions under preprocessor directives for target frameworks greater than netstandard2.0 * Add tests for int and DateTime extensions, multi-target the test project to cover netstandard2.0 * Improve DateTimeExtensionsTests a tiny bit * Tests: update .NET Framework target from net462 to net481
- Loading branch information
Showing
10 changed files
with
691 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using System; | ||
|
||
#nullable enable | ||
|
||
namespace NReco.Logging.File.Extensions { | ||
public static class DateTimeExtensions { | ||
public static int GetFormattedLength(this DateTime dateTime) { | ||
const int BaseCharCountInFormatO = 27; | ||
|
||
return BaseCharCountInFormatO + dateTime.Kind switch { | ||
DateTimeKind.Local => 6, | ||
DateTimeKind.Utc => 1, | ||
_ => 0 | ||
}; | ||
} | ||
|
||
#if NETSTANDARD2_0 | ||
public static bool TryFormatO(this DateTime dateTime, Span<char> destination, out int charsWritten) { | ||
var charsRequired = dateTime.GetFormattedLength(); | ||
|
||
if (destination.Length < charsRequired) { | ||
charsWritten = 0; | ||
return false; | ||
} | ||
|
||
charsWritten = charsRequired; | ||
|
||
var year = (uint)dateTime.Year; | ||
var month = (uint)dateTime.Month; | ||
var day = (uint)dateTime.Day; | ||
var hour = (uint)dateTime.Hour; | ||
var minute = (uint)dateTime.Minute; | ||
var second = (uint)dateTime.Second; | ||
var tick = (uint)(dateTime.Ticks - (dateTime.Ticks / TimeSpan.TicksPerSecond * TimeSpan.TicksPerSecond)); | ||
|
||
year.WriteDigits(destination, 4); | ||
destination[4] = '-'; | ||
month.WriteDigits(destination.Slice(5), 2); | ||
destination[7] = '-'; | ||
day.WriteDigits(destination.Slice(8), 2); | ||
destination[10] = 'T'; | ||
hour.WriteDigits(destination.Slice(11), 2); | ||
destination[13] = ':'; | ||
minute.WriteDigits(destination.Slice(14), 2); | ||
destination[16] = ':'; | ||
second.WriteDigits(destination.Slice(17), 2); | ||
destination[19] = '.'; | ||
tick.WriteDigits(destination.Slice(20), 7); | ||
|
||
var kind = dateTime.Kind; | ||
if (kind == DateTimeKind.Local) { | ||
var offset = TimeZoneInfo.Local.GetUtcOffset(dateTime); | ||
var offsetTotalMinutes = (int)(offset.Ticks / TimeSpan.TicksPerMinute); | ||
|
||
var sign = '+'; | ||
if (offsetTotalMinutes < 0) { | ||
sign = '-'; | ||
offsetTotalMinutes = -offsetTotalMinutes; | ||
} | ||
|
||
var offsetHours = Math.DivRem(offsetTotalMinutes, 60, out var offsetMinutes); | ||
|
||
destination[27] = sign; | ||
((uint)offsetHours).WriteDigits(destination.Slice(28), 2); | ||
destination[30] = ':'; | ||
((uint)offsetMinutes).WriteDigits(destination.Slice(31), 2); | ||
} | ||
else if (kind == DateTimeKind.Utc) { | ||
destination[27] = 'Z'; | ||
} | ||
|
||
return true; | ||
} | ||
#else | ||
public static bool TryFormatO(this DateTime dateTime, Span<char> destination, out int charsWritten) { | ||
return dateTime.TryFormat(destination, out charsWritten, format: "O"); | ||
} | ||
#endif | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
|
||
#nullable enable | ||
|
||
namespace NReco.Logging.File.Extensions { | ||
public static class IntExtensions { | ||
public static int GetFormattedLength(this int value) { | ||
return value == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs((double)value))) + (value > 0 ? 1 : 2); | ||
} | ||
|
||
#if NETSTANDARD2_0 | ||
public static bool TryFormat(this int value, Span<char> destination, out int charsWritten) { | ||
charsWritten = value.GetFormattedLength(); | ||
if (destination.Length < charsWritten) { | ||
charsWritten = 0; | ||
return false; | ||
} | ||
|
||
var dst = destination.Slice(0, charsWritten); | ||
|
||
if (value < 0) { | ||
dst[0] = '-'; | ||
dst = dst.Slice(1); | ||
} | ||
|
||
((uint)Math.Abs((long)value)).WriteDigits(dst, dst.Length); | ||
return true; | ||
} | ||
|
||
internal static void WriteDigits(this uint value, Span<char> destination, int count) { | ||
for (var cur = count - 1; cur > 0; cur--) { | ||
uint temp = '0' + value; | ||
value /= 10; | ||
destination[cur] = (char)(temp - (value * 10)); | ||
} | ||
|
||
destination[0] = (char)('0' + value); | ||
} | ||
#endif | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.