Skip to content

[XNNPACK] Lower upsample_nearest2d x2 as concatenate4 + depth_to_space_2d - #24113

Open
borys3kk wants to merge 3 commits into
pytorch:mainfrom
borys3kk:xnnpack-upsample-nearest2d-main
Open

borys3kk wants to merge 3 commits into
pytorch:mainfrom
borys3kk:xnnpack-upsample-nearest2d-main

Conversation

@borys3kk

@borys3kk borys3kk commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Profiling the MI-GAN inpainting model (a conv encoder-decoder, fp16) on XNNPACK showed the graph split into 7-8 delegate partitions around portable upsample_nearest2d ops. This PR lowers upsample_nearest2d x2.

XNNPACK has no nearest resize, and its static_broadcast is defined but returns xnn_status_unsupported_parameter at reshape time. Nearest x2 is instead lowered as concatenate4 of the input with itself along channels ([N, H, W, 4C]), followed by depth_to_space_2d(block_size=2). This adds an XNNDepthToSpace2d node, appended at the end of the XNodeUnion in schema.fbs / runtime_schema.fbs so existing node IDs don't change, plus defineDepthToSpace2dNode in XNNCompiler.cpp. UpsampleNearest2dConfig only partitions static 4D inputs with an exact x2 output. An explicit scale_factors that is not exactly 2.0 stays portable as well: PyTorch indexes with the given scale rather than the size ratio, so scale_factor=2.004 doubles the size but picks different pixels. Static quantization is supported: the quantizer annotates the op with input/output shared qparams, and the intermediate value carries them, since neither node changes values.

The ChannelsLastTaggedReshapePass change that was originally part of this PR (keeping NHWC across scalar and low-rank broadcast operands) moved to its own PR at review request, together with the fix for the dynamic-shape low-rank operand case reported below.

Measured on MI-GAN 256/512 fp16 with the host runtime, together with that pass change, on an ExecuTorch 1.4.1-based branch: XNNPACK partitions went from 7/8 to 1, the delegated share of run time from 87/82% to 98/95%, and Method::execute dropped by 29%/28%. The output was unchanged (max abs diff 2e-3, fp16 noise).

The new node needs an updated runtime. A program that uses it fails to load on an older runtime.

Review order: the serialization schema and XNNCompiler.cpp, then op_upsample_nearest2d.py with UpsampleNearest2dConfig and the quantizer annotator, then the tests.

Test plan

Tested on macOS arm64, with pybindings built from this branch (cmake --preset pybind, target portable_lib) so that the run-on-delegate stages use the new runtime node.

backends/xnnpack/test/ops/test_upsample_nearest2d.py covers x2 by scale and by size, fp16, fp32 and qs8 conv-upsample-conv lowered to one partition, and x3 and scale_factor=2.004 staying portable.

python -m pytest backends/xnnpack/test/ops/test_upsample_nearest2d.py backends/xnnpack/test/passes/test_channels_last_tagged_reshape.py backends/xnnpack/test/test_xnnpack_partitioner.py
# 35 passed

lintrunner reports no issues on the changed files.

…ow-rank broadcast operands

Profiling a conv encoder-decoder (MI-GAN inpainting, fp16) showed the graph
split into 7-8 XNNPACK partitions around portable upsample_nearest2d ops and
~25% of the time spent in NCHW<->NHWC transposes.

upsample_nearest2d.vec with an exact x2 scale on a static 4D input is now
partitioned and lowered as concatenate4 of the input along channels followed
by depth_to_space_2d(block_size=2). XNNPACK has no nearest resize and its
static_broadcast is not implemented at run time. This adds an
XNNDepthToSpace2d node to the flatbuffer schemas and XNNCompiler. Static
quantization is supported: the quantizer annotates the op with shared
input/output qparams and the intermediate value carries them. Other scales,
including an explicit scale_factors that is not exactly 2.0, stay portable.

In ChannelsLastTaggedReshapePass, single-element operands of
add/sub/mul/div/maximum/minimum (a lifted Python scalar such as the gain in
x * 1.4142) no longer take part in the layout decision, and rank < 4
operands that broadcast over the trailing dims (a [H, W] map added to
[N, C, H, W]) are reshaped to 4D with leading ones so they follow the NHWC
operand instead of dragging the activation back to NCHW. Dequantized
operands keep their rank, since a view between the dequantize and the op
would carry no quant params.
@pytorch-bot

pytorch-bot Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24113

Note: Links to docs will display an error until the docs builds have been completed.

⚠️ 15 Awaiting Approval

As of commit 7877056 with merge base 422cc39 (image):

AWAITING APPROVAL - The following workflows need approval before CI can run:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 23, 2026
@borys3kk

Copy link
Copy Markdown
Contributor Author

@pytorchbot label "release notes: xnnpack"

@borys3kk
borys3kk marked this pull request as ready for review October 26, 2026 14:45
@borys3kk
borys3kk requested a review from digantdesai as a code owner October 26, 2026 14:45
@pytorch-bot pytorch-bot Bot added the release notes: xnnpack Changes to the XNNPack backend delegate label Oct 26, 2026
@executorch-triage executorch-triage Bot added the community: contribution PRs coming from community (excluding hardware partners) label Oct 26, 2026
@nil-is-all nil-is-all added the module: xnnpack Issues related to xnnpack delegation and the code under backends/xnnpack/ label Oct 26, 2026
@JakeStevens

Copy link
Copy Markdown
Contributor

sorry, can we split this into two PRs? to keep the changes atomic per pr instead of squishing them together. the upsample changes are additive and lower risk.

that being said, a lower-rank operand with symbolic dims breaks lowering with these changes. Before, a dynamic-shape model with conv_out + y, where y is [H, W] or [C, H, W] with dynamic H/W, now fails in preprocess. On main it lowered fine by falling back to NCHW.

@nil-is-all nil-is-all added the need-user-input The issue needs more information from the reporter before moving forward label Oct 26, 2026
@borys3kk

Copy link
Copy Markdown
Contributor Author

sorry, can we split this into two PRs? to keep the changes atomic per pr instead of squishing them together. the upsample changes are additive and lower risk.

that being said, a lower-rank operand with symbolic dims breaks lowering with these changes. Before, a dynamic-shape model with conv_out + y, where y is [H, W] or [C, H, W] with dynamic H/W, now fails in preprocess. On main it lowered fine by falling back to NCHW.

I will look into that tomorrow

borys3kk added a commit to borys3kk/executorch that referenced this pull request Oct 27, 2026
@borys3kk borys3kk changed the title [XNNPACK] Lower upsample_nearest2d x2 and keep NHWC across scalar / low-rank broadcast operands [XNNPACK] Lower upsample_nearest2d x2 as concatenate4 + depth_to_space_2d Oct 27, 2026
@borys3kk

Copy link
Copy Markdown
Contributor Author

Done, split in two: this PR now only lowers upsample_nearest2d, the ChannelsLastTaggedReshapePass change moved to #24183.

Thanks for the dynamic-shape report, reproduced it: unsqueeze_to_4d put the symbolic dims into the view_copy shape, which XNNStaticReshape rejects. In #24183 an operand with symbolic dims keeps the NCHW path as on main, with a test for conv_out + y with y of shape [H, W] and dynamic H/W that also runs at a second input size.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. community: contribution PRs coming from community (excluding hardware partners) module: xnnpack Issues related to xnnpack delegation and the code under backends/xnnpack/ need-user-input The issue needs more information from the reporter before moving forward release notes: xnnpack Changes to the XNNPack backend delegate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants