Skip to content

Commit 465fcc2

Browse files
authored
AttributeSet cleanup, better perf for overflows (#2313)
1 parent af9d925 commit 465fcc2

File tree

3 files changed

+13
-63
lines changed

3 files changed

+13
-63
lines changed

opentelemetry-sdk/benches/metrics_counter.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
|--------------------------------|-------------|
1010
| Counter_Add_Sorted | 172 ns |
1111
| Counter_Add_Unsorted | 183 ns |
12-
| Counter_Overflow | 898 ns |
12+
| Counter_Overflow | 562 ns |
1313
| ThreadLocal_Random_Generator_5 | 37 ns |
1414
*/
1515

opentelemetry-sdk/src/metrics/internal/mod.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ pub(crate) use exponential_histogram::{EXPO_MAX_SCALE, EXPO_MIN_SCALE};
1818
use once_cell::sync::Lazy;
1919
use opentelemetry::{otel_warn, KeyValue};
2020

21-
use crate::metrics::AttributeSet;
22-
2321
pub(crate) static STREAM_OVERFLOW_ATTRIBUTES: Lazy<Vec<KeyValue>> =
2422
Lazy::new(|| vec![KeyValue::new("otel.metric.overflow", "true")]);
2523

@@ -95,7 +93,7 @@ where
9593
}
9694

9795
// Try to retrieve and update the tracker with the attributes sorted.
98-
let sorted_attrs = AttributeSet::from(attributes).into_vec();
96+
let sorted_attrs = sort_and_dedup(attributes);
9997
if let Some(tracker) = trackers.get(sorted_attrs.as_slice()) {
10098
tracker.update(value);
10199
return;
@@ -198,6 +196,16 @@ fn prepare_data<T>(data: &mut Vec<T>, list_len: usize) {
198196
}
199197
}
200198

199+
fn sort_and_dedup(attributes: &[KeyValue]) -> Vec<KeyValue> {
200+
// Use newly allocated vec here as incoming attributes are immutable so
201+
// cannot sort/de-dup in-place. TODO: This allocation can be avoided by
202+
// leveraging a ThreadLocal vec.
203+
let mut sorted = attributes.to_vec();
204+
sorted.sort_unstable_by(|a, b| a.key.cmp(&b.key));
205+
sorted.dedup_by(|a, b| a.key == b.key);
206+
sorted
207+
}
208+
201209
/// Marks a type that can have a value added and retrieved atomically. Required since
202210
/// different types have different backing atomic mechanisms
203211
pub(crate) trait AtomicTracker<T>: Sync + Send + 'static {

opentelemetry-sdk/src/metrics/mod.rs

+1-59
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,7 @@ pub use view::*;
7777
// #[cfg(not(feature = "spec_unstable_metrics_views"))]
7878
// pub(crate) use view::*;
7979

80-
use std::collections::hash_map::DefaultHasher;
81-
use std::collections::HashSet;
82-
use std::hash::{Hash, Hasher};
83-
84-
use opentelemetry::KeyValue;
80+
use std::hash::Hash;
8581

8682
/// Defines the window that an aggregation was calculated over.
8783
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
@@ -106,60 +102,6 @@ pub enum Temporality {
106102
LowMemory,
107103
}
108104

109-
/// A unique set of attributes that can be used as instrument identifiers.
110-
///
111-
/// This must implement [Hash], [PartialEq], and [Eq] so it may be used as
112-
/// HashMap keys and other de-duplication methods.
113-
#[derive(Clone, Default, Debug, PartialEq, Eq)]
114-
pub(crate) struct AttributeSet(Vec<KeyValue>, u64);
115-
116-
impl From<&[KeyValue]> for AttributeSet {
117-
fn from(values: &[KeyValue]) -> Self {
118-
let mut seen_keys = HashSet::with_capacity(values.len());
119-
let vec = values
120-
.iter()
121-
.rev()
122-
.filter_map(|kv| {
123-
if seen_keys.insert(kv.key.clone()) {
124-
Some(kv.clone())
125-
} else {
126-
None
127-
}
128-
})
129-
.collect::<Vec<_>>();
130-
131-
AttributeSet::new(vec)
132-
}
133-
}
134-
135-
fn calculate_hash(values: &[KeyValue]) -> u64 {
136-
let mut hasher = DefaultHasher::new();
137-
values.iter().fold(&mut hasher, |mut hasher, item| {
138-
item.hash(&mut hasher);
139-
hasher
140-
});
141-
hasher.finish()
142-
}
143-
144-
impl AttributeSet {
145-
fn new(mut values: Vec<KeyValue>) -> Self {
146-
values.sort_unstable_by(|a, b| a.key.cmp(&b.key));
147-
let hash = calculate_hash(&values);
148-
AttributeSet(values, hash)
149-
}
150-
151-
/// Returns the underlying Vec of KeyValue pairs
152-
pub(crate) fn into_vec(self) -> Vec<KeyValue> {
153-
self.0
154-
}
155-
}
156-
157-
impl Hash for AttributeSet {
158-
fn hash<H: Hasher>(&self, state: &mut H) {
159-
state.write_u64(self.1)
160-
}
161-
}
162-
163105
#[cfg(all(test, feature = "testing"))]
164106
mod tests {
165107
use self::data::{DataPoint, HistogramDataPoint, ScopeMetrics};

0 commit comments

Comments
 (0)