KEMBAR78
Fix the boxing when using the LoggerMessage.Define is used and values are converted ToString · Issue #50768 · dotnet/runtime · GitHub
Skip to content

Fix the boxing when using the LoggerMessage.Define is used and values are converted ToString #50768

@davidfowl

Description

@davidfowl

Description

Today LoggerMessage.Define is a high performance logging API that aims to reduce the overhead of logging by pre-parsing format strings and parameters holes to log. Today the values are stored in a LogValues<T0,...,TN> struct to avoid boxing. We do all of this work only to eventually box these arguments and allocate an array to turn this into a string. We should avoid the object[] and the boxing here.

Regression?

No

Data

using System;
using Microsoft.Extensions.Logging;

namespace ConsoleApp105
{
    class Program
    {
        static void Main(string[] args)
        {
            var callback = LoggerMessage.Define<int, int>(LogLevel.Information, new(0, "Event"), "This is a {Value} and a {Score}");
            var logger = new Logger();

            Console.WriteLine("Waiting");
            Console.ReadLine();

            for (int i = 0; i < 10000; i++)
            {
                callback(logger, 1, 0, null);
            }

            Console.WriteLine("Done");
            Console.ReadLine();

        }

        public class Logger : ILogger
        {
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }

            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }

            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                var value = formatter(state, exception);
                LogToSomeplace(value);
            }

            private void LogToSomeplace(string value)
            {
                
            }
        }
    }
}
Type Allocations Bytes Average Size (Bytes)
- System.Int32 20,010 480,240 24
- System.String 12,115 957,270 79.02

When I add a 4 int parameters:

Type Allocations Bytes Average Size (Bytes)
- System.String 12,119 1,217,518 100.46
- System.Int32 40,010 960,240 24
- System.Object[4] 10,000 560,000 56

Analysis

We need to implement non-allocating formatting for these calls

internal string FormatWithOverwrite(object?[]? values)
{
if (values != null)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = FormatArgument(values[i]);
}
}
return string.Format(CultureInfo.InvariantCulture, _format, values ?? Array.Empty<object>());
}
internal string Format()
{
return _format;
}
internal string Format(object? arg0)
{
return string.Format(CultureInfo.InvariantCulture, _format, FormatArgument(arg0));
}
internal string Format(object? arg0, object? arg1)
{
return string.Format(CultureInfo.InvariantCulture, _format, FormatArgument(arg0), FormatArgument(arg1));
}
internal string Format(object? arg0, object? arg1, object? arg2)
{
return string.Format(CultureInfo.InvariantCulture, _format, FormatArgument(arg0), FormatArgument(arg1), FormatArgument(arg2));
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions