Skip to content

Commit

Permalink
Removed temporary array allocations for cases with the small number o…
Browse files Browse the repository at this point in the history
…f log event properties
  • Loading branch information
epeshk committed Aug 21, 2023
1 parent 801898d commit 3b7311a
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
6 changes: 5 additions & 1 deletion src/Serilog/Capturing/MessageTemplateProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ public MessageTemplateProcessor(PropertyValueConverter propertyValueConverter)
_propertyBinder = new(_propertyValueConverter);
}

public void Process(string messageTemplate, object?[]? messageTemplateParameters, out MessageTemplate parsedTemplate, out EventProperty[] properties)
#if FEATURE_SPAN
public void Process(string messageTemplate, ReadOnlySpan<object?> messageTemplateParameters, out MessageTemplate parsedTemplate, out EventProperty[] properties)
#else
public void Process(string messageTemplate, object?[] messageTemplateParameters, out MessageTemplate parsedTemplate, out EventProperty[] properties)
#endif
{
parsedTemplate = _parser.Parse(messageTemplate);
properties = _propertyBinder.ConstructProperties(parsedTemplate, messageTemplateParameters);
Expand Down
20 changes: 16 additions & 4 deletions src/Serilog/Capturing/PropertyBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ public PropertyBinder(PropertyValueConverter valueConverter)
/// represented in the message template.</param>
/// <returns>A list of properties; if the template is malformed then
/// this will be empty.</returns>
public EventProperty[] ConstructProperties(MessageTemplate messageTemplate, object?[]? messageTemplateParameters)
#if FEATURE_SPAN
public EventProperty[] ConstructProperties(MessageTemplate messageTemplate, ReadOnlySpan<object?> messageTemplateParameters)
#else
public EventProperty[] ConstructProperties(MessageTemplate messageTemplate, object?[] messageTemplateParameters)
#endif
{
if (messageTemplateParameters == null || messageTemplateParameters.Length == 0)
if (messageTemplateParameters.Length == 0)
{
if (messageTemplate.NamedProperties != null || messageTemplate.PositionalProperties != null)
SelfLog.WriteLine("Required properties not provided for: {0}", messageTemplate);
Expand All @@ -47,10 +51,14 @@ public EventProperty[] ConstructProperties(MessageTemplate messageTemplate, obje
if (messageTemplate.PositionalProperties != null)
return ConstructPositionalProperties(messageTemplate, messageTemplateParameters, messageTemplate.PositionalProperties);

return ConstructNamedProperties(messageTemplate, messageTemplateParameters!);
return ConstructNamedProperties(messageTemplate, messageTemplateParameters);
}

#if FEATURE_SPAN
EventProperty[] ConstructPositionalProperties(MessageTemplate template, ReadOnlySpan<object?> messageTemplateParameters, PropertyToken[] positionalProperties)
#else
EventProperty[] ConstructPositionalProperties(MessageTemplate template, object?[] messageTemplateParameters, PropertyToken[] positionalProperties)
#endif
{
var result = new EventProperty[messageTemplateParameters.Length];
foreach (var property in positionalProperties)
Expand Down Expand Up @@ -83,7 +91,11 @@ EventProperty[] ConstructPositionalProperties(MessageTemplate template, object?[
return result;
}

EventProperty[] ConstructNamedProperties(MessageTemplate template, object[] messageTemplateParameters)
#if FEATURE_SPAN
EventProperty[] ConstructNamedProperties(MessageTemplate template, ReadOnlySpan<object?> messageTemplateParameters)
#else
EventProperty[] ConstructNamedProperties(MessageTemplate template, object?[] messageTemplateParameters)
#endif
{
var namedProperties = template.NamedProperties;
if (namedProperties == null)
Expand Down
77 changes: 76 additions & 1 deletion src/Serilog/Core/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,14 @@ public void Write<T>(LogEventLevel level, string messageTemplate, T propertyValu
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
#if FEATURE_SPAN
var inlineArray = new PropertiesInlineArray();
var span = inlineArray.AsSpan(1);
span[0] = propertyValue;
Write(level, messageTemplate, span);
#else
Write(level, messageTemplate, new object?[] { propertyValue });
#endif
}
}

Expand All @@ -222,7 +229,15 @@ public void Write<T>(LogEventLevel level, string messageTemplate, T propertyValu
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
#if FEATURE_SPAN
var inlineArray = new PropertiesInlineArray();
var span = inlineArray.AsSpan(2);
span[0] = propertyValue0;
span[1] = propertyValue1;
Write(level, messageTemplate, span);
#else
Write(level, messageTemplate, new object?[] { propertyValue0, propertyValue1 });
#endif
}
}

Expand All @@ -240,7 +255,16 @@ public void Write<T>(LogEventLevel level, string messageTemplate, T propertyValu
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
#if FEATURE_SPAN
var inlineArray = new PropertiesInlineArray();
var span = inlineArray.AsSpan(3);
span[0] = propertyValue0;
span[1] = propertyValue1;
span[2] = propertyValue2;
Write(level, messageTemplate, span);
#else
Write(level, messageTemplate, new object?[] { propertyValue0, propertyValue1, propertyValue2 });
#endif
}
}

Expand All @@ -256,6 +280,13 @@ public void Write(LogEventLevel level, string messageTemplate, params object?[]?
Write(level, (Exception?)null, messageTemplate, propertyValues);
}

#if FEATURE_SPAN
void Write(LogEventLevel level, string messageTemplate, ReadOnlySpan<object?> propertyValues)
{
Write(level, null, messageTemplate, propertyValues);
}
#endif

/// <summary>
/// Determine if events at the specified level, and higher, will be passed through
/// to the log sinks.
Expand Down Expand Up @@ -300,7 +331,14 @@ public void Write<T>(LogEventLevel level, Exception? exception, string messageTe
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
#if FEATURE_SPAN
var inlineArray = new PropertiesInlineArray();
var span = inlineArray.AsSpan(1);
span[0] = propertyValue;
Write(level, exception, messageTemplate, span);
#else
Write(level, exception, messageTemplate, new object?[] { propertyValue });
#endif
}
}

Expand All @@ -318,7 +356,15 @@ public void Write<T>(LogEventLevel level, Exception? exception, string messageTe
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
#if FEATURE_SPAN
var inlineArray = new PropertiesInlineArray();
var span = inlineArray.AsSpan(2);
span[0] = propertyValue0;
span[1] = propertyValue1;
Write(level, exception, messageTemplate, span);
#else
Write(level, exception, messageTemplate, new object?[] { propertyValue0, propertyValue1 });
#endif
}
}

Expand All @@ -337,7 +383,16 @@ public void Write<T>(LogEventLevel level, Exception? exception, string messageTe
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
#if FEATURE_SPAN
var inlineArray = new PropertiesInlineArray();
var span = inlineArray.AsSpan(3);
span[0] = propertyValue0;
span[1] = propertyValue1;
span[2] = propertyValue2;
Write(level, exception, messageTemplate, span);
#else
Write(level, exception, messageTemplate, new object?[] { propertyValue0, propertyValue1, propertyValue2 });
#endif
}
}

Expand All @@ -359,12 +414,32 @@ public void Write(LogEventLevel level, Exception? exception, string messageTempl
propertyValues.GetType() != typeof(object[]))
propertyValues = new object[] { propertyValues };

var logTimestamp = DateTimeOffset.Now;
#if FEATURE_SPAN
var propertiesSpan = propertyValues == null ? Span<object?>.Empty : propertyValues.AsSpan();
_messageTemplateProcessor.Process(messageTemplate, propertiesSpan, out var parsedTemplate, out var boundProperties);
#else
_messageTemplateProcessor.Process(messageTemplate, propertyValues ?? NoPropertyValues, out var parsedTemplate, out var boundProperties);
#endif

var logEvent = new LogEvent(logTimestamp, level, exception, parsedTemplate, boundProperties);
Dispatch(logEvent);
}

#if FEATURE_SPAN
[MessageTemplateFormatMethod("messageTemplate")]
void Write(LogEventLevel level, Exception? exception, string messageTemplate, ReadOnlySpan<object?> propertyValues)
{
if (!IsEnabled(level)) return;
if (messageTemplate == null) return;

var logTimestamp = DateTimeOffset.Now;
_messageTemplateProcessor.Process(messageTemplate, propertyValues, out var parsedTemplate, out var boundProperties);

var logEvent = new LogEvent(logTimestamp, level, exception, parsedTemplate, boundProperties);
Dispatch(logEvent);
}
#endif

/// <summary>
/// Write an event to the log.
Expand Down Expand Up @@ -1329,7 +1404,7 @@ public void Fatal(Exception? exception, string messageTemplate, params object?[]
return false;
}

_messageTemplateProcessor.Process(messageTemplate, propertyValues, out parsedTemplate, out var boundEventProperties);
_messageTemplateProcessor.Process(messageTemplate, propertyValues ?? NoPropertyValues, out parsedTemplate, out var boundEventProperties);
boundProperties = boundEventProperties.Length == 0 ?
NoProperties :
boundEventProperties.Select(p => new LogEventProperty(p));
Expand Down
27 changes: 27 additions & 0 deletions src/Serilog/Core/PropertiesInlineArray.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#if FEATURE_SPAN
using System.Runtime.InteropServices;

namespace Serilog.Core;

[StructLayout(LayoutKind.Sequential)]
struct PropertiesInlineArray
{
object? Object1;
object? Object2;
object? Object3;

public Span<object?> AsSpan(int count)
{
if (count is < 0 or > 3)
ThrowArgumentOutOfRangeException(nameof(count));

return MemoryMarshal.CreateSpan(ref Object1, count);
}

[DoesNotReturn]
static void ThrowArgumentOutOfRangeException(string param)
{
throw new ArgumentOutOfRangeException(param);
}
}
#endif

0 comments on commit 3b7311a

Please sign in to comment.