Tracing vs Logging Performance in .NET 6
I was wondering what’s the overhead of using ILogger
versus built-in tracing so I’ve made some measurements.
Despite the fact that using ILogger
needs extra library and EventSource
is part of the runtime, I wanted to compare what’s the performance like when using:
- Just
EventSource
. ILogger
with no log sinks configured at all.ILogger
with the fastest sink available, which isEventSource
(to be more honest).
Here are the results:
Method | Mean | Error | StdDev | Min | Max | Median | Gen0 | Allocated |
---|---|---|---|---|---|---|---|---|
EmptyILogger | 40.828 ns | 7.079 ns | 0.3880 ns | 40.468 ns | 41.239 ns | 40.776 ns | 0.0076 | 32 B |
EventSourceILogger | 58.839 ns | 11.665 ns | 0.6394 ns | 58.196 ns | 59.475 ns | 58.845 ns | 0.0076 | 32 B |
NativeEventSource | 3.588 ns | 1.077 ns | 0.0590 ns | 3.528 ns | 3.645 ns | 3.592 ns | - | - |
So it seems like even an empty ILogger withing nothing configured at all is 11.3 times faster. Another interesting thing is EventSource has absolutely zero memory allocations, even when using message formatting, wow!
Adding a very fast EventSource logger to ILogger makes it 16.3 times slower than EventSource. Adding other sinks is apparently even worse.
Now, this is not a critique of ILogger in any way, because ES and IL serve completely different purposes. It’s a simple comparison for a piece of mind.
For completeness, including benchmarking code below:
#LINQPad optimize+
[EventSource(Name = "Perf")]
public class PerfEventSource : EventSource {
public static PerfEventSource Log { get; } = new PerfEventSource();
[Event(1, Message = "a simple {0}", Level = EventLevel.Informational)]
public void PerfPing(string p) {
WriteEvent(1, p);
}
}
[ShortRunJob]
[MinColumn, MaxColumn, MeanColumn, MedianColumn]
[MemoryDiagnoser]
[MarkdownExporter]
public class TracingVsLoggingBenchmark {
Microsoft.Extensions.Logging.ILogger emptyLogger;
Microsoft.Extensions.Logging.ILogger esLogger;
[GlobalSetup]
public void Setup() {
var emptyFactory = LoggerFactory.Create(builder => {
});
emptyLogger = emptyFactory.CreateLogger("empty");
var tracingFactory = LoggerFactory.Create(builder => {
builder.AddEventSourceLogger();
});
esLogger = tracingFactory.CreateLogger("es");
}
[Benchmark]
public void EmptyILogger() {
emptyLogger.LogInformation("a simple {0}", "message");
}
[Benchmark]
public void EventSourceILogger() {
esLogger.LogInformation("a simple {0}", "message");
}
[Benchmark]
public void NativeEventSource() {
PerfEventSource.Log.PerfPing("message");
}
}
void Main() {
Util.AutoScrollResults = true;
BenchmarkRunner.Run<TracingVsLoggingBenchmark>();
}
To contact me, send an email anytime or leave a comment below.