diff --git a/src/EasyTrace.Benchmarks/MemoryBenchmark.cs b/src/EasyTrace.Benchmarks/MemoryBenchmark.cs index a6f3c3c..b2d9f78 100644 --- a/src/EasyTrace.Benchmarks/MemoryBenchmark.cs +++ b/src/EasyTrace.Benchmarks/MemoryBenchmark.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -59,13 +58,27 @@ public void Cleanup() } [Benchmark] - public ulong ActivitySource() => Execute(() => _activitySource!.StartActivity()); + public ulong ActivitySource() + { + _activityCounter = 0; - [Benchmark] - public ulong TraceActivityRef() => Execute(() => _traceActivitySource!.Start()); + Parallel.For(0, + ParallelLimit, + i => + { + foreach (var _ in Enumerable.Repeat(0, Iterations)) + { + using var activity1 = _activitySource!.StartActivity(); + using var activity2 = _activitySource!.StartActivity(); + using var activity3 = _activitySource!.StartActivity(); + } + }); - private ulong Execute(Func factory) - where T : IDisposable?, allows ref struct + return Interlocked.Read(ref _activityCounter); + } + + [Benchmark] + public ulong TraceActivityRef() { _activityCounter = 0; @@ -75,9 +88,9 @@ private ulong Execute(Func factory) { foreach (var _ in Enumerable.Repeat(0, Iterations)) { - using var activity1 = factory(); - using var activity2 = factory(); - using var activity3 = factory(); + using var activity1 = _traceActivitySource!.Start(); + using var activity2 = _traceActivitySource!.Start(); + using var activity3 = _traceActivitySource!.Start(); } }); diff --git a/src/EasyTrace/Activity/TraceActivityRef.cs b/src/EasyTrace/Activity/TraceActivityRef.cs index dc15a74..6edef65 100644 --- a/src/EasyTrace/Activity/TraceActivityRef.cs +++ b/src/EasyTrace/Activity/TraceActivityRef.cs @@ -4,8 +4,8 @@ namespace EasyTrace.Activity; -public readonly ref struct TraceActivityRef(TraceActivitySource activitySource, TraceActivity activity) - : ITraceActivity, ICopiable, IDisposable +public readonly ref struct TraceActivityRef(TraceActivity activity) + : ITraceActivity, ICopiable { public TraceIdentifier TraceId => activity.TraceId; public TraceIdentifier SpanId => activity.SpanId; @@ -21,16 +21,6 @@ public readonly ref struct TraceActivityRef(TraceActivitySource activitySource, public TimeSpan Duration => activity.Duration; - public void Dispose() - { - if (activity == TraceActivity.Empty) - { - return; - } - - activitySource.Stop(this, activity); - } - void ICopiable.CopyFrom(TraceActivity source) { activity.CopyFrom(source); diff --git a/src/EasyTrace/Activity/TraceActivityScope.cs b/src/EasyTrace/Activity/TraceActivityScope.cs new file mode 100644 index 0000000..bd1fc6d --- /dev/null +++ b/src/EasyTrace/Activity/TraceActivityScope.cs @@ -0,0 +1,14 @@ +namespace EasyTrace.Activity; + +public readonly struct TraceActivityScope(TraceActivity activity) : IDisposable +{ + public void Dispose() + { + if (activity == TraceActivity.Empty) + { + return; + } + + activity.Source.Stop(activity); + } +} \ No newline at end of file diff --git a/src/EasyTrace/Export/Batch/BatchExportWorker.cs b/src/EasyTrace/Export/Batch/BatchExportWorker.cs index d295852..2408635 100644 --- a/src/EasyTrace/Export/Batch/BatchExportWorker.cs +++ b/src/EasyTrace/Export/Batch/BatchExportWorker.cs @@ -115,7 +115,7 @@ private void ExporterProc() var activities = CircularBuffer.Next(MaxExportBatchSize); foreach (var activity in activities) { - _exporter.Export(new TraceActivityRef(activity.Source, activity)); + _exporter.Export(new TraceActivityRef(activity)); } } } diff --git a/src/EasyTrace/TraceActivitySource.cs b/src/EasyTrace/TraceActivitySource.cs index a8f8dd6..c305c48 100644 --- a/src/EasyTrace/TraceActivitySource.cs +++ b/src/EasyTrace/TraceActivitySource.cs @@ -12,7 +12,7 @@ public class TraceActivitySource(string name, Version? version = null) { private static readonly ThreadLocal ParentActivityByThreadLocal = new(); - internal static TraceActivitySource Empty = new (nameof(Empty)); + internal static readonly TraceActivitySource Empty = new(nameof(Empty)); internal ITraceTimeProvider TimeProvider { get; init; } = new TraceTimeProvider(); internal ITraceIdentifierGenerator IdentifierGenerator { get; init; } = new Xoshiro256PlusPlus(); @@ -28,11 +28,13 @@ private static TraceActivity? Parent public string? Version { get; } = version?.ToString(); - public TraceActivityRef Start([CallerMemberName] string operationName = "", ActivityKind kind = ActivityKind.Internal) + public TraceActivityScope? Start( + [CallerMemberName] string operationName = "", + ActivityKind kind = ActivityKind.Internal) { if (Exporter == null) { - return new TraceActivityRef(this, TraceActivity.Empty); + return null; } var activity = TraceActivityPool.Shared.Rent(); @@ -55,17 +57,17 @@ public TraceActivityRef Start([CallerMemberName] string operationName = "", Acti Parent ??= activity; - return new TraceActivityRef(this, activity); + return new TraceActivityScope(activity); } - public void Stop(scoped in TraceActivityRef activityRef, TraceActivity activity) + public void Stop(TraceActivity activity) { if (activity.EndTime == TimeSpan.Zero) { activity.EndTime = TimeProvider.GetTimestamp(); } - Exporter?.Export(activityRef); + Exporter?.Export(new TraceActivityRef(activity)); if (Parent == activity) {