[CUDA] Make native CTC loss backward deterministic by shuo-ouyang · Pull Request #201617 · pytorch/pytorch · GitHub
Skip to content

[CUDA] Make native CTC loss backward deterministic - #201617

Open
shuo-ouyang wants to merge 1 commit into
pytorch:mainfrom
shuo-ouyang:feature/deterministic-ctcloss
Open

shuo-ouyang wants to merge 1 commit into
pytorch:mainfrom
shuo-ouyang:feature/deterministic-ctcloss

Conversation

@shuo-ouyang

Copy link
Copy Markdown
Contributor

Linked issue or supporting maintainer

Fixes #178052

BC-breaking?

No. With torch.use_deterministic_algorithms(True), CUDA ctc_loss backward used to raise and now succeeds. Gradients on large inputs may differ from the non-deterministic path in the last bits (due to reduction order).

Summary

Currently, only the large-input CUDA CTC backward used atomicAdd (the nonblank kernel); the small-input kernel was already deterministic, but ctc_loss_backward_gpu raised for every size.
This PR removes the blanket alert and, in deterministic mode, replaces the nonblank atomic update with label grouping (stable sort + binary-searched offsets) and a fixed-order warp reduction.

Benchmarks

Backward latency in milliseconds on an NVIDIA RTX 3080 (10 GB), CUDA 12.8, cuDNN 9.8.0 (cuDNN disabled for these runs, so the native kernels are measured). Each number is the median of 3 rounds of 10 calls after 5 warmup calls, timed with CUDA events [state whether each round reports the mean of its 10 calls]. Shapes are (T, B, C, L): input length, batch size, vocabulary size, and target length.

Shape (T, B, C, L) dtype Non-deterministic Deterministic
(26, 1, 30, 8)* float32 0.169 0.200
(101, 64, 1000, 32) float32 0.505 0.666
(249, 8, 5000, 60) float32 0.762 0.834
(999, 8, 1000, 240) float32 2.338 3.085
(999, 8, 5000, 240) float32 3.410 4.260
(249, 8, 5000, 60) float64 5.558 5.139
(999, 8, 5000, 240) float64 21.152 25.328

* Uses the small-input kernel in both modes, which is unchanged, so the difference reflects measurement noise.

On large inputs the deterministic path takes 0.92x-1.32x the time of the non-deterministic path.

For reference, the native deterministic path versus cuDNN (float32 only, forward + backward combined, since cuDNN computes gradients in the forward pass):

Native deterministic vs cuDNN
Shape (T, B, C, L) Native deterministic cuDNN
(26, 1, 30, 8) 0.422 0.255
(101, 64, 1000, 32) 0.986 0.562
(249, 8, 5000, 60) 1.135 0.939
(999, 8, 1000, 240) 3.819 2.660
(999, 8, 5000, 240) 5.059 4.898

cuDNN is only used when blank == 0, targets are contiguous int32 on CPU, log_probs is float32, all input lengths equal T, and target lengths are below 256. The native path remains the fallback for all other inputs.

Agent details (if applicable)

Worked with GPT-6.1-Sol and Claude Code Sonnet 5.5

Signed-off-by: shuo-ouyang <shuo-ouyang@foxmail.com>
@pytorch-bot pytorch-bot Bot added the release notes: nn release notes category label Oct 21, 2026
@pytorch-bot

pytorch-bot Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

@github-actions github-actions Bot added bot-triaged This is a label only to be used by the auto triage bot missing actionable issue A PR without a linked actionable issue or maintainer sponsor triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module labels Oct 21, 2026

This branch was successfully deployed

1 active deployment
bedrock — 69e62332 Deployed Oct 21, 2026 by pytorchbot via analyze #12281
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-triaged This is a label only to be used by the auto triage bot missing actionable issue A PR without a linked actionable issue or maintainer sponsor open source release notes: nn release notes category triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deterministic implementation of CTC Loss

2 participants