Skip to content

Commit 4bd7153

Browse files
committed
perf(sdk): add optional foldhash for ValueMap HashMaps in metrics hot path
1 parent 345cd74 commit 4bd7153

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

opentelemetry-sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- **Breaking** The SDK `testing` feature is now runtime agnostic. [#3407][3407]
66
- `TokioSpanExporter` and `new_tokio_test_exporter` have been renamed to `TestSpanExporter` and `new_test_exporter`.
77
- The following transitive dependencies and features have been removed: `tokio/rt`, `tokio/time`, `tokio/macros`, `tokio/rt-multi-thread`, `tokio-stream`, `experimental_async_runtime`
8+
- Add optional `metrics-use-foldhash` feature flag to use [foldhash](https://github.com/orlp/foldhash) for `ValueMap` HashMaps in the metrics hot path, providing ~30% improvement over the default std `SipHash` hasher. [#3388](https://github.com/open-telemetry/opentelemetry-rust/pull/3388)
89
- Add 32-bit platform support by using `portable-atomic` for `AtomicI64` and `AtomicU64` in the metrics module. This enables compilation on 32-bit ARM targets (e.g., `armv5te-unknown-linux-gnueabi`, `armv7-unknown-linux-gnueabihf`).
910
- `Aggregation` enum and `StreamBuilder::with_aggregation()` are now stable and no longer require the `spec_unstable_metrics_views` feature flag.
1011
- Fix `service.name` Resource attribute fallback to follow OpenTelemetry

opentelemetry-sdk/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ autobenches = false
1212

1313
[dependencies]
1414
opentelemetry = { workspace = true }
15+
foldhash = { version = "0.1", optional = true }
1516
opentelemetry-http = { workspace = true, optional = true }
1617
futures-channel = { workspace = true }
1718
futures-executor = { workspace = true }
@@ -60,6 +61,7 @@ experimental_logs_batch_log_processor_with_async_runtime = ["logs", "experimenta
6061
experimental_logs_concurrent_log_processor = ["logs"]
6162
experimental_trace_batch_span_processor_with_async_runtime = ["tokio/sync", "trace", "experimental_async_runtime"]
6263
experimental_metrics_disable_name_validation = ["metrics"]
64+
metrics-use-foldhash = ["metrics", "foldhash"]
6365
bench_profiling = []
6466

6567
[[bench]]

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use core::fmt;
99
#[cfg(not(target_has_atomic = "64"))]
1010
use portable_atomic::{AtomicI64, AtomicU64};
1111
use std::cmp::min;
12-
use std::collections::{HashMap, HashSet};
12+
use std::collections::HashSet;
1313
use std::mem::swap;
1414
use std::ops::{Add, AddAssign, DerefMut, Sub};
1515
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
@@ -24,6 +24,23 @@ use opentelemetry::{otel_warn, KeyValue};
2424
use super::data::{AggregatedMetrics, MetricData};
2525
use super::pipeline::DEFAULT_CARDINALITY_LIMIT;
2626

27+
#[cfg(feature = "metrics-use-foldhash")]
28+
type HashMap<K, V> = std::collections::HashMap<K, V, foldhash::fast::RandomState>;
29+
#[cfg(not(feature = "metrics-use-foldhash"))]
30+
type HashMap<K, V> = std::collections::HashMap<K, V>;
31+
32+
#[cfg(feature = "metrics-use-foldhash")]
33+
fn new_hashmap<K, V>(capacity: usize) -> HashMap<K, V> {
34+
std::collections::HashMap::with_capacity_and_hasher(
35+
capacity,
36+
foldhash::fast::RandomState::default(),
37+
)
38+
}
39+
#[cfg(not(feature = "metrics-use-foldhash"))]
40+
fn new_hashmap<K, V>(capacity: usize) -> HashMap<K, V> {
41+
std::collections::HashMap::with_capacity(capacity)
42+
}
43+
2744
// TODO Replace it with LazyLock once it is stable
2845
pub(crate) static STREAM_OVERFLOW_ATTRIBUTES: OnceLock<Vec<KeyValue>> = OnceLock::new();
2946

@@ -85,7 +102,7 @@ where
85102
{
86103
fn new(config: A::InitConfig, cardinality_limit: usize) -> Self {
87104
ValueMap {
88-
trackers: RwLock::new(HashMap::with_capacity(
105+
trackers: RwLock::new(new_hashmap(
89106
1 + min(DEFAULT_CARDINALITY_LIMIT, cardinality_limit),
90107
)),
91108
trackers_for_collect: OnceLock::new(),
@@ -100,7 +117,7 @@ where
100117
#[inline]
101118
fn trackers_for_collect(&self) -> &RwLock<HashMap<Vec<KeyValue>, Arc<A>>> {
102119
self.trackers_for_collect.get_or_init(|| {
103-
RwLock::new(HashMap::with_capacity(
120+
RwLock::new(new_hashmap(
104121
1 + min(DEFAULT_CARDINALITY_LIMIT, self.cardinality_limit),
105122
))
106123
})

0 commit comments

Comments
 (0)