Skip to content

Commit b58b727

Browse files
[profiling] Only convert labels once per labelset (#1215)
Co-authored-by: realFlowControl <[email protected]>
1 parent 22af117 commit b58b727

File tree

2 files changed

+51
-21
lines changed

2 files changed

+51
-21
lines changed

datadog-profiling/src/internal/label.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ impl From<&Label> for prost_impls::Label {
7676

7777
impl From<Label> for datadog_profiling_protobuf::Label {
7878
fn from(label: Label) -> Self {
79+
Self::from(&label)
80+
}
81+
}
82+
83+
impl From<&Label> for datadog_profiling_protobuf::Label {
84+
fn from(label: &Label) -> Self {
7985
let (str, num, num_unit) = match label.value {
8086
LabelValue::Str(str) => (str, 0, StringOffset::ZERO),
8187
LabelValue::Num { num, num_unit } => (StringOffset::ZERO, num, num_unit),
@@ -125,10 +131,22 @@ pub struct LabelSet {
125131
}
126132

127133
impl LabelSet {
134+
pub fn is_empty(&self) -> bool {
135+
self.labels.is_empty()
136+
}
137+
128138
pub fn iter(&self) -> core::slice::Iter<'_, LabelId> {
129139
self.labels.iter()
130140
}
131141

142+
pub fn labels(&self) -> &[LabelId] {
143+
&self.labels
144+
}
145+
146+
pub fn len(&self) -> usize {
147+
self.labels.len()
148+
}
149+
132150
pub fn new(labels: Box<[LabelId]>) -> Self {
133151
// Once upon a time label ids were guaranteed to be sorted. However,
134152
// this makes testing difficult because the order of input labels and

datadog-profiling/src/internal/profile/mod.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -371,25 +371,50 @@ impl Profile {
371371
.as_nanos()
372372
.min(i64::MAX as u128) as i64;
373373

374+
let mut extended_label_sets: Vec<Vec<Label>> = Vec::with_capacity(self.label_sets.len());
375+
376+
for label_set in std::mem::take(&mut self.label_sets) {
377+
let endpoint_label = self.get_endpoint_for_label_set(&label_set)?;
378+
// Leave one space for the timestamp if needed
379+
let mut labels = Vec::with_capacity(
380+
label_set.len() + 1 + if endpoint_label.is_some() { 1 } else { 0 },
381+
);
382+
for l in label_set.iter() {
383+
labels.push(*self.get_label(*l)?);
384+
}
385+
if let Some(endpoint_label) = endpoint_label {
386+
labels.push(endpoint_label);
387+
}
388+
extended_label_sets.push(labels);
389+
}
390+
374391
for (sample, timestamp, mut values) in std::mem::take(&mut self.observations).into_iter() {
375-
let labels = self.enrich_sample_labels(sample, timestamp)?;
392+
let labels = &mut extended_label_sets[sample.labels.to_raw_id()];
376393
let location_ids: Vec<_> = self
377394
.get_stacktrace(sample.stacktrace)?
378395
.locations
379396
.iter()
380397
.map(Id::to_raw_id)
381398
.collect();
382399
self.check_location_ids_are_valid(&location_ids, self.locations.len())?;
383-
self.upscaling_rules.upscale_values(&mut values, &labels)?;
400+
self.upscaling_rules.upscale_values(&mut values, labels)?;
401+
402+
// Use the extra slot in the labels vector to store the timestamp without any reallocs.
403+
if let Some(ts) = timestamp {
404+
labels.push(Label::num(self.timestamp_key, ts.get(), StringId::ZERO))
405+
}
406+
let pprof_labels: Vec<_> = labels.iter().map(protobuf::Label::from).collect();
407+
if timestamp.is_some() {
408+
labels.pop();
409+
}
384410

385-
let labels: Vec<_> = labels.into_iter().map(protobuf::Label::from).collect();
386411
let item = protobuf::Sample {
387412
location_ids: Record::from(location_ids.as_slice()),
388413
values: Record::from(values.as_slice()),
389414
// SAFETY: converting &[Label] to &[Field<Label,..>] which is
390415
// safe, because Field is repr(transparent).
391416
labels: unsafe {
392-
&*(labels.as_slice() as *const [protobuf::Label]
417+
&*(pprof_labels.as_slice() as *const [protobuf::Label]
393418
as *const [Record<protobuf::Label, 3, NO_OPT_ZERO>])
394419
},
395420
};
@@ -679,16 +704,15 @@ impl Profile {
679704
.map(|v| Label::str(self.endpoints.endpoint_label, *v)))
680705
}
681706

682-
fn get_endpoint_for_labels(&self, label_set_id: LabelSetId) -> anyhow::Result<Option<Label>> {
683-
let label = self.get_label_set(label_set_id)?.iter().find_map(|id| {
707+
fn get_endpoint_for_label_set(&self, label_set: &LabelSet) -> anyhow::Result<Option<Label>> {
708+
if let Some(label) = label_set.iter().find_map(|id| {
684709
if let Ok(label) = self.get_label(*id) {
685710
if label.get_key() == self.endpoints.local_root_span_id_label {
686711
return Some(label);
687712
}
688713
}
689714
None
690-
});
691-
if let Some(label) = label {
715+
}) {
692716
self.get_endpoint_for_label(label)
693717
} else {
694718
Ok(None)
@@ -701,6 +725,7 @@ impl Profile {
701725
.context("LabelId to have a valid interned index")
702726
}
703727

728+
#[allow(dead_code)]
704729
fn get_label_set(&self, id: LabelSetId) -> anyhow::Result<&LabelSet> {
705730
self.label_sets
706731
.get_index(id.to_offset())
@@ -800,19 +825,6 @@ impl Profile {
800825
profile
801826
}
802827

803-
fn enrich_sample_labels(
804-
&self,
805-
sample: Sample,
806-
timestamp: Option<Timestamp>,
807-
) -> anyhow::Result<Vec<Label>> {
808-
self.get_label_set(sample.labels)?
809-
.iter()
810-
.map(|l| self.get_label(*l).copied())
811-
.chain(self.get_endpoint_for_labels(sample.labels).transpose())
812-
.chain(timestamp.map(|ts| Ok(Label::num(self.timestamp_key, ts.get(), StringId::ZERO))))
813-
.collect()
814-
}
815-
816828
#[cfg(debug_assertions)]
817829
fn validate_sample_labels(&mut self, sample: &api::Sample) -> anyhow::Result<()> {
818830
let mut seen: HashMap<&str, &api::Label> = HashMap::new();

0 commit comments

Comments
 (0)