[SPARK-58201][SQL] Optimize sliding window MIN/MAX aggregate function using monotonic deque#57346
[SPARK-58201][SQL] Optimize sliding window MIN/MAX aggregate function using monotonic deque#57346pavan51 wants to merge 2 commits into
Conversation
…s using monotonic deque
7b7ba70 to
1691d2d
Compare
| * using monotonic deques. This provides O(N) time complexity instead of O(N * W) of | ||
| * [[SlidingWindowFunctionFrame]] or O(N log W) of [[SegmentTreeWindowFunctionFrame]]. | ||
| */ | ||
| private[window] final class SlidingWindowMinMaxFunctionFrame( |
There was a problem hiding this comment.
This adds a new default-on execution path with no correctness test. I think it might be worth considering adding a differential test.
There was a problem hiding this comment.
Thanks for the suggestion! I've added a new differential correctness test suite (MonotonicDequeWindowFunctionSuite ) verifying that the Monotonic Deque matches both the Segment Tree and Naive baselines across primitives, reference types, range/row frames, and null values.
| case BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType | DoubleType | | ||
| DateType | TimestampType | TimestampNTZType => true |
There was a problem hiding this comment.
YearMonthIntervalType (Int) and DayTimeIntervalType (Long) are also primitive-backed and valid for MIN/MAX. Suggested Performance Fix:
| case BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType | DoubleType | | |
| DateType | TimestampType | TimestampNTZType => true | |
| case BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType | DoubleType | | |
| DateType | TimestampType | TimestampNTZType | _: YearMonthIntervalType | | |
| _: DayTimeIntervalType => true |
|
Can you PR title complete? |
4ad13b9 to
3c3a8a3
Compare
Updated the title of the PR with complete info |
…terval primitives
3c3a8a3 to
5fb7ba8
Compare
What changes were proposed in this pull request?
This PR proposes to optimize sliding window
MIN/MAXaggregate functions using a monotonic deque implementation (SlidingWindowMinMaxFunctionFrame).Key improvements:
MINand/orMAX, we maintain a double-ended queue (deque) of(value, index)pairs for each function. New values are admitted and elements that fall outside the sliding window boundary are dropped from the front in amortizedUnsafeRow) recycle memory, meaning reference types (like String, Decimal, Array) must be copied to avoid corruption. However, JVM primitives (like Int, Long, Double, Float, Boolean, Date, Timestamp) are passed by value and are immune to this. We introduce a type check that skips heap allocation/copying entirely for primitive types.WindowEvaluatorFactoryBaseto detect when the query uses onlyMIN/MAXand automatically instantiateSlidingWindowMinMaxFunctionFrame.spark.sql.window.monotonicDeque.enabled(default:true) to allow toggling this optimization.Why are the changes needed?
Currently, sliding window aggregates in Spark are executed using:
SlidingWindowFunctionFrame): Re-scans all rows in the sliding window buffer on every row, resulting inSegmentTreeWindowFunctionFrame): Built over partition blocks, offeringWhile the Segment Tree is an improvement over Naive for large windows, it has three major deficiencies:
By implementing a monotonic deque, we achieve true linear$O(N)$ time complexity (amortized $O(1)$ operations per row regardless of window size) and a minimal memory footprint.
As a result:
Does this PR introduce any user-facing change?
No user-facing behavior changes. It is a purely physical plan performance optimization.
There is a new configuration:
spark.sql.window.monotonicDeque.enabled(Default:true) to enable/disable the monotonic deque optimization.How was this patch tested?
SegmentTreeWindowFunctionSuiteandDataFrameWindowFramesSuite(all 80 tests succeeded), and added a new differential correctness suiteMonotonicDequeWindowFunctionSuiteverifying monotonic deque outputs against Naive and Segment Tree baselines across multiple data types (including date/timestamp/interval types), range/row frames, and null values.WindowBenchmarkmatch the baseline exactly.WindowBenchmarksuite.The updated benchmark results under
OpenJDK 23.0.1 on Mac OS X 15.1.1 (Apple M1)are:MIN Sliding Window (W=1001, 256K rows)
MAX Sliding Window (W=1001, 256K rows)
MIN Sliding Window Scaling (W=100001, 2M rows)
MIN/MAX Small Window Scaling (W=11, 2M rows - Pareto Loss Zone Check)
In small windows, the naive sliding window scan O(W) is fast, whereas the segment tree has high setup and block query overhead O(log W), causing it to be slower than naive. Monotonic Deque dominates both.
Was this patch authored or co-authored using generative AI tooling?
Yes, co-authored by Google DeepMind Coding Assistant