-
Notifications
You must be signed in to change notification settings - Fork 786
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
Use Volatile instead of Interlocked where appropriate #6051
base: main
Are you sure you want to change the base?
Conversation
@@ -394,7 +394,7 @@ internal void Update(long number) | |||
case AggregationType.LongSumIncomingCumulative: | |||
case AggregationType.LongGauge: | |||
{ | |||
Interlocked.Exchange(ref this.runningValue.AsLong, number); | |||
Volatile.Write(ref this.runningValue.AsLong, number); |
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.
curious, if this shows improvement in the metric stress tests?
@@ -135,7 +135,7 @@ internal void Update<T>(in ExemplarMeasurement<T> measurement) | |||
this.StoreRawTags(measurement.Tags); | |||
} | |||
|
|||
Interlocked.Exchange(ref this.isCriticalSectionOccupied, 0); | |||
Volatile.Write(ref this.isCriticalSectionOccupied, 0); |
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.
We need to consider the memory ordering guarantees of Volatile.Write
. With Interlocked
methods, the read/writes would not be moved before or after a given Interlocked
method.
With volatile writes, read/writes that happen after a given Volatile.Write
method can be moved before that Volatile.Write
method. We need to evaluate if that affects the correctness of our code. There are some write operations that we do after releasing the locks (for exemplar and MetricPoint updates):
- Call
OnCollected
for Exemplars which resets the internal measurement state - Update
MetricStatus
toCollectPending
when updatingMetricPoint
s
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 went over all uses of Interlocked
and also checked the code flow after these lock releases. I now found a few places where the current code incorrectly relies on the preceding interlocked operation for memory ordering, for example in case of MetricPoint.UpdateWithExemplar
the order of operations is currently:
Interlocked.Exchange(ref this.runningValue.AsLong, number); // full fence
this.UpdateExemplar(number, tags, offerExemplar); // could run arbitrary lock-free code, though in practice uses locks
this.MetricPointStatus = MetricPointStatus.CollectPending; // no memory ordering guarantees, could become observable before exemplar updates
With this PR:
Volatile.Write(ref this.runningValue.AsLong, number); // release
this.UpdateExemplar(number, tags, offerExemplar);
Volatile.Write(ref this.status, (byte)MetricPointStatus.CollectPending); // release, guarantees all exemplar updates become observable before
This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or pushing will instruct the bot to automatically remove the label. This bot runs once per day. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6051 +/- ##
==========================================
- Coverage 86.39% 86.39% -0.01%
==========================================
Files 257 257
Lines 11690 11668 -22
==========================================
- Hits 10100 10080 -20
+ Misses 1590 1588 -2
|
using OpenTelemetry.Internal; | ||
|
||
namespace OpenTelemetry.Metrics; | ||
|
||
/// <summary> | ||
/// Represents a metric data point. | ||
/// </summary> | ||
[StructLayout(LayoutKind.Auto)] |
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.
Using auto-layout together with the field type change for the two enums below reduces the struct size from 72 bytes to 64 bytes.
Volatile reads/writes are atomic and have acquire/release semantics, but are for the most part as fast as regular reads/writes. Any interlocked operation is at least 30-40 CPU cycles and needs exclusive cache line ownership, which is especially bad for reads.
Split off from #6048.