Skip to content

Add ExplainMemory Trait for Memory Usage Reporting and Enhance Memory Debugging Tools - #16926

Closed
kosiew wants to merge 0 commit into
apache:mainfrom
kosiew:memory2-16984
Closed

kosiew wants to merge 0 commit into
apache:mainfrom
kosiew:memory2-16984

Conversation

@kosiew

@kosiew kosiew commented Jul 26, 2025 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

Datafusion’s memory management intentionally does not track every allocation. The MemoryPool docs explain that only “large” consumers (like grouping or joining) register allocations, while transient buffers are not tracked. This keeps memory accounting simpler and avoids overhead:

/// Rather than tracking all allocations, DataFusion takes a pragmatic approach:
/// Intermediate memory used as data streams through the system is not accounted
/// (it assumed to be "small") but the large consumers of memory must register
/// and constrain their use. This design trades off the additional code
/// complexity of memory tracking with limiting resource usage.

Therefore, DataFusion already has abstractions for tracking memory per consumer.

This PR leverages existing MemoryReservation sizes and report_top to implement debugging helpers that show how much memory each accumulator or structure uses by introducing a standardized way to inspect and debug memory usage across various components of the execution engine. The ExplainMemory trait provides a human-readable summary of memory usage, which is helpful for debugging and monitoring purposes. This also lays the groundwork for improving visibility into memory consumption patterns in complex queries.

What changes are included in this PR?

  • Introduced a new module memory_report implementing the ExplainMemory trait.
  • Blanket implementation of ExplainMemory for any Accumulator using its size() method.
  • Specific implementation of ExplainMemory for MemoryReservation.
  • A helper function report_top_consumers that attempts to downcast a memory pool and extract consumer memory usage stats.
  • Unit tests verifying the behavior of the ExplainMemory trait.
  • Implemented ExplainMemory for GroupedHashAggregateStream to provide detailed insight into memory usage of group aggregates.
  • Enabled lz4 and zstd features in datafusion/physical-plan to allow test coverage of compression-related functionality.

Are these changes tested?

Yes:

  • Added unit tests for both MemoryReservation and Accumulator implementations of ExplainMemory.
  • Ensured the GroupedHashAggregateStream's memory explanation logic compiles and integrates cleanly.
  • Existing and new tests validate the memory reporting logic.

Are there any user-facing changes?

Yes:

  • Users can now call .explain_memory() on memory reservations and accumulators to obtain human-readable memory usage summaries.
  • Enhanced memory reporting visibility for GroupedHashAggregateStream, aiding debugging and optimization efforts.

@github-actions github-actions Bot added execution Related to the execution crate physical-plan Changes to the physical-plan crate labels Jul 26, 2025
@kosiew kosiew changed the title Memory2 16984 Add ExplainMemory Trait for Memory Usage Reporting and Enhance Memory Debugging Tools Jul 26, 2025

@rluvaton rluvaton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some thoughts, thank you for opening the PR!

}
}

impl<T: Accumulator + ?Sized> ExplainMemory for T {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this will cause rust to disallow you to implement it on other trait.

As I wouldn't be able to do:

trait MyTrait {}

impl<T: MyTrait> ExplainMemory for T {
  // ...
}

As rust will say to me that MyTrait can implement Accumulator

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @rluvaton! 👋

You're totally right to be cautious — that blanket impl of ExplainMemory for T where T: Accumulator will indeed block any other downstream impls of ExplainMemory for overlapping traits like T: MyTrait, due to Rust's coherence and orphan rules.

💡 Workaround: You can safely achieve what you want using a newtype pattern. That way, you avoid trait conflicts while still providing a blanket impl.

Here’s a minimal example:

// Newtype wrapper
struct AccumExplainer<T: Accumulator>(T);

// Blanket impl only applies to the wrapper, not all T
impl<T: Accumulator> ExplainMemory for AccumExplainer<T> {
    fn explain(&self) -> String {
        format!("Memory usage: {}", self.0.accumulate())
    }
}

// Convenience (optional)
impl<T: Accumulator> From<T> for AccumExplainer<T> {
    fn from(inner: T) -> Self {
        Self(inner)
    }
}

You can now wrap any Accumulator type like this:

fn explain_it<T: Accumulator>(thing: T) {
    let wrapped = AccumExplainer::from(thing);
    println!("{}", wrapped.explain());
}

This gives you a clean, conflict-free path to implement ExplainMemory broadly without locking out other impls for T in the future.

/// ```
pub trait ExplainMemory {
/// Returns a human readable string describing memory usage.
fn explain_memory(&self) -> Result<String>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should have something similar to formatter? So we will have easier to read memory report?

/// reservation.try_grow(256).unwrap();
/// println!("{}", reservation.explain_memory().unwrap());
/// ```
pub trait ExplainMemory {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should include size as well so any trait that need to report the size will also can report the breakdown of the size.

@github-actions github-actions Bot added documentation Improvements or additions to documentation logical-expr Logical plan and expressions core Core DataFusion crate sqllogictest SQL Logic Tests (.slt) common Related to common crate labels Aug 3, 2025
@kosiew kosiew closed this Aug 3, 2025
@kosiew

kosiew commented Aug 3, 2025

Copy link
Copy Markdown
Contributor Author

closed by accident.

@kosiew

kosiew commented Aug 3, 2025

Copy link
Copy Markdown
Contributor Author

Reopening..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common Related to common crate core Core DataFusion crate documentation Improvements or additions to documentation execution Related to the execution crate logical-expr Logical plan and expressions physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a fine grain memory usage tracking / break down of memory usage *within* each operator

2 participants