Fix #18562: Method.execute() silently produces wrong results for no...#18935
Fix #18562: Method.execute() silently produces wrong results for no...#18935JiwaniZakir wants to merge 1 commit into
Method.execute() silently produces wrong results for no...#18935Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/18935
Note: Links to docs will display an error until the docs builds have been completed. ❗ 1 Active SEVsThere are 1 currently active SEVs. If your PR is affected, please view them below: ❌ 7 New Failures, 2 Unrelated FailuresAs of commit 531ef37 with merge base ec8d70b ( NEW FAILURES - The following jobs have failed:
BROKEN TRUNK - The following jobs failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Fixes Method.execute() producing incorrect results when given non-contiguous torch.Tensor inputs by normalizing them to contiguous tensors before dispatching to the underlying runtime.
Changes:
- Normalize non-contiguous
torch.Tensorinputs via.contiguous()insideMethod.execute(). - Add a regression test covering non-contiguous inputs producing identical outputs to contiguous equivalents.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| runtime/init.py | Ensures Method.execute() passes contiguous tensor data to the C++ runtime by copying non-contiguous tensors. |
| runtime/test/test_runtime.py | Adds a regression test validating correctness for non-contiguous tensor inputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Returns: | ||
| A list of output values, typically torch.Tensor objects. | ||
| """ | ||
| import torch |
There was a problem hiding this comment.
Importing torch inside execute() adds overhead on every call and also changes when ImportError would surface (now at runtime call time). Prefer a module-level import, or at least import lazily only when a torch.Tensor is actually present (e.g., scan inputs first), optionally with a clear error if torch isn't available.
| inputs = [ | ||
| x.contiguous() if isinstance(x, torch.Tensor) and not x.is_contiguous() else x | ||
| for x in inputs | ||
| ] | ||
| return self._method(inputs) |
There was a problem hiding this comment.
This always allocates a new Python list and iterates all inputs even when all tensors are already contiguous (or when there are no tensors). For a hot path like execute(), consider only creating a copied container if at least one replacement is needed; otherwise, pass through the original inputs unchanged to avoid unnecessary allocations.
| inputs = [ | |
| x.contiguous() if isinstance(x, torch.Tensor) and not x.is_contiguous() else x | |
| for x in inputs | |
| ] | |
| return self._method(inputs) | |
| converted_inputs = None | |
| for i, x in enumerate(inputs): | |
| if isinstance(x, torch.Tensor) and not x.is_contiguous(): | |
| if converted_inputs is None: | |
| converted_inputs = list(inputs) | |
| converted_inputs[i] = x.contiguous() | |
| return self._method(converted_inputs if converted_inputs is not None else inputs) |
| runtime = Runtime.get() | ||
| program = runtime.load_program(ep.buffer, verification=Verification.Minimal) | ||
|
|
||
| # Make a non-contiguous version of the first input via transpose. |
There was a problem hiding this comment.
The comment says the non-contiguous tensor is created 'via transpose', but the code uses unsqueeze/expand/permute and slicing. Update the comment to match the actual approach to avoid confusion during future maintenance.
| # Make a non-contiguous version of the first input via transpose. | |
| # Make a non-contiguous version of the first input via | |
| # unsqueeze/expand/permute followed by slicing. |
|
@claude review |
|
Claude finished @kirklandsign's task in 3m 21s —— View job Review of PR #18935
SummaryThis PR adds a Issue: PR description is misleadingThe PR description and issue #18562 state that non-contiguous tensors cause "silently wrong outputs with no error or warning." This is only partially accurate:
The fix is still useful as a defensive measure and for improving UX (converting automatically rather than throwing a confusing error), but the framing of "silently wrong results" applies only in certain build configurations. Code concerns1. This adds an import lookup on every However, I notice this module currently avoids importing 2. Unnecessary list allocation on every call ( The list comprehension creates a new list on every 3. Test comment mismatch ( The comment says "via transpose" but the code uses # Current (inaccurate):
# Make a non-contiguous version of the first input via transpose.
# Should be:
# Make a non-contiguous version of the first input via expand/permute.4. Consider whether this is the right layer for the fix The contiguity check already exists in the C++ pybindings for portable mode. Adding it again in the Python wrapper means there's now redundant validation in the portable mode path (Python converts to contiguous, then C++ checks contiguity again). This isn't harmful but adds conceptual overhead. An argument could be made that the fix should be in the C++ pybindings layer instead (calling Minor nits
VerdictThe fix is correct and the test is reasonable. The main concerns are:
|
mergennachin
left a comment
There was a problem hiding this comment.
We should look at dim order instead of adding torch dependency to the runtime.
Cc @Gasoonjia
Closes #18562
Summary
Method.execute()inruntime/__init__.pypassed input tensors directly to the underlying C++ runtime without checking memory contiguity. The runtime reads fromdata_ptrassuming a contiguous layout, so non-contiguous tensors — most commonly produced by.permute(),.transpose(), or.expand()— caused silently wrong outputs with no error or warning.Fix: in
Method.execute(), normalize any non-contiguoustorch.Tensorinput to a contiguous copy before dispatch. This is a one-time copy per non-contiguous input and has no effect on already-contiguous tensors.Test plan
Added
test_execute_non_contiguous_inputsinruntime/test/test_runtime.py. The test constructs a non-contiguous 2-D tensor from the existingModuleAddfixture input by expanding and permuting (unsqueeze(0).expand(3, -1, -1).permute(1, 2, 0)[:, :, 0]), verifiesis_contiguous()isFalseandtorch.equalwith the original isTrue, then assertstorch.allclosebetween the output from the non-contiguous input and the output from the original contiguous input.This PR was created with AI assistance (Claude). The changes were reviewed by quality gates and a critic model before submission.