-
Notifications
You must be signed in to change notification settings - Fork 773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Batch<T> public api additions to unblock users #2542
Conversation
|
||
this.Current = null; | ||
return false; | ||
return this.moveNextFunc(ref this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm hoping this delegate method is faster, but not sure. Need to benchmark it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Yun-Ting could you help on the benchmark part?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, I'm working on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The benchmarks summary are as below.
With If statements: https://github.com/Yun-Ting/opentelemetry-dotnet/blob/Yun-Ting/oldMoveNext/test/OpenTelemetry.Tests/Trace/BatchTestMoveNextBenchmarks.cs
// * Summary *
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1288 (21H1/May2021Update)
Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=5.0.402
[Host] : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT [AttachedDebugger]
DefaultJob : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT
Method | Mean | Error | StdDev | Gen 0 | Allocated |
---|---|---|---|---|---|
SingleElement | 26.18 ns | 0.514 ns | 0.900 ns | - | - |
MoveNextCircularBuffer | 220.11 ns | 4.389 ns | 11.562 ns | 0.0138 | 112 B |
MoveNextArray | 47.21 ns | 0.984 ns | 2.097 ns | 0.0081 | 64 B |
And with delegate methods: https://github.com/Yun-Ting/opentelemetry-dotnet/blob/Yun-Ting/newMoveNext/test/OpenTelemetry.Tests/Trace/BatchTestMoveNextBenchmarks.cs
// * Summary *
Method | Mean | Error | StdDev | Gen 0 | Allocated |
---|---|---|---|---|---|
SingleElement | 26.59 ns | 0.527 ns | 1.002 ns | - | - |
MoveNextCircularBuffer | 219.28 ns | 4.341 ns | 8.769 ns | 0.0141 | 112 B |
MoveNextArray | 54.77 ns | 1.114 ns | 2.146 ns | 0.0081 | 64 B |
With delegate method, MoveNextArray is slightly slower than using if statements.
I think it is caused by:
this.Current = metrics[this.metricIndex];
this.metricIndex++;
vs
enumerator.Current = items[enumerator.itemIndex++];
But I'm quite surprised to learn that one line assignment + variable increment is slower than assign and increment in two lines.
For SingleElement and MoveNextCircularBuffer they are almost identical.
I'm voting for the delegate methods because of code readability.
Codecov Report
@@ Coverage Diff @@
## main #2542 +/- ##
==========================================
+ Coverage 80.65% 80.70% +0.04%
==========================================
Files 254 254
Lines 8494 8516 +22
==========================================
+ Hits 6851 6873 +22
Misses 1643 1643
|
Is it possible to make |
Good question! We could make it a
I think I would vote for leaving it an array or public override ExportResult Export(in Batch<LogRecord> batch)
{
LogRecord[] storage = ArrayPool<LogRecord>.Shared.Rent(batch.Count);
try
{
int storageCount = 0;
foreach (var logRecord in batch)
{
if (shouldInclude(logRecord))
{
storage[storageCount++] = logRecord;
}
}
return this.innerExporter.Export(new Batch(storage, storageCount));
}
finally
{
ArrayPool<LogRecord>.Shared.Return(storage);
}
} |
Yeah, I figured the same after I posted the question 😄
Makes sense 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Let's wait for the perf numbers https://github.com/open-telemetry/opentelemetry-dotnet/pull/2542/files#r738743789.
Is it safe to return the buffer back to the pool in the |
Good question. Batch (at least today) doesn't really dispose anything. For array mode or single item mode, the dispose no-ops. For circular buffer mode, it makes sure everything was drained out of the batch to free up the slots in the circular buffer. Batch is this kind of ephemeral thing that doesn't live beyond the export call. So in the above, it is safe to do. There is this IMemoryOwner we could use to transfer ownership to batch and let it handle the return. But that isn't available to net461/netstandard2.0 and it will force people to use pooling where they might not care so I'm not sure if the juice is worth the squeeze. |
Issue
@Yun-Ting is blocked by the
Batch<T>
API. My understanding is the ask is to be able to filter log records before export. We don't want to do this in a custom processor because that will add time when the logs are being written. We would like to do this on the export thread so it doesn't block normal processing.Changes
Batch<T>
already has a ctor that accepts an array, I'm proposing we make it public and addpublic int Count { get; }
to enable this type of logic:TODOs
CHANGELOG.md
updated for non-trivial changes