Filling the empty slot made the cache longer
Part nine sorted requests by length and took padding waste from 47.4% to
0.7%. Its closing paragraph noted that sorting is a one-shot arrangement: when
requests finish at different times, the seat a short one vacates stays empty.
Filling that seat immediately is continuous batching.
Two things went unmeasured there. What continuous batching actually buys, and how request lengths are distributed in the first place. Part nine used lengths uniform from 1 to 128, which I chose rather than measured.
Measure the lengths first
Ask the model. Draw a prompt of 8 to 64 characters from the validation split, let it continue, stop at a newline. Each request gets its own random seed so its length is the same whichever batch it lands in - otherwise changing the schedule would change what is being scheduled.
256 requests, stop at newline, cap 64
mean 37.6 median 37 min 1 max 64 sd 21.3
10th pct 8
25th pct 19
50th pct 37
75th pct 64
hit the cap 69
Not uniform. Half are under 37 characters and 27% run all the way to the cap - the bunched-at-the-short-end shape with a long tail that part nine guessed at.
Part nine’s handle does not turn here
Prompt length is known the moment a request arrives. Generation length is known
only when it is over. The correlation between them is -0.076.
So sorting by prompt length removes none of the seat waste.
steps row-steps row occ cache occ mean cache
static, arrival 512 16,384 58.8% 61.7% 93.3
static, by prompt 512 16,384 58.8% 82.3% 70.0
static, by gen len 332 10,624 90.6% 67.7% 87.9
continuous, arrival 336 9,630 100.0% 56.2% 104.1
continuous, by prompt 339 9,630 100.0% 70.8% 87.2
The first two rows have identical row-steps. 512 = 8 x 64: the maximum in all
eight groups was exactly 64. With 69 of 256 requests hitting the cap, any 32
you grab almost certainly contains one, and reordering does not change that.
Handing the sort to an oracle that knows the generation length gets 90.6%. What
part nine had was not an algorithm but information - prompt length is in your
hand on arrival and generation length is not.
Not leaving the seat empty
If you cannot sort, go the other way. Instead of waiting for the whole group, admit the next queued request the instant one finishes.
Row-steps fall from 16,384 to 9,630, steps from 512 to 336.
The 100.0% occupancy is a definition, not a discovery: a schedule that drops a
finished row from the tensor immediately cannot waste rows. The numbers beside it
are the ones worth reading.
The seat is paid for out of the cache
In the mean-cache column, continuous batching is the longest at 104.1 -
longer than static in arrival order at 93.3.
Of course it is. Continuous batching deliberately parks a freshly admitted short request next to a long-running one, and if the cache is one dense tensor its length is set by the longest occupant. That is where the seat is paid for.
Row-steps fell 41%, but what attention actually reads - the sum of
rows x cache - only fell 34%, from 1,528,896 to 1,009,601. Seven points
leaked into the cache.
Converting to time
Measuring one step across a grid of row counts and cache lengths and fitting
t = a + b·R + d·R·C:
a = 569 us fixed, per step
b = 7.68 us per row
d = 0.1015 us per row x cache slot
Worst residual over the 3 x 3 grid is 2.6%. At 32 rows and cache 104 that is
49% fixed, 21% on rows, 29% attention.
Pricing five schedules with that, against 41 rounds of paired measurement:
predicted measured 25th ~ 75th
static, arrival 1.00 1.00
static, by prompt 1.07 1.06 0.94 ~ 1.13
static, by gen len 1.57 1.57 1.49 ~ 1.66
continuous, arrival 1.56 1.58 1.50 ~ 1.69
continuous, by prompt 1.64 1.65 1.54 ~ 1.78
The sorting row straddles 1.0, buried by going through a common baseline. So
the two configurations were divided against each other directly instead, in the
same round, alternating which ran first, 61 times:
static, by prompt / arrival 1.077 quartiles 1.036 ~ 1.127 won 54 of 61
continuous, by prompt / arrival 1.057 quartiles 0.992 ~ 1.102 won 44 of 61
Three things fall out.
One. Sorting by prompt length is 1.08. It lifted cache occupancy a full 20
points, 61.7% to 82.3%, and bought 8% of the time - because attention is only
29% of a step. Part nine’s “removing the waste does not halve the time” is not
close to a half here.
Two. Continuous batching alone is 1.58, effectively the oracle’s 1.57. It
delivers what knowing the unknowable would have delivered, while knowing nothing.
Three. Together they are 1.65. Laying the sort on top of continuous batching
adds 1.06 - small, 44 rounds of 61, but one-sided. They do not overlap: sorting
fixes padding in the cache dimension, continuous batching fixes it in the row
dimension. Continuous plus prompt sort has the smallest rows x cache total of
the five, 801,245.
One place this disagrees with part nine
Part nine measured the cache going from 1 to 128 at batch 32 as 675 us of
added time and reported 48.3% of a step as length-dependent. This grid fit puts
the same span at 412 us and 33.8%.
Same laptop, same model, fifteen points apart. Part nine was a single sweep over cache length (its table even dips going from 16 to 32), while this is a plane fit to a 3 x 3 grid measured round-robin 50 times. I trust the second more, but the honest conclusion is that this machine cannot say whether the fraction is in the thirties or the forties. That is why the speedup table above reports measured paired ratios rather than the prediction.
What is left
27% of requests hit the cap at 64. The real tail is cut off, and had it not been, static batching would have looked worse - the cut favours continuous batching, so the numbers above are the conservative ones.
Holding the cache as one dense tensor is also a choice. Split it into fixed-size blocks and each row can hold only what it needs, and the price continuous batching pays here disappears. This part did not go there.
The priced absolute times land 24~29% below the measured ones. All five miss in
the same direction by about the same amount, so the ratios get used and the
absolute times do not.
The day this table was first measured, the same trace came out at 1,581 ms.
Today it is 461 ms. Same code, same data, a factor of 3.4 - the laptop was busy
with other things that day. The speedup ratio that day was also 1.58. Only the
numbers divided within a round survived; had the absolute times been the result,
every one of them would have been wrong.
So
- Generation length is not predictable from prompt length. Correlation
-0.076 - So sorting by prompt length does not move row occupancy off
58.8%at all. The maximum in all eight groups was64 - An oracle sorting by generation length reaches
90.6%. What part nine held was information - Continuous batching makes row occupancy
100%, which is a definition. The step count,512 -> 336, is the real number - The seat is paid for out of the cache: mean cache
93.3 -> 104.1, the longest of the five - Row-steps fall 41% while
rows x cachefalls only 34% - Measured
1.08(sorting) /1.58(continuous) /1.65(both). They fix different waste, so they add
Comments