Vivek1106-04 commented on PR #57528:
URL: https://github.com/apache/spark/pull/57528#issuecomment-5170718701

   Both reproductions confirmed at d3be2ba389d, and they land on your numbers 
exactly. A reports a median of 57,393,120 and a threshold of 286,965,600, with 
19 of the 120 tied reducers under it. B reports 636,320,000 against 
754,350,000, all 200 missed.
   
    I don't think this can be fixed by rotating better, so rather than send 
another window schedule I want to make the case that the approach is wrong.
   
    In B the tie group is exactly twice the cap, so coverage is already a 
perfect 50% and stays 50% under any rotation policy, including a full-window 
stride. The miss doesn't come from coverage. It comes from avgSize: the 100 
ties each map task drops are folded into totalSmallBlockSize, lifting avgSize 
to 15,088, lifting the median reducer to 150,870,000, lifting the threshold to 
754,350,000. Recording more ties per map task raises the reported size of the 
tied reducers and the threshold they're compared against at the same time. No 
schedule closes that gap. A is a genuine coverage bug on top of it — a stride 
of one reaches only 100 + M - 1 ties — but fixing the stride alone leaves B 
failing.
   
   So I've stopped rationing the ties. They're all exactly skewCutoff, so the 
missing information is which reducers are at that size, not what their sizes 
are. That's one bitmap plus one byte: HighlyCompressedMapStatus now carries a 
run-optimized RoaringBitmap of the tied blocks alongside the existing 
emptyBlocks one, and the shared compressed cutoff size. Ties are excluded from 
the avgSize average, since they're recorded exactly and folding them in would 
only inflate the size reported for the blocks that really are average. The 
rotation, the tie counting pass and the mapTaskId-derived offset are gone; the 
id and size arrays still hold only the blocks strictly above the cutoff, which 
nthSmallest bounds.
   
   This does drop the per-status cap on tied blocks, which your P1 asked for 
and which you asked me to preserve here. That's deliberate — the cap is the 
thing that makes this unsolvable — but it's your call, so the cost is measured 
below rather than asserted.
   
    |                            | reproduction A | reproduction B |
    |----------------------------|----------------|----------------|
    | tied reducers / map tasks  | 120 / 10       | 200 / 10,000   |
    | real size per tied reducer | 524,288,000    | 1,024,000,000  |
    | reported, before           | 57,393,120     | 636,320,000    |
    | AQE threshold, before      | 286,965,600    | 754,350,000    |
    | detected, before           | 101 / 120      | 0 / 200        |
    | reported, after            | 550,087,940    | 1,121,770,000  |
    | AQE threshold, after       | 268,435,456    | 512,000,000    |
    | detected, after            | 120 / 120      | 200 / 200      |
   
    Retained driver heap, SizeEstimator over the 10,000 deserialized map 
statuses of a 2,001 partition stage, which is what your earlier P2 asked for:
   
    |                                        | bytes per map task | serialized |
    |----------------------------------------|--------------------|------------|
    | factor -1.0, before                    | 164                | 13 KiB     |
    | factor 5.0, before (100 ties recorded) | 668                | 27 KiB     |
    | factor -1.0, after                     | 172                | 15 KiB     |
    | factor 5.0, after (every tie recorded) | 308                | 15 KiB     |
   
   Recording all 200 ties now costs less than half of what recording 100 of 
them did. Two caveats. The disabled case grows 8 bytes per map task for the 
extra field; statuses with no ties share one never-mutated empty bitmap, which 
is what keeps it to that. And the bitmap's cost tracks partition count, not tie 
count: your 50,000-partition case from the first round, with 24,999 ties, is a 
single run and retains 448 bytes per status, but a maximally scattered tie set 
at that width would be closer to 6.25 KiB, which is worse than the capped 
array. Bounded by the partition count either way, which the id array was not.
   
   HighlyCompressedMapStatusBenchmark, back to back on the same machine, ns per 
block to build one status with sizes recorded:
   
   | partitions | skewed, before | skewed, after | organ pipe, before | organ 
pipe, after |
   
|------------|----------------|---------------|--------------------|-------------------|
   | 2,048      | 17,875         | 18,250        | 24,875             | 24,917  
          |
   | 10,000     | 56,750         | 55,833        | 105,208            | 100,333 
          |
   | 50,000     | 346,417        | 310,750       | 555,458            | 531,458 
          |
   
   Construction is flat to slightly faster — the tie counting pass is gone, 
which pays for the bitmap insertions — and the -1.0 cases are unchanged. 
getSizeForBlock gets faster, 4.7 to 3.9 ns per block at 2,048 partitions and 
3.9 to 3.0 at 50,000, since the ties leave the binary-searched array. Laptop, 
single run per case, so treat small differences as noise and the direction as 
the claim.
   
    On the tests, the two rounds of misses share a cause worth naming: they 
asserted the mechanism — coverage, and per-reducer error against a fixed target 
— rather than the property. Both sides of AQE's comparison come out of the map 
statuses, so a test that fixes the target can't see the threshold move. The 
replacement computes max(skewedPartitionThresholdInBytes, skewedPartitionFactor 
* medianReportedSize) from the statuses themselves and requires every skewed 
reducer to clear it, over three shapes: your A, your B, and the 
101-tie/many-mapper case. It fails on d3be2ba389d with
   
   106662602 was not greater than 286965600 reducer 1881 holds 524288000 bytes 
but is reported as 106662602, below the AQE skew threshold of 286965600, so it 
would never be split
   
    One thing that fell out of writing it: the previous rotation test used 
2,000 map tasks and 100 KiB tied blocks, so its tied reducers held 204,800,000 
bytes — below the 256 MiB floor. They'd never be split even if reported 
perfectly, so that test was never measuring AQE visibility at all. The new case 
uses 5,000 map tasks.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to