Repository navigation
Commit 9b5b8da
committed
Lower mean.dim beyond 4D pooling as an XNNPACK static mean
## Ulterior Motive
Run RMSNorm in LeRobot policies on XNNPACK (pytorch#24667), where `mean.dim` fails to partition with "only supports averaging 4D tensors, got tensor of rank 3".
## Rationale
**What**: Add an `XNNStaticMean` node backed by `xnn_define_static_reduce(xnn_reduce_mean)`, and lower every `mean.dim` that is not Global Average Pooling to it: ranks 1 to 6, any dims, and either `keepdim`.
**Why**: XNNPACK only lowered a 4D mean over dims [2, 3] with `keepdim=True`. RMSNorm averages the last dim of a 3-D tensor, so its mean stayed on the portable kernel and split the delegate around every norm.
## Details
Review order: the serialization schema and `XNNCompiler.cpp`, then `op_mean_dim.py` and `is_global_avg_pool_mean`, then `ChannelsLastTaggedReshapePass`, then `MeanDimConfig`, then the tests.
- The 4D, dims [2, 3], `keepdim=True` case still lowers to Global Average Pooling on NHWC. For those programs the `.pte` is byte-identical to main (fp32, fp16, qs8, conv-bn-hardtanh-mean, and a CNN ending in `AdaptiveAvgPool2d(1)`), and the tests check which XNNPACK node each mean serializes to.
- Every other `mean.dim` reduces over NCHW dims, so `ChannelsLastTaggedReshapePass` gives it an NCHW input, and the visitor refuses an NHWC one. After a convolution that is the same NHWC to NCHW copy main made at the delegate boundary. Remapping the reduction axes to read NHWC directly would remove that copy for heads like `x.mean([2, 3])` and `x.mean(1, keepdim=True)`; that is left for a follow-up.
- `is_global_avg_pool_mean` takes the rank from the mean's own output, which with `keepdim=True` equals the input's, because inside `ChannelsLastTaggedReshapePass` the mean's input can be a newly inserted node without `"val"`.
- The node uses a shared `_XNNStaticReduce` table, so other XNNPACK reductions can reuse it and `defineGenericReduceNode`. It is appended to `XNodeUnion`, so existing node IDs do not change. Practically every re-exported XNNPACK LLM program contains it, because RMSNorm (`examples/models/llama/norm.py`) and QK-norm lower to it, and those programs fail to load on runtimes built before this change, including released prebuilt runtimes. The release note should say so.
- An empty dim list averages over every dim, as in PyTorch.
- Latency against main, branch time over main time (below 1 is faster), each a median over at least 1000 timed runs per program, alternating main and branch programs on one runtime, on an Apple M-series CPU (12 performance + 4 efficiency cores) with the default 16 threads: RMSNorm 1x128x2048 0.48x, 3-D last-dim mean 0.45x, RMSNorm then Linear 0.77x, 4 pre-norm residual MLP blocks 0.99x for decode (1 token) and 0.80x for prefill (64 tokens), conv then mean over channels 0.88x.
- XNNPACK splits a reduction with more than one output into threadpool tasks, so a small mean with several outputs can cost more in XNNPACK than on the portable kernel when the pool has many threads. For RMSNorm on 10 shapes from 1x1x128 to 1x32x2048, every shape is 0.43x to 0.97x at 1 thread. At 4 threads the shapes under 8K elements with several outputs are 1.19x to 1.55x (1.5 to 2.7 us more), and the rest 0.44x to 0.93x. At 16 threads those small shapes are 3.7x to 5.7x (21 to 23 us more), and the rest 0.70x to 0.98x. Inside a model with neighbouring delegated ops the cost can remain: decode QK-norm between two linears, 8 heads of 64, goes from 11.8 us to 39.1 us at 16 threads, and 16 heads of 128 from 222 us to 241 us. A mean alone in its delegate pays the same wake-up (a bare 1x128x128 mean is 3.26x at 16 threads and 1.14x at 4), as does any op lowered alone, such as `add` (2.95x at 1x128x128 and 16 threads on main). These means are lowered like every other op the XNNPACK partitioner takes, rather than gated by size, which would be tuned to one thread count and treat static and dynamic exports differently.
- The LoRA CI scripts check the start of the 8da4w-quantized Qwen3 base model's greedy output with a regex. With RMSNorm and QK-norm now in XNNPACK, its first token can tip from `:\n` to `:` (`assistant: me` instead of `assistant:\n<think>`): unquantized, main and this branch generate the same tokens with logits within 1e-4, but 8da4w re-rounds activations and the two candidates are within about 0.5 logits. The regex accepts exactly those two continuations, and the scripts print it when it does not match.
- Tests cover 1-D to 6-D inputs, all dims, `keepdim=False`, fp16 and qs8 over the innermost and a middle dim, dynamic kept and reduced dims over 5 random shapes, RMSNorm, fp32 and qs8 convolutions followed by a mean, the serialized XNNPACK nodes (moved `xnnpack_node_types` from the layout pass tests to the XNNPACK `Tester`), and the layout copies the pass inserts.
Test plan:
```
python -m pytest backends/xnnpack/test/ops/test_mean_dim.py backends/xnnpack/test/passes/test_channels_last_tagged_reshape.py
python -m pytest backends/xnnpack/test/ops backends/xnnpack/test/passes backends/xnnpack/test/test_xnnpack_partitioner.py backends/xnnpack/test/serialization backends/xnnpack/test/quantizer
```
Authored with AI using Claude Code.1 parent bdc7c35 commit 9b5b8da
15 files changed
Lines changed: 475 additions & 169 deletions
File tree
- .ci/scripts
- backends/xnnpack
- _passes
- operators
- partition/config
- runtime
- serialization
- test
- ops
- passes
- tester
- utils
- docs/source/backends/xnnpack
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
49 | 52 | | |
50 | | - | |
51 | | - | |
| 53 | + | |
| 54 | + | |
52 | 55 | | |
53 | 56 | | |
54 | 57 | | |
55 | | - | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
56 | 61 | | |
57 | 62 | | |
58 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
49 | 52 | | |
50 | | - | |
51 | | - | |
| 53 | + | |
| 54 | + | |
52 | 55 | | |
53 | 56 | | |
54 | 57 | | |
55 | | - | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
56 | 61 | | |
57 | 62 | | |
58 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
69 | 70 | | |
70 | 71 | | |
71 | 72 | | |
72 | | - | |
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| |||
316 | 316 | | |
317 | 317 | | |
318 | 318 | | |
| 319 | + | |
| 320 | + | |
319 | 321 | | |
320 | 322 | | |
321 | 323 | | |
322 | 324 | | |
323 | 325 | | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
324 | 330 | | |
325 | 331 | | |
326 | 332 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
11 | 14 | | |
12 | 15 | | |
13 | 16 | | |
| |||
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| 23 | + | |
20 | 24 | | |
21 | 25 | | |
22 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
23 | 31 | | |
24 | 32 | | |
25 | 33 | | |
26 | 34 | | |
27 | 35 | | |
28 | 36 | | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
33 | 41 | | |
34 | 42 | | |
35 | 43 | | |
| |||
44 | 52 | | |
45 | 53 | | |
46 | 54 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | 55 | | |
64 | 56 | | |
65 | 57 | | |
66 | 58 | | |
67 | 59 | | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
79 | 63 | | |
| 64 | + | |
| 65 | + | |
80 | 66 | | |
81 | | - | |
82 | | - | |
| 67 | + | |
| 68 | + | |
83 | 69 | | |
84 | | - | |
85 | | - | |
86 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
87 | 89 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
714 | 714 | | |
715 | 715 | | |
716 | 716 | | |
717 | | - | |
718 | | - | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
719 | 720 | | |
720 | 721 | | |
721 | 722 | | |
722 | 723 | | |
723 | | - | |
724 | | - | |
725 | | - | |
726 | | - | |
727 | | - | |
728 | | - | |
729 | | - | |
730 | | - | |
731 | | - | |
732 | | - | |
| 724 | + | |
733 | 725 | | |
734 | 726 | | |
735 | 727 | | |
736 | 728 | | |
737 | | - | |
| 729 | + | |
738 | 730 | | |
739 | | - | |
| 731 | + | |
740 | 732 | | |
741 | 733 | | |
742 | 734 | | |
743 | 735 | | |
744 | | - | |
745 | | - | |
746 | | - | |
747 | | - | |
748 | | - | |
749 | | - | |
750 | | - | |
751 | | - | |
752 | | - | |
753 | | - | |
754 | | - | |
755 | 736 | | |
756 | 737 | | |
757 | 738 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2037 | 2037 | | |
2038 | 2038 | | |
2039 | 2039 | | |
| 2040 | + | |
| 2041 | + | |
| 2042 | + | |
| 2043 | + | |
| 2044 | + | |
| 2045 | + | |
| 2046 | + | |
| 2047 | + | |
| 2048 | + | |
| 2049 | + | |
| 2050 | + | |
| 2051 | + | |
| 2052 | + | |
| 2053 | + | |
| 2054 | + | |
| 2055 | + | |
| 2056 | + | |
| 2057 | + | |
| 2058 | + | |
| 2059 | + | |
| 2060 | + | |
| 2061 | + | |
| 2062 | + | |
| 2063 | + | |
| 2064 | + | |
| 2065 | + | |
| 2066 | + | |
| 2067 | + | |
| 2068 | + | |
| 2069 | + | |
| 2070 | + | |
| 2071 | + | |
| 2072 | + | |
| 2073 | + | |
| 2074 | + | |
| 2075 | + | |
| 2076 | + | |
| 2077 | + | |
| 2078 | + | |
| 2079 | + | |
| 2080 | + | |
| 2081 | + | |
| 2082 | + | |
| 2083 | + | |
| 2084 | + | |
| 2085 | + | |
| 2086 | + | |
| 2087 | + | |
| 2088 | + | |
| 2089 | + | |
| 2090 | + | |
| 2091 | + | |
| 2092 | + | |
| 2093 | + | |
2040 | 2094 | | |
2041 | 2095 | | |
2042 | 2096 | | |
| |||
2080 | 2134 | | |
2081 | 2135 | | |
2082 | 2136 | | |
| 2137 | + | |
| 2138 | + | |
| 2139 | + | |
2083 | 2140 | | |
2084 | 2141 | | |
2085 | 2142 | | |
| |||
2125 | 2182 | | |
2126 | 2183 | | |
2127 | 2184 | | |
| 2185 | + | |
2128 | 2186 | | |
2129 | 2187 | | |
2130 | 2188 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| 164 | + | |
164 | 165 | | |
165 | 166 | | |
166 | 167 | | |
| |||
306 | 307 | | |
307 | 308 | | |
308 | 309 | | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
309 | 319 | | |
310 | 320 | | |
311 | 321 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
| 160 | + | |
160 | 161 | | |
161 | 162 | | |
162 | 163 | | |
| |||
302 | 303 | | |
303 | 304 | | |
304 | 305 | | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
305 | 315 | | |
306 | 316 | | |
307 | 317 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
213 | 226 | | |
214 | 227 | | |
215 | 228 | | |
| |||
436 | 449 | | |
437 | 450 | | |
438 | 451 | | |
| 452 | + | |
439 | 453 | | |
440 | 454 | | |
441 | 455 | | |
| |||
0 commit comments