Skip to content

Commit 3c70c21

Browse files
committed
Avoid intermediate string allocation for label values
1 parent 3a4b942 commit 3c70c21

3 files changed

Lines changed: 53 additions & 22 deletions

File tree

datafusion/datasource-parquet/src/metrics.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use std::sync::Arc;
19+
1820
use datafusion_physical_plan::metrics::{
1921
Count, ExecutionPlanMetricsSet, Gauge, Label, MetricBuilder, MetricCategory,
2022
MetricType, PruningMetrics, RatioMergeStrategy, RatioMetrics, Time,
@@ -102,7 +104,7 @@ impl ParquetFileMetrics {
102104
) -> Self {
103105
// Share the filename label across all per-file metrics to avoid
104106
// allocating the same filename string for each metric.
105-
let filename_label = Label::new("filename", filename.to_string());
107+
let filename_label = Label::new("filename", Arc::<str>::from(filename));
106108
let builder = MetricBuilder::new(metrics).with_label(filename_label);
107109

108110
// -----------------------

datafusion/physical-expr-common/src/metrics/builder.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ use crate::metrics::{
2525
};
2626

2727
use super::{
28-
Count, ExecutionPlanMetricsSet, Gauge, Label, Metric, MetricValue, Time, Timestamp,
28+
Count, ExecutionPlanMetricsSet, Gauge, Label, LabelValue, Metric, MetricValue, Time,
29+
Timestamp,
2930
};
3031

3132
/// Structure for constructing metrics, counters, timers, etc.
@@ -110,7 +111,10 @@ impl<'a> MetricBuilder<'a> {
110111
name: impl Into<Cow<'static, str>>,
111112
value: impl Into<Cow<'static, str>>,
112113
) -> Self {
113-
self.with_label(Label::new(name.into(), value.into()))
114+
self.with_label(Label::new(
115+
LabelValue::from(name.into()),
116+
LabelValue::from(value.into()),
117+
))
114118
}
115119

116120
/// Set the partition of the metric being constructed

datafusion/physical-expr-common/src/metrics/mod.rs

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,9 @@ pub struct Label {
532532

533533
impl Label {
534534
/// Create a new [`Label`]
535-
pub fn new(
536-
name: impl Into<Cow<'static, str>>,
537-
value: impl Into<Cow<'static, str>>,
538-
) -> Self {
539-
let name = LabelValue::from(name.into());
540-
let value = LabelValue::from(value.into());
535+
pub fn new(name: impl Into<LabelValue>, value: impl Into<LabelValue>) -> Self {
536+
let name = name.into();
537+
let value = value.into();
541538
Self { name, value }
542539
}
543540

@@ -558,32 +555,55 @@ impl Display for Label {
558555
}
559556
}
560557

561-
/// Internal representation for label names and values.
558+
/// A label name or value.
562559
///
563-
/// `Static` preserves the existing allocation-free path for string literals.
564-
/// `Shared` stores dynamic strings behind [`Arc<str>`], so cloning a [`Label`]
565-
/// only increments an atomic reference count and does not allocate or copy the
566-
/// underlying string data.
567-
#[derive(Clone, Eq)]
568-
enum LabelValue {
560+
/// String literals preserve the existing allocation-free path. Dynamic strings
561+
/// can be stored behind [`Arc<str>`], so cloning a [`Label`] only increments an
562+
/// atomic reference count and does not allocate or copy the underlying string
563+
/// data.
564+
#[derive(Clone)]
565+
pub struct LabelValue(LabelValueInner);
566+
567+
/// Internal representation for label names and values.
568+
#[derive(Clone)]
569+
enum LabelValueInner {
569570
Static(&'static str),
570571
Shared(Arc<str>),
571572
}
572573

573574
impl LabelValue {
574-
fn as_str(&self) -> &str {
575-
match self {
576-
Self::Static(value) => value,
577-
Self::Shared(value) => value.as_ref(),
575+
/// Return this label value as a string slice.
576+
pub fn as_str(&self) -> &str {
577+
match &self.0 {
578+
LabelValueInner::Static(value) => value,
579+
LabelValueInner::Shared(value) => value.as_ref(),
578580
}
579581
}
580582
}
581583

584+
impl From<&'static str> for LabelValue {
585+
fn from(value: &'static str) -> Self {
586+
Self(LabelValueInner::Static(value))
587+
}
588+
}
589+
590+
impl From<String> for LabelValue {
591+
fn from(value: String) -> Self {
592+
Self(LabelValueInner::Shared(Arc::from(value)))
593+
}
594+
}
595+
596+
impl From<Arc<str>> for LabelValue {
597+
fn from(value: Arc<str>) -> Self {
598+
Self(LabelValueInner::Shared(value))
599+
}
600+
}
601+
582602
impl From<Cow<'static, str>> for LabelValue {
583603
fn from(value: Cow<'static, str>) -> Self {
584604
match value {
585-
Cow::Borrowed(value) => Self::Static(value),
586-
Cow::Owned(value) => Self::Shared(Arc::from(value)),
605+
Cow::Borrowed(value) => value.into(),
606+
Cow::Owned(value) => value.into(),
587607
}
588608
}
589609
}
@@ -594,6 +614,8 @@ impl PartialEq for LabelValue {
594614
}
595615
}
596616

617+
impl Eq for LabelValue {}
618+
597619
impl Hash for LabelValue {
598620
fn hash<H: Hasher>(&self, state: &mut H) {
599621
self.as_str().hash(state);
@@ -670,9 +692,12 @@ mod tests {
670692
fn test_label_owned_and_borrowed_values_are_equal() {
671693
let borrowed = Label::new("foo", "bar");
672694
let owned = Label::new("foo".to_string(), "bar".to_string());
695+
let shared = Label::new("foo", Arc::<str>::from("bar"));
673696

674697
assert_eq!(borrowed, owned);
698+
assert_eq!(borrowed, shared);
675699
assert_eq!(borrowed.to_string(), owned.to_string());
700+
assert_eq!(borrowed.to_string(), shared.to_string());
676701
}
677702

678703
#[test]

0 commit comments

Comments
 (0)