[SPARK-48290][CORE] Record skewed shuffle block sizes by default so that AQE can detect skew#57528
Open
Vivek1106-04 wants to merge 1 commit into
Open
[SPARK-48290][CORE] Record skewed shuffle block sizes by default so that AQE can detect skew#57528Vivek1106-04 wants to merge 1 commit into
Vivek1106-04 wants to merge 1 commit into
Conversation
…hat AQE can detect skew Once a shuffle has more than spark.shuffle.minNumPartitionsToHighlyCompress (2000) partitions, map statuses switch to HighlyCompressedMapStatus, which reports every block below spark.shuffle.accurateBlockThreshold as the average block size. A skewed partition whose rows are spread over many map tasks has no individual block above that threshold, so its size is averaged away, adaptive query execution sees min == median == max and the skew is left unhandled. spark.shuffle.accurateBlockSkewedFactor, added by SPARK-36967, keeps the sizes of skewed blocks accurate, but it shipped disabled because enabling it sorted the block sizes in every map task. Only two order statistics are needed, so they are now selected in O(numPartitions) instead of sorting, and the feature is enabled by default with the value its documentation already recommended.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes were proposed in this pull request?
Once a shuffle has more than
spark.shuffle.minNumPartitionsToHighlyCompress(2000) partitions, map statuses switch toHighlyCompressedMapStatus, which keeps exact sizes only for blocks abovespark.shuffle.accurateBlockThreshold(100MB) and reports the average block size for everything else.MapOutputTracker.getStatisticssums those per reduce id, so AQE ends up looking at a flat distribution.spark.shuffle.accurateBlockSkewedFactor, added by SPARK-36967, keeps skewed block sizes accurate, but it ships disabled (-1.0) even though its own documentation recommends setting it to the same value asspark.sql.adaptive.skewJoin.skewedPartitionFactor. Enabling it fully sorted the block sizes in every map task, which is presumably why it was left off.This PR:
HighlyCompressedMapStatus.applywith selection of the only two order statistics it needs (the median, and the K-th largest size), using a new in-place quickselectUtils.nthSmallestplusUtils.medianInPlace. This is O(numPartitions) rather than O(numPartitions log numPartitions), with the same result;spark.shuffle.accurateBlockSkewedFactorfrom-1.0to5.0, matchingspark.sql.adaptive.skewJoin.skewedPartitionFactor.-1.0still disables the feature;Why are the changes needed?
Skew join optimization silently stops working above 2000 shuffle partitions. In the reporter's case a 263GB partition spread over 10335 mappers is only ~25MB per block, far below the 100MB accurate threshold, so every block is reported as the average and AQE logs
median size == max size == min sizeand finds no skew. Loweringspark.shuffle.accurateBlockThresholddoes not help, because what matters is the per-block size rather than the per-partition total.Reproduced on master with a join where 95% of the rows land on one key, varying only
spark.sql.shuffle.partitions:spark.sql.shuffle.partitionsSortMergeJoin(skew=true)spark.shuffle.accurateBlockSkewedFactor=5.0spark.shuffle.minNumPartitionsToHighlyCompress=100000Either knob restores detection on its own, which locates the problem in the map status compression rather than in AQE.
The cost of the change, measured with the benchmark added here (per map task, Apple M4, JDK 21):
At 50000 partitions the accurate path is 4.1x cheaper than it was, and the remaining overhead over not recording skewed sizes at all is 0.31ms per map task. On the driver side each map status carries at most
spark.shuffle.maxAccurateSkewedBlockNumber(100) extra entries of an int and a byte, so about 500 bytes per map task, or ~5MB for a 10000 map task stage.Does this PR introduce any user-facing change?
Yes. Shuffles with more than
spark.shuffle.minNumPartitionsToHighlyCompresspartitions now report skewed block sizes accurately by default, so AQE can detect and split skewed partitions where it previously could not. Queries affected by this will change plans (skew joins get split) and should run faster. Settingspark.shuffle.accurateBlockSkewedFactor=-1.0restores the old behavior.How was this patch tested?
MapStatusSuiteasserting that a skewed block belowspark.shuffle.accurateBlockThresholdis recorded accurately, and that the average is not inflated by it, using default configuration. It fails on master with88733 did not equal 1500.UtilsSuitecheckingUtils.nthSmallestagainst a full sort over sorted, reverse sorted, constant, duplicate-heavy and random inputs, its argument validation, and thatUtils.medianInPlaceagrees withUtils.median.HighlyCompressedMapStatusBenchmarkwith results generated at 2048, 10000 and 50000 partitions.MapStatusSuite,MapOutputTrackerSuite,ShuffleSuite,UtilsSuite,org.apache.spark.shuffle.sort.*(158 tests) andAdaptiveQueryExecSuite(129 tests) all pass.I did not add an AQE level test for the >2000 partition case:
spark.shuffle.minNumPartitionsToHighlyCompressis read through a lazy val on the JVM'sSparkEnv, so it cannot be lowered from within a suite, and running a query with 2001 shuffle partitions takes about a minute. SPARK-36967 tested this feature atMapStatusSuitelevel for the same reason. Happy to add one if reviewers would rather pay the runtime.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code