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 intopytorch:mainfrom
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.
|
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. |
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.