Skip to content

Commit 133f0e9

Browse files
justinchubyCopilotCopilotCopilot
authored
Refresh skill docs for current multimodal/CUDA behavior (#282)
Align skill docs with current mobius behavior: encoder inputs are declared with config.dtype, KV-shared Gemma4 GQA emits empty K/V with runtime dependency on ORT KV-shared support, and quality checklist now points to search.past_present_share_buffer in genai_config.json. --------- Signed-off-by: Justin Chu <justinchu@microsoft.com> Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 12eb22b commit 133f0e9

3 files changed

Lines changed: 60 additions & 22 deletions

File tree

.agents/skills/debugging-memcpy/SKILL.md

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ pkg = build(model_id, execution_provider='cuda', dtype='float16')
6565
tmpdir = tempfile.mkdtemp(prefix="memcpy_profile_")
6666

6767
for name, model in pkg.items():
68-
# IMPORTANT: lower opset to 23 — CUDA EP doesn't register many ops
69-
# (including Reshape, Cast) at opset 24. Without this, you'll see
70-
# hundreds of false-positive Memcpy from ops that work fine at opset 23.
71-
if model.opset_imports.get("", 0) > 23:
72-
model.opset_imports[""] = 23
68+
# NOTE: ORT ≤1.24.x didn't register CUDA kernels for some opset 24
69+
# standard ops. If profiling on older ORT, lower opset to 23 to
70+
# avoid false-positive Memcpy nodes. On current ORT this is
71+
# unnecessary — the correct fix is updating ORT kernel registration.
72+
# if model.opset_imports.get("", 0) > 23:
73+
# model.opset_imports[""] = 23
7374

7475
path = os.path.join(tmpdir, f"{name}.onnx")
7576
ir.save(model, path, external_data=f"{name}.onnx.data")
@@ -120,14 +121,16 @@ This gives you the exact node names causing Memcpy. Common categories:
120121
- **Equal/Cast on token IDs** — usually low-impact (small tensors)
121122
- **CumSum on INT64** — inherent, no GPU kernel
122123

123-
### Critical: opset 24 false positives
124+
### Critical: opset 24 false positives (ORT ≤1.24.x)
124125

125-
**Always lower opset to 23 before profiling.** ORT CUDA EP (≤1.24.x)
126-
does not register kernels for many standard ops at opset 24, including
127-
`Reshape`, `Cast`, and others. A Gemma4 decoder at opset 24 shows
128-
**280 Memcpy** nodes; at opset 23, it shows **4**. The
129-
`ort_lower_opset_for_ep` flag in `_flags.py` handles this at runtime,
130-
but you must apply it manually when profiling with raw ORT sessions.
126+
**On ORT ≤1.24.x**, CUDA EP did not register kernels for many standard
127+
ops at opset 24, including `Reshape`, `Cast`, and others. A Gemma4
128+
decoder at opset 24 showed **280 Memcpy** nodes; at opset 23, just **4**.
129+
The correct fix is to update ORT kernel registration for the missing
130+
opset versions — not to lower the model's opset. The
131+
`ort_lower_opset_for_ep` flag in `_flags.py` is available as a
132+
workaround (disabled by default, opt-in via
133+
`MOBIUS_ORT_LOWER_OPSET_FOR_EP=1`).
131134

132135
## CPU-only op reference (ORT CUDA EP)
133136

@@ -223,6 +226,11 @@ and the `GreaterOrEqual` for causality.
223226
with `is_causal=1`. Not applicable to `GroupQueryAttention` (GQA handles
224227
masking internally via `local_window_size`).
225228

229+
> **Note**: For complex attention patterns (e.g. dual head_dim, KV-shared
230+
> layers, mixed sliding/full attention), prefer **float additive bias** over
231+
> bool masks. Float masks are batch-safe and work correctly with MEA
232+
> (Memory Efficient Attention) on CUDA.
233+
226234
### Pattern 3: Use GQA's built-in local_window_size
227235

228236
**Problem**: Sliding-window attention requires an explicit mask (CumSum-based)
@@ -303,12 +311,14 @@ at opset 23. Results after optimization:
303311
| Decoder | 4 | `input_ids` (input), 2× `Equal` (token masks), `Where` (bool mask) |
304312
| Embedding | 3 | `input_ids` (input), 2× `Equal` (token masks) |
305313

306-
### Opset 24 trap
314+
### Opset 24 historical note (ORT ≤1.24.x)
307315

308-
Without opset lowering, the decoder showed **280 Memcpy** nodes because
309-
CUDA EP doesn't register `Reshape`, `Cast`, and other standard ops at
310-
opset 24. The `ort_lower_opset_for_ep` flag (enabled by default) fixes
311-
this at runtime. Always lower to opset 23 before profiling.
316+
On ORT ≤1.24.x, the decoder showed **280 Memcpy** nodes because
317+
CUDA EP didn't register `Reshape`, `Cast`, and other standard ops at
318+
opset 24. This has been fixed in newer ORT versions. The
319+
`ort_lower_opset_for_ep` flag (disabled by default, opt-in via
320+
`MOBIUS_ORT_LOWER_OPSET_FOR_EP=1`) is available as a workaround for
321+
older ORT builds.
312322

313323
## Impact assessment
314324

.agents/skills/debugging-multimodal/SKILL.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,28 @@ ORT bug: microsoft/onnxruntime#28107
254254

255255
### Opset 24 kernel registration
256256

257-
ORT ≤1.24.x CUDA/TRT EPs don't register kernels for opset 24.
258-
**Fix:** Use the `ort_lower_opset_for_ep` feature flag (enabled by
259-
default). See `src/mobius/_flags.py`.
257+
ORT ≤1.24.x CUDA/TRT EPs didn't register kernels for opset 24.
258+
This has been fixed in newer ORT versions. The `ort_lower_opset_for_ep`
259+
feature flag is available as a workaround (disabled by default, opt-in
260+
via `MOBIUS_ORT_LOWER_OPSET_FOR_EP=1`). See `src/mobius/_flags.py`.
261+
262+
### Encoder input dtype alignment
263+
264+
Encoder task inputs should be declared with `dtype=config.dtype` so
265+
entry tensors match the model compute dtype (float32/float16/bfloat16).
266+
In the current codebase, multimodal encoder task builders set encoder
267+
inputs directly to `config.dtype` (there is no `_cast_encoder_input()`
268+
helper in `src/mobius/tasks/_base.py`).
269+
270+
### GQA for KV-shared layers
271+
272+
Gemma4 KV-shared layers now emit `GroupQueryAttention` with empty K/V
273+
inputs (`kv_sequence_length=0`) and borrowed source-layer KV wired via
274+
`past_key`/`past_value`, avoiding extra Transpose/Reshape cache ops.
275+
276+
Runtime support depends on ORT having KV-shared GQA support (tracked in
277+
microsoft/onnxruntime#28242; still upstreaming as of this writing). On
278+
ORT builds without that support, this path can fail at runtime.
260279

261280
### NaN for large head_dim (> 256)
262281

.agents/skills/quality-checklist/SKILL.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,19 @@ python examples/<model>_text_generation.py --compare-hf --dtype bf16
135135
overflow: microsoft/onnxruntime#28107)
136136
- [ ] No NaN from large head_dim > 256 (tracked in
137137
microsoft/onnxruntime#28195, #28196)
138-
- [ ] `ort_lower_opset_for_ep` flag handles opset 24→23 lowering for
139-
CUDA EP (enabled by default in `src/mobius/_flags.py`)
138+
- [ ] `ort_lower_opset_for_ep` flag available for opset 24→23 lowering
139+
on older ORT builds (disabled by default in `src/mobius/_flags.py`,
140+
opt-in via `MOBIUS_ORT_LOWER_OPSET_FOR_EP=1`)
140141
- [ ] Dead graph inputs removed after EP-aware optimization
141142
(`RemoveDeadGraphInputsPass` in Stage 4 of `optimize_model()`)
143+
- [ ] For models with dual head_dim (e.g. Gemma4: 256 for sliding,
144+
512 for full attention), verify whether ORT GenAI requires
145+
`search.past_present_share_buffer=false` because GenAI allocates
146+
uniform KV cache shapes; if so, explicitly override the generated
147+
`genai_config.json` before runtime validation rather than assuming
148+
the default generated setting is correct
149+
- [ ] Encoder inputs (vision/audio) are declared with `dtype=config.dtype`
150+
at sub-model entry (no stale float32-only cast guidance)
142151

143152
### 9. ORT GenAI runtime
144153

0 commit comments

Comments
 (0)