Skip to content

[trainer throughput] Carry Miles rows and log probabilities as arrays - #158

Merged
micahtyong merged 1 commit into
mainfrom
micah/perf-4b/A1-array-rows
Oct 28, 2026
Merged

micahtyong merged 1 commit into
mainfrom
micah/perf-4b/A1-array-rows

Conversation

@micahtyong

@micahtyong micahtyong commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Inside the trainer container, the Miles backend runs as one controller process and one actor process per GPU. For every forward or forward_backward call the controller builds the batch, puts it in the Ray object store, and each actor pulls the whole batch out and keeps its data-parallel share. When the actors finish, each sends its per-datum log probabilities back and the controller assembles the result.

Until now the batch crossed that boundary as nested Python lists: one int per token and one float per label weight, about 2.3 million Python objects per 128-example call for a classifier with 5 labels per position. Pickling and unpickling that many objects is slow, and it is slow by a different amount on each actor. The step-2 trace showed the cost in two places. After each actor returned, the controller spent about 0.9 s turning the returned lists into results. And at the start of every forward_backward, rank 0 sat 0.73 s at the first collective waiting for ranks 1–3 to finish unpickling. (A collective is the point where all ranks synchronize; the first one there waits for the rest.)

Arrays fix both. Numpy arrays pickle as one buffer each, Ray hands them to the actors without copying, and torch.tensor builds a GPU tensor from an array without walking it in Python. So this change keeps the rows as arrays from prepare_batch to the actors, and brings the log probabilities back as arrays.

  • data.py: tokens, target_tokens, weights, advantages and sampling_logprobs are arrays. Sequence alignment and data-parallel padding pad arrays.
  • runtime.py: Miles' bridge is subclassed so loss masks go in as arrays and per-datum log probabilities come back as arrays instead of through .tolist(). Weights, advantages and sampling log probabilities are sent as CPU tensors, because Miles slices those per context-parallel rank with either list + or torch.cat, and an array would reach torch.cat.
  • replay_data.py: sampling-support validation accepts array target rows.

Stacks on #154, which prepared the rows with numpy but still turned them into lists at the end.

E2E results

A 4B LoRA classifier with 5 candidate labels per position on 4×H100, max_tokens_per_gpu 8192, with #157 and #152–#154 applied. Each step runs two forward calls on a frozen adapter, one forward_backward on the trained adapter, then optim_step; 128 examples of about 1,630 tokens per call. Same driver and data for both rows; 34 steps, steady-state medians.

Before WandB / After WandB

Median per step Before After
Step (client) 11.62 s 7.62 s
forward_backward (trainer) 5.18 s 3.68 s
Two forward calls (trainer) 4.54 s 2.61 s
Controller time after the actors return, per call ~0.9 s < 0.01 s
Rank 0's wait at the first collective of forward_backward 0.73 s 0.00 s

Step-0 log probabilities are bit-identical before and after. The after run traced all four ranks; the wait is 0.000 s on every one of them.

🤖 Generated with Claude Code

@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch from 21e4945 to 10335f7 Compare October 28, 2026 16:29
@micahtyong
micahtyong added this pull request to stack #162 October 28, 2026 21:37
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch from 10335f7 to 8d5d033 Compare October 28, 2026 21:43
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch from 8d5d033 to f23ab9a Compare October 28, 2026 21:49
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch from f23ab9a to e0109cc Compare October 28, 2026 21:50
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch from e0109cc to b653264 Compare October 28, 2026 21:51
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch 2 times, most recently from 1e9d950 to 66a3157 Compare October 28, 2026 21:54
@micahtyong
micahtyong marked this pull request as ready for review October 28, 2026 21:56
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch 2 times, most recently from 1fccd05 to 7dd0030 Compare October 28, 2026 22:00
Base automatically changed from micah/perf-4b/O2-vectorize-rows to main October 28, 2026 22:40
Rows reach the Miles object store as numpy arrays instead of per-token
Python lists, and per-datum log probabilities come back as arrays.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@micahtyong
micahtyong force-pushed the micah/perf-4b/A1-array-rows branch from 7dd0030 to f9fa0c4 Compare October 28, 2026 22:40
@micahtyong

Copy link
Copy Markdown
Collaborator Author

We'll need to upstream this to miles, but otherwise lgtm

@micahtyong
micahtyong merged commit af7dccb into main Oct 28, 2026
4 checks passed
@micahtyong
micahtyong deleted the micah/perf-4b/A1-array-rows branch October 28, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant