datafusion_physical_expr_common/metrics/expression.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Metrics helpers for expression evaluation.
19
20use super::{ExecutionPlanMetricsSet, MetricBuilder, MetricType, ScopedTimerGuard, Time};
21
22/// Tracks evaluation time for a sequence of expressions.
23///
24/// # Example
25/// Given SQL query:
26/// EXPLAIN ANALYZE
27/// SELECT a+1, pow(a,2)
28/// FROM generate_series(1, 1000000) as t1(a)
29///
30/// This struct holds two time metrics for the projection expressions
31/// `a+1` and `pow(a,2)`, respectively.
32///
33/// The output reads:
34/// `ProjectionExec: expr=[a@0 + 1 as t1.a + Int64(1), power(CAST(a@0 AS Float64), 2) as pow(t1.a,Int64(2))], metrics=[... expr_0_eval_time=9.23ms, expr_1_eval_time=32.35ms...]`
35#[derive(Debug, Clone)]
36pub struct ExpressionEvaluatorMetrics {
37 expression_times: Vec<Time>,
38}
39
40impl ExpressionEvaluatorMetrics {
41 /// Create metrics for a collection of expressions.
42 ///
43 /// # Args
44 /// - metrics: see `MetricBuilder` for details.
45 /// - partition: see `MetricBuilder` for details.
46 /// - expression_labels: unique identifier for each metric, so that the metric
47 /// can get aggregated across multiple partitions. It is not the name showed
48 /// in the `EXPLAIN ANALYZE`, the metric name will be `expr_{idx}_eval_time`
49 /// according to the expression order.
50 pub fn new<T>(
51 metrics: &ExecutionPlanMetricsSet,
52 partition: usize,
53 expression_labels: impl IntoIterator<Item = T>,
54 ) -> Self
55 where
56 T: Into<String>,
57 {
58 let expression_times = expression_labels
59 .into_iter()
60 .enumerate()
61 .map(|(idx, label)| {
62 MetricBuilder::new(metrics)
63 .with_new_label("expr", label.into())
64 .with_type(MetricType::DEV)
65 // Existing PhysicalExpr formatter is a bit verbose, so use simple name
66 .subset_time(format!("expr_{idx}_eval_time"), partition)
67 })
68 .collect();
69
70 Self { expression_times }
71 }
72
73 /// Returns a timer guard for the expression at `index`, if present.
74 #[inline]
75 pub fn scoped_timer(&self, index: usize) -> Option<ScopedTimerGuard<'_>> {
76 self.expression_times.get(index).map(Time::timer)
77 }
78
79 /// The number of tracked expressions.
80 pub fn len(&self) -> usize {
81 self.expression_times.len()
82 }
83
84 /// True when no expressions are tracked.
85 pub fn is_empty(&self) -> bool {
86 self.expression_times.is_empty()
87 }
88}