-
Notifications
You must be signed in to change notification settings - Fork 3
fused_ops: typed flash backward (FusedSDPA + VJP), not a generic CustomCall #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
d291a82
9ae11d1
725aad0
8940377
67997e2
e8b8dbe
45cebd1
e479da1
d69f2d0
502e5de
e815ee8
8eb259d
20331a9
0058d22
e738459
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -203,8 +203,15 @@ type Quantization struct { | |
| GGMLType GGMLQuantType | ||
| } | ||
|
|
||
| // ScaledDotProductAttentionConfig holds optional optimization hints for FusedScaledDotProductAttention. | ||
| // ScaledDotProductAttentionConfig holds optional optimization hints and fused-attention | ||
| // parameters for FusedScaledDotProductAttention. | ||
| // A nil *ScaledDotProductAttentionConfig means "use defaults" (all optimizations disabled). | ||
| // Correctness-affecting fields (Bias, QuerySeqLen, KeyValueSeqLen, dropout, etc.): backends | ||
| // that cannot honor them MUST return ErrNotImplemented so the caller falls back to the | ||
| // decomposed path. | ||
| // Pure optimization hints (e.g. QuantizedMatmuls): backends that do not support them may | ||
| // silently ignore them and compute a numerically equivalent result in float arithmetic. | ||
| // nil/zero means "unused". | ||
| type ScaledDotProductAttentionConfig struct { | ||
| // QuantizedMatmuls: if true, the backend may use dynamic per-head symmetric | ||
| // affine quantization (scale-only, no zero point) to convert float32 Q/K/V slices | ||
|
|
@@ -215,6 +222,12 @@ type ScaledDotProductAttentionConfig struct { | |
| // (e.g. ARM SDOT/UDOT, x86 VNNI). Backends that do not support quantized matmuls | ||
| // ignore this flag and use float arithmetic. | ||
| QuantizedMatmuls bool | ||
|
|
||
| // QuerySeqLen, KeyValueSeqLen are optional per-batch actual sequence lengths | ||
| // (int32 tensors, shape [B]). When set, the backend masks by sequence length | ||
| // (padding mask) instead of a materialized [S,Skv] mask. Combined with causal=true | ||
| // this is a padding-causal mask. nil = unused. | ||
| QuerySeqLen, KeyValueSeqLen Value | ||
| } | ||
|
|
||
| // ActivationType specifies the activation function for fused operations. | ||
|
|
@@ -310,14 +323,37 @@ type FusedOps interface { | |
| // this method when both are needed. Backends may assume they won't both be set. | ||
| // - options: optional optimization hints (nil uses defaults). See ScaledDotProductAttentionConfig. | ||
| // | ||
| // Output: same shape as query. | ||
| // Outputs: | ||
| // - output: same shape as query. | ||
| // - softmaxStats: optional (may be nil). When the backend supports a fused backward | ||
| // (FusedScaledDotProductAttentionVJP), it returns the per-row softmax statistics | ||
| // (log-sum-exp), shape [batch, numHeads, seqLen] f32, which the VJP needs. Backends | ||
| // without a fused backward return nil here and ErrNotImplemented from the VJP, so the | ||
| // caller differentiates the decomposed attention instead. | ||
| FusedScaledDotProductAttention( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we are changing this a couple of suggestions (but I can do them later):
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deferring these to a follow-up PR to keep the statesForVJP refactor isolated: drop |
||
| query, key, value, mask Value, | ||
| numHeads, numKVHeads int, | ||
| axesLayout AxesLayout, | ||
| scale float64, | ||
| causal bool, | ||
| options *ScaledDotProductAttentionConfig) (Value, error) | ||
| options *ScaledDotProductAttentionConfig) (output, softmaxStats Value, err error) | ||
|
|
||
| // FusedScaledDotProductAttentionVJP computes the gradients (dQuery, dKey, dValue) of | ||
| // FusedScaledDotProductAttention given the forward output, the softmaxStats it returned, and | ||
| // the output gradient dOutput. The query/key/value/mask and numHeads..options parameters are | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: "the adjoint output gradient dOutput" (that's how I've been calling the V in VJP, following the convention I found in the related literature).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed: the VJP doc now calls it "the adjoint output gradient dOutput". guygrigsby@e815ee8 |
||
| // the same values passed to the forward call. | ||
| // | ||
| // Backends that return non-nil softmaxStats from the forward and implement a fused backward | ||
| // (e.g. the cuDNN flash backward) implement this. Others return ErrNotImplemented so the caller | ||
| // falls back to differentiating the decomposed attention. | ||
| FusedScaledDotProductAttentionVJP( | ||
| query, key, value, mask Value, | ||
| numHeads, numKVHeads int, | ||
| axesLayout AxesLayout, | ||
| scale float64, | ||
| causal bool, | ||
| options *ScaledDotProductAttentionConfig, | ||
| output, softmaxStats, dOutput Value) (dQuery, dKey, dValue Value, err error) | ||
|
|
||
| // QuantizedEmbeddingLookup performs a quantized embedding lookup (row gather) | ||
| // with on-the-fly dequantization. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies, but I just realized something: the
softmaxStatsis very specific to thecudnnimplementation. Actually, I'm surprised it also doesn't want themax-expof the attention logits, since having it could speed-up the online softmax, which I assume it re-calculates in the VJP (?).But, I was going to suggest to change
softmaxStats ValuetostatesForVJP []Value. And theFusedScaledDotProductAttentionVJPwould take that as input.I think the
cudnnimplementation works as a gradient checkpoint, it regenerates some forward values to save temporary memory. But other implementations (for CPU, where memory is cheaper) may find a different balance and decide to keep more state in temporary memory to avoid having to regenerate them on the VJP. Hence the change from a singleValueto a[]Value.Actually, I asked the AI, it mentions that some version of the
cudnn FMHAalso requires a byte tensor calledworkspaceas additional output that needs to be passed back to the backward (VJP) function.Any thoughts ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double checked with the AI and looks like this is the right call. I'll add
statesForVJP []Valuein place ofsoftmaxStats Value.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done:
softmaxStats Value->statesForVJP []Valueon the forward return and the VJP input, threaded through the interface, thenotimplementeddefault, and the go backend reference. An empty slice keeps the oldnilmeaning (no fused backward -> decomposed VJP). guygrigsby@502e5de