Skip to content

Commit a732dc3

Browse files
authored
feat: add human-readable formatting to EXPLAIN ANALYZE metrics apache#18689 (apache#18734)
## Which issue does this PR close? <!-- We generally require a GitHub issue to be filed for all bug fixes and enhancements and this helps us generate change logs for our releases. You can link an issue to this PR using the GitHub syntax. For example `Closes #123` indicates that this PR will close issue #123. --> - Closes apache#18689 ## Rationale for this change <!-- Why are you proposing this change? If this is already explained clearly in the issue then this section is not needed. Explaining clearly why changes are proposed helps reviewers understand your changes and offer better suggestions for fixes. --> ## What changes are included in this PR? <!-- There is no need to duplicate the description in the issue here but it is sometimes worth providing a summary of the individual changes in this PR. --> ## Are these changes tested? <!-- We typically require tests for all PRs in order to: 1. Prevent the code from being accidentally broken by subsequent changes 2. Serve as another way to document the expected behavior of the code If tests are not included in your PR, please explain why (for example, are they covered by existing tests)? --> ## Are there any user-facing changes? <!-- If there are user-facing changes then we may require documentation to be updated before approving the PR. --> <!-- If there are any breaking changes to public APIs, please add the `api change` label. -->
1 parent 82181ac commit a732dc3

File tree

4 files changed

+259
-38
lines changed

4 files changed

+259
-38
lines changed

Cargo.lock

Lines changed: 26 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

datafusion/core/tests/physical_optimizer/filter_pushdown/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1824,7 +1824,7 @@ STORED AS PARQUET;
18241824
assert!(explain.contains("output_rows=128")); // Read 1 row group
18251825
assert!(explain.contains("t@0 < 1372708809")); // Dynamic filter was applied
18261826
assert!(
1827-
explain.contains("pushdown_rows_matched=128, pushdown_rows_pruned=99872"),
1827+
explain.contains("pushdown_rows_matched=128, pushdown_rows_pruned=99.87 K"),
18281828
"{explain}"
18291829
);
18301830
// Pushdown pruned most rows

datafusion/execution/src/memory_pool/mod.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,56 @@ pub fn human_readable_size(size: usize) -> String {
501501
format!("{value:.1} {unit}")
502502
}
503503

504+
/// Present count in human-readable form with K, M, B, T suffixes
505+
pub fn human_readable_count(count: usize) -> String {
506+
let count = count as u64;
507+
let (value, unit) = {
508+
if count >= 1_000_000_000_000 {
509+
(count as f64 / 1_000_000_000_000.0, " T")
510+
} else if count >= 1_000_000_000 {
511+
(count as f64 / 1_000_000_000.0, " B")
512+
} else if count >= 1_000_000 {
513+
(count as f64 / 1_000_000.0, " M")
514+
} else if count >= 1_000 {
515+
(count as f64 / 1_000.0, " K")
516+
} else {
517+
return count.to_string();
518+
}
519+
};
520+
521+
// Format with appropriate precision
522+
// For values >= 100, show 1 decimal place (e.g., 123.4 K)
523+
// For values < 100, show 2 decimal places (e.g., 10.12 K)
524+
if value >= 100.0 {
525+
format!("{value:.1}{unit}")
526+
} else {
527+
format!("{value:.2}{unit}")
528+
}
529+
}
530+
531+
/// Present duration in human-readable form with 2 decimal places
532+
pub fn human_readable_duration(nanos: u64) -> String {
533+
const NANOS_PER_SEC: f64 = 1_000_000_000.0;
534+
const NANOS_PER_MILLI: f64 = 1_000_000.0;
535+
const NANOS_PER_MICRO: f64 = 1_000.0;
536+
537+
let nanos_f64 = nanos as f64;
538+
539+
if nanos >= 1_000_000_000 {
540+
// >= 1 second: show in seconds
541+
format!("{:.2}s", nanos_f64 / NANOS_PER_SEC)
542+
} else if nanos >= 1_000_000 {
543+
// >= 1 millisecond: show in milliseconds
544+
format!("{:.2}ms", nanos_f64 / NANOS_PER_MILLI)
545+
} else if nanos >= 1_000 {
546+
// >= 1 microsecond: show in microseconds
547+
format!("{:.2}µs", nanos_f64 / NANOS_PER_MICRO)
548+
} else {
549+
// < 1 microsecond: show in nanoseconds
550+
format!("{nanos}ns")
551+
}
552+
}
553+
504554
#[cfg(test)]
505555
mod tests {
506556
use super::*;
@@ -597,4 +647,57 @@ mod tests {
597647
assert_eq!(r2.size(), 25);
598648
assert_eq!(pool.reserved(), 28);
599649
}
650+
651+
#[test]
652+
fn test_human_readable_count() {
653+
// Test small numbers (< 1000) - should display as-is
654+
assert_eq!(human_readable_count(0), "0");
655+
assert_eq!(human_readable_count(1), "1");
656+
assert_eq!(human_readable_count(999), "999");
657+
658+
// Test thousands (K)
659+
assert_eq!(human_readable_count(1_000), "1.00 K");
660+
assert_eq!(human_readable_count(10_100), "10.10 K");
661+
assert_eq!(human_readable_count(1_532), "1.53 K");
662+
assert_eq!(human_readable_count(99_999), "100.00 K");
663+
664+
// Test millions (M)
665+
assert_eq!(human_readable_count(1_000_000), "1.00 M");
666+
assert_eq!(human_readable_count(1_532_000), "1.53 M");
667+
assert_eq!(human_readable_count(99_000_000), "99.00 M");
668+
assert_eq!(human_readable_count(123_456_789), "123.5 M");
669+
670+
// Test billions (B)
671+
assert_eq!(human_readable_count(1_000_000_000), "1.00 B");
672+
assert_eq!(human_readable_count(1_532_000_000), "1.53 B");
673+
assert_eq!(human_readable_count(999_999_999_999), "1000.0 B");
674+
675+
// Test trillions (T)
676+
assert_eq!(human_readable_count(1_000_000_000_000), "1.00 T");
677+
assert_eq!(human_readable_count(42_000_000_000_000), "42.00 T");
678+
}
679+
680+
#[test]
681+
fn test_human_readable_duration() {
682+
// Test nanoseconds (< 1µs)
683+
assert_eq!(human_readable_duration(0), "0ns");
684+
assert_eq!(human_readable_duration(1), "1ns");
685+
assert_eq!(human_readable_duration(999), "999ns");
686+
687+
// Test microseconds (1µs to < 1ms)
688+
assert_eq!(human_readable_duration(1_000), "1.00µs");
689+
assert_eq!(human_readable_duration(1_234), "1.23µs");
690+
assert_eq!(human_readable_duration(999_999), "1000.00µs");
691+
692+
// Test milliseconds (1ms to < 1s)
693+
assert_eq!(human_readable_duration(1_000_000), "1.00ms");
694+
assert_eq!(human_readable_duration(11_295_377), "11.30ms");
695+
assert_eq!(human_readable_duration(1_234_567), "1.23ms");
696+
assert_eq!(human_readable_duration(999_999_999), "1000.00ms");
697+
698+
// Test seconds (>= 1s)
699+
assert_eq!(human_readable_duration(1_000_000_000), "1.00s");
700+
assert_eq!(human_readable_duration(1_234_567_890), "1.23s");
701+
assert_eq!(human_readable_duration(42_000_000_000), "42.00s");
702+
}
600703
}

0 commit comments

Comments
 (0)