More efficient prometheus metrics in Rust
https://github.com/conradludgate/measured
let m_sent = NUM_BYTES_PROXIED.with_label_values(&aux.traffic_labels("tx"));
let mut client = MeasuredStream::new(
client,
|cnt| m_sent.inc_by(cnt as u64),
);
#[derive(Debug, Deserialize, Clone, Default)]
pub struct MetricsAuxInfo {
pub endpoint_id: EndpointId,
pub project_id: ProjectId,
pub branch_id: BranchId,
}
impl MetricsAuxInfo {
pub fn traffic_labels(&self, direction: &'static str) -> [&str; 4] {
[direction, &self.project_id, &self.endpoint_id, &self.branch_id]
}
}
90000 entries
- 3 strings of 32 characters = 168 bytes
- 24 bytes needed for String, 32 bytes for alloc
- 1 arc of AtomicU64 = 32 bytes
90000 * (168 + 32) = 18MB
Timer precision: 10 ns
counters fastest │ slowest │ median │ mean
├─ measured 22.68 ns │ 27.49 ns │ 23.93 ns │ 23.9 ns
├─ measured_sparse 22.56 ns │ 148.5 ns │ 50.88 ns │ 61.48 ns
├─ metrics 508.3 ns │ 647.7 ns │ 605.7 ns │ 606.4 ns
├─ prometheus 1.547 µs │ 1.7 µs │ 1.657 µs │ 1.645 µs
╰─ prometheus_client 2.99 µs │ 3.38 µs │ 3.317 µs │ 3.262 µs
enum VecInner<U: Hash + Eq> {
Dense(Box<[OnceLock<AtomicU64>]>),
Sparse(DashMap<U, AtomicU64>),
}
enum VecInner<U: Hash + Eq> {
Dense(Box<[CachePadded<OnceLock<AtomicU64>>]>),
Sparse(DashMap<U, AtomicU64>),
}
Timer precision: 10 ns
counters fastest │ slowest │ median │ mean
├─ padded 67.26 ns │ 152.2 ns │ 142.6 ns │ 142.3 ns
╰─ unpadded 133.6 ns │ 278.8 ns │ 265.6 ns │ 263.1 ns
enum VecInner<U: Ord + Send> {
Dense(Dense),
Sparse(DashMap<U, AtomicU64>),
}
struct Dense {
size: usize,
sample: Mutex<Box<[Option<u64>]>>,
locals: ThreadLocal<Box<[OnceLock<AtomicU64>]>>,
}
Timer precision: 10 ns
counters fastest │ slowest │ median │ mean
├─ padded 67.26 ns │ 152.2 ns │ 142.6 ns │ 142.3 ns
╰─ local 22.68 ns │ 27.49 ns │ 23.93 ns │ 23.9 ns
#[derive(FixedCardinalityLabel, Copy, Clone)]
enum Operation {
Create,
Update,
Delete,
}
#[derive(LabelGroup)]
#[label(set = MyLabelGroupSet)]
struct MyLabelGroup {
operation: Operation,
}
#[derive(MetricGroup)]
#[metric(new())]
struct MyMetricGroup {
/// counts things
my_first_counter: CounterVec<MyLabelGroupSet>,
}
// create the metrics
let metrics = MyMetricGroup::new();
// increment the counter at a given label
metrics.my_first_counter.inc(MyLabelGroup { operation: Operation::Create });
metrics.my_first_counter.inc(MyLabelGroup { operation: Operation::Delete });
let mut text_encoder = BufferedTextEncoder::new();
metrics.collect_group_into(&mut text_encoder);
let bytes = text_encoder.finish();