Skip to content

Bound the weight-gradient temporary in FusedEncoder.backward - #141

Open
alepot55 wants to merge 1 commit into
EleutherAI:mainfrom
alepot55:pr-encoder-chunk
Open

alepot55 wants to merge 1 commit into
EleutherAI:mainfrom
alepot55:pr-encoder-chunk

Conversation

@alepot55

@alepot55 alepot55 commented Aug 23, 2026 •

Copy link
Copy Markdown

What

FusedEncoder.backward builds grad_values[:, :, None] * input[:, None, :], an [N, k, D] tensor holding every top-k contribution at once, and then folds it into grad_weight with a single index_add_. This consumes it in row-blocks sized to a fixed byte budget instead.

Why

It is the largest single allocation in SAE training. Some sizes, all measured on an A100:

shape temporary
batch 8, ctx 512, d_model 576, k 32 (repo defaults) 288 MiB
N=8192, D=1024, k=32 (what tests/test_encode.py already runs) 1 GiB

It is linear in k, so the --k 192 configuration the README recommends for Llama 3 8B pays six times the first row.

Effect

Peak allocated per rank, SmolLM2-135M, 30 hookpoints, 2x A100-40GB, batch 8:

mode before after
DDP 9.19 GiB 9.04 GiB
--distribute_modules 4.24 GiB 3.86 GiB

Median step time is unchanged within run-to-run variance, which I measured at about 25% on this setup, so I would not read a throughput difference either way from these runs.

Correctness

The arithmetic is unchanged. Only the accumulation order inside index_add_ differs, and that was already unspecified on CUDA. tests/test_encode.py compares the encoder gradient against a naive autograd reference at N=8192, D=1024, k=32 and passes.

Full suite on an A100, this branch against main: same results, with the one pre-existing test_auxk_loss failure on both (that one is #142).

The backward pass builds `grad_values[:, :, None] * input[:, None, :]`, an
`[N, k, D]` tensor holding every top-k contribution at once. It is the largest
single allocation in SAE training: 288 MiB at batch 8, ctx 512, d_model 576,
k 32, and 1 GiB in the shape `tests/test_encode.py` already exercises. It
scales with k, so the `--k 192` configuration the README recommends pays six
times that.

Consume it in row-blocks sized to a fixed byte budget instead. The arithmetic
is unchanged; only the accumulation order inside `index_add_` differs, and that
was already unspecified on CUDA.

Peak allocated per rank on SmolLM2-135M with 30 hookpoints at 2 GPUs:
9.19 -> 9.04 GiB under DDP, 4.24 -> 3.86 GiB under --distribute_modules. Step
time is unchanged within run-to-run variance.
@CLAassistant

CLAassistant commented Aug 23, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@alepot55

Copy link
Copy Markdown
Author

recheck

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.

2 participants