spans #3380 (offline prepacked, open) and
#4105 (Energon, open) —
current-main links pinned to f31f793, #3380 links to b84eba1.
The map across both PRs — this one: every way a packed row can be produced, and the single place they converge.
Packing puts several short conversations into one long row so the GPU is not multiplying through padding. NeMo-RL has one way to do this today and two more proposed in open PRs. They differ in when the boundaries are decided — at dataset build, at data load, or at the training step — and in how much padding that decision leaves behind.
The map below carries the same two conversations through all three: A is 5 tokens, B is 10. Watch the third column — every path produces the same real boundaries and a differently sized row.
| 1 · Offline prepacked | 2 · Loader packs | 3 · Trainer packs | |
|---|---|---|---|
| Status | #3380, open | #4105, open | on main |
| Boundaries decided | at dataset build | as the loader buffers | at the training step |
| Where the work runs | CPU, once, offline | loader worker | GPU worker, every step |
| Same row each epoch? | yes | no | no |
| Row width | fixed when written | always max_input_seq_length | as wide as the batch needs |
| Rows per microbatch | one | one pack | several |
| Built for | reproducing a Megatron-LM baseline exactly | multimodal SFT streamed through Energon | general use |
| Detail page | two packers | who packs | covered in both |
Because the thing the model needs is small: a flat run of tokens, and a list saying where each conversation ends — the shape the Energon path builds and the trainer path builds. Everything upstream is free to decide those two however it likes.
The attention kernel reads cu_seqlens to know where one conversation stops and the next
starts, so it never lets them see each other. Whether that list was computed months ago by an offline
script, a second ago by the loader, or right now by the trainer makes no difference to it.
cu_seqlens_q and the padded ones separately. Path 2 passes the padded array
for both, on purpose — some consumers read cu_seqlens_q as a wrap-around point, and real
boundaries would let a value roll into the padding. So pad_between_seqs is False
there and True on the others. Same object, different contents, and the reason is worth
knowing before adding a fourth path.