[Feature]: key-padding lengths for scaledDotProductAttention — mask as op metadata, lengths-based lowering
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 18/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- kotlin
- Área
- compilers, machine-learning
Línea de trabajo
Start from TensorOps.scaledDotProductAttention, then follow the mask handling in DefaultCpuOps.scaledDotProductAttention, RecordingExecution, and AttentionOperationsConverter, which the issue names. The issue depends on the not-yet-built LoweringSpec from #1342, so check that first. Done means the acceptance checklist holds: a padded batch matches unpadded single passes within 1e-5, and the dense mask path is unchanged.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
🧠 D: DEFINE — Problem & Opportunity
TensorOps.scaledDotProductAttention(query, key, value, mask, scale, causal) knows two mask forms: the causal flag and an opaque additive mask tensor [batch, 1, seqLen, kvLen]. There is no notion of a key-padding mask, so a batch of sequences with different lengths can only be attended by baking -1e30 into that dense tensor on the caller's side.
That works on the eager path and loses the structure everywhere else:
- the recorded graph (
RecordingExecution→ScaledDotProductAttentionOperation,parameters = { scale, causal }) sees a per-call constant of sizeb·Sq·Skper layer instead of "these keys are padding"; - the StableHLO converter (
AttentionOperationsConverter) can only broadcast and add it before the softmax; a fused attention kernel on a vendor target takes lengths, which can no longer be recovered; transformer-corealready carries its sliding/causal mask the same way (MultiHeadAttention.buildSlidingCausalMask,-1e30), and the converter has a second copy of the causal formula in its iota path — a padding mask would be the third copy.
Consumer: SKaiNET-transformers#490 (key-padding mask for MultiHeadAttention, batched BERT/Laya encoder inference) needs this to batch the questions of one typed-decision request instead of running one 322M-parameter encoder pass per question (1.8–5 s each, measured 2026-10-12/13).
🔍 A: ASSESS — Options
keyLengths: Tensor<Int>?operand + recordedmaskKind(proposed): the op gains a[batch]lengths operand and recordsmaskKind ∈ { none, causal, slidingWindow, keyPadding, combined },window,rightContextas parameters. The densemaskstays as the generic fallback, so no caller changes numerically.- Dense mask only, as today: zero API change, but the compiled lane inherits a batch-sized graph input per layer and the fused-kernel path is closed for good.
- A separate
maskedScaledDotProductAttentionop: avoids widening the signature, but doubles the converter, the autograd entry and the backend kernels for one extra operand.
✅ R: RESOLVE — Decision
Option 1, with the mask construction unified:
-
TensorOps.scaledDotProductAttention(query, key, value, mask = null, scale = 0f, causal = false, keyLengths: Tensor<*, *>? = null, window: Int = 0, rightContext: Int = 0);VoidTensorOps,RecordingExecution,ComputeGraphExecutor,DefaultExecutionTapebackward follow.RecordingExecutionrecordsmaskKind,window,rightContextinparametersandkeyLengthsas operand 4. -
One mask builder
AttentionMask.additive(kind, lengths, window, rightContext, seqQ, seqKV)inskainet-lang-core, used by the CPU kernel (DefaultCpuOps.scaledDotProductAttention), by the StableHLO decomposition and, downstream, byMultiHeadAttentionin place ofbuildSlidingCausalMask. Padded keys get-1e30(the value the converter and transformer-core already agree on), never-inf, so a fully padded row stays finite. -
CPU kernel: when
keyLengthsis given, the score loop stops atlen[b]per batch row instead of adding a mask — cheaper than the dense path and bit-identical to it on the attended keys. -
StableHLO lowering, keyed on
OpGranularityPolicy.keepFused("scaleddotproductattention")(the policy is threaded intoConversionContextbut unused so far — this and #1342 are its first consumers):policy emitted form null/false(portable,llvm-cpu)decomposed as today; the padding mask is built from the keyLengthsoperand withiota+compare+selectin the scores shape, so no[b, 1, Sq, Sk]constant enters the moduletrue(vendor target with a fused attention kernel)stablehlo.composite "skainet.sdpa" %q, %k, %v, %lengths { composite_attributes = { mask_kind = "keyPadding", causal = false, window = 128 : i32, right_context = 0 : i32, scale = 0.125 : f32 }, decomposition = @skainet_sdpa_…, version = 1 : i32 }with the decomposition function emitted by the same mask builderBoth rows have identical semantics by construction: the StableHLO spec defines
compositeas replaceable by itsdecomposition, andstablehlo-legalize-composite-to-calldoes that mechanically. TheLoweringSpecrecord and theConversionContext.emitCompositehelper are specified in #1342; this issue reuses them for SDPA rather than adding a second mechanism. -
Autograd:
scaledDotProductAttentionBackwardmasks the gradient of padded keys to zero from the same builder. -
Docs: operator matrix row,
@Lowering(composite = "skainet.sdpa")once #1342 adds the annotation.
🚀 C: COMMIT — Acceptance
-
keyLengths/window/rightContextonTensorOps.scaledDotProductAttention; densemaskpath byte-identical to today -
AttentionMask.additivebuilder shared by the CPU kernel, the StableHLO decomposition and the backward pass; the converter's iota causal path replaced by it -
RecordingExecutionrecordsmaskKind+ lengths operand;ComputeGraphExecutordispatches on them; DIRECT vs OPTIMIZED parity within 1e-6 on a padded batch - StableHLO: lengths-based decomposition and
"skainet.sdpa"composite (version 1) behindOpGranularityPolicy; test runs the composite throughstablehlo-legalize-composite-to-calland compares with the decomposed module - Parity test: padded batch of N sequences equals N unpadded single passes within 1e-5 (CPU, both kernel tiers)
- transformers:
MultiHeadAttentionswitches to the builder and passes lengths (SKaiNET-transformers#490)
Related: #1342 (exact GELU — same LoweringSpec / composite emission seam, first consumers of OpGranularityPolicy together), SKaiNET-transformers#490 (consumer), SKaiNET-transformers#487 (Laya adapter, batchSupport = false today).
- Lenguaje dominante
- Kotlin
- Estrellas
- 52
- Forks
- 15
- Merge medio
- 1 d 8 h
- PR fusionados (30 d)
- 37
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de SKaiNET-developers/SKaiNET
-
coding good first issue platform size:xs skill:js sub-issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
SKaiNET-developers/SKaiNET#1232 ·
Los mantenedores suelen responder en 1 día
-
compute-backend dsl/api enhancement size:m skill:kotlin-core skill:numerics
Dificultad 5/5 Más de una semana Aptitud para principiantes 12/100
SKaiNET-developers/SKaiNET#1342 ·
Los mantenedores suelen responder en 1 día
-
skeep tracking
Dificultad 5/5 Más de una semana Aptitud para principiantes 15/100
SKaiNET-developers/SKaiNET#1331 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
assessment size:s skill:review sub-issue
Dificultad 4/5 1-2 días Aptitud para principiantes 18/100
SKaiNET-developers/SKaiNET#1330 ·
Los mantenedores suelen responder en 1 día
-
documentation good first issue size:s skill:docs sub-issue
Dificultad 3/5 1-2 días Aptitud para principiantes 78/100
SKaiNET-developers/SKaiNET#1329 ·
Los mantenedores suelen responder en 1 día
Todos los issues de SKaiNET-developers/SKaiNET
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
rustyrazorblade/easy-db-lab#1017 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
callstack/react-native-brownfield#492 · 1 comentario ·
Los mantenedores suelen responder en 4 días
-
fix: retain the best partial noise-correction solutionPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
world-federation-of-advertisers/cross-media-measurement#4653 ·
Los mantenedores suelen responder en 1 día