Skip to content

Commit 4f3502b

Browse files
committed
Update on "Arm backend: Add INT16 support to rescale operation"
Add INT16 support for RequantizeNode rescale operations in ExecutorTorch ARM backend. This follows the pattern established for linear, mul, sigmoid, tanh, slice, view/transpose, cat, and FCNode operations, extending int16 support to RequantizeNode rescale operations. Changes: - Add INT16 dtype validation support in op_rescale.py - Enable rescale operations for 16A8W quantization configuration The 16A8W configuration uses 16-bit activations with 8-bit weights, enabling higher precision for activations while maintaining weight efficiency. RequantizeNode rescale operations are essential for proper quantization scaling in the 16A8W pipeline. Differential Revision: [D80513725](https://our.internmc.facebook.com/intern/diff/D80513725/) cc digantdesai freddan80 per zingo oscarandersson8218 [ghstack-poisoned]
2 parents cfcb68f + 6e348b5 commit 4f3502b

File tree

159 files changed

+4203
-1626
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+4203
-1626
lines changed

.ci/scripts/test_model.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function ExportModel-Xnnpack {
3434
[bool]$quantize
3535
)
3636

37-
if $(quantize) {
37+
if ($quantize) {
3838
python -m examples.xnnpack.aot_compiler --model_name="${MODEL_NAME}" --delegate --quantize | Write-Host
3939
$modelFile = "$($modelName)_xnnpack_q8.pte"
4040
} else {
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/bin/bash
2+
# === CI Wheel Build & Test Script ===
3+
4+
# Exit immediately on error, print each command, and capture all output to build.log
5+
set -e
6+
set -x
7+
exec > >(tee -i build.log) 2>&1
8+
9+
# Save repo root
10+
REPO_ROOT=$(pwd)
11+
12+
# ----------------------------
13+
# Dynamically create script_qnn_wheel_test.py
14+
# ----------------------------
15+
cat > "/tmp/script_qnn_wheel_test.py" << 'EOF'
16+
# pyre-ignore-all-errors
17+
import argparse
18+
19+
import torch
20+
from executorch.backends.qualcomm.quantizer.quantizer import QnnQuantizer
21+
from executorch.backends.qualcomm.utils.utils import (
22+
generate_htp_compiler_spec,
23+
generate_qnn_executorch_compiler_spec,
24+
get_soc_to_chipset_map,
25+
to_edge_transform_and_lower_to_qnn,
26+
)
27+
from executorch.exir.backend.utils import format_delegated_graph
28+
from executorch.examples.models.model_factory import EagerModelFactory
29+
from executorch.exir.capture._config import ExecutorchBackendConfig
30+
from executorch.extension.export_util.utils import save_pte_program
31+
from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e, prepare_qat_pt2e
32+
33+
def main() -> None:
34+
parser = argparse.ArgumentParser()
35+
parser.add_argument("-f", "--output_folder", type=str, default="", help="The folder to store the exported program")
36+
parser.add_argument("--soc", type=str, default="SM8650", help="Specify the SoC model.")
37+
parser.add_argument("-q", "--quantization", choices=["ptq", "qat"], help="Run post-traininig quantization.")
38+
args = parser.parse_args()
39+
40+
class LinearModule(torch.nn.Module):
41+
def __init__(self):
42+
super().__init__()
43+
self.linear = torch.nn.Linear(3, 3)
44+
def forward(self, arg):
45+
return self.linear(arg)
46+
def get_example_inputs(self):
47+
return (torch.randn(3, 3),)
48+
49+
model = LinearModule()
50+
example_inputs = model.get_example_inputs()
51+
52+
if args.quantization:
53+
quantizer = QnnQuantizer()
54+
m = torch.export.export(model.eval(), example_inputs, strict=True).module()
55+
if args.quantization == "qat":
56+
m = prepare_qat_pt2e(m, quantizer)
57+
m(*example_inputs)
58+
elif args.quantization == "ptq":
59+
m = prepare_pt2e(m, quantizer)
60+
m(*example_inputs)
61+
m = convert_pt2e(m)
62+
else:
63+
m = model
64+
65+
use_fp16 = True if args.quantization is None else False
66+
backend_options = generate_htp_compiler_spec(use_fp16=use_fp16)
67+
compile_spec = generate_qnn_executorch_compiler_spec(
68+
soc_model=get_soc_to_chipset_map()[args.soc],
69+
backend_options=backend_options,
70+
)
71+
delegated_program = to_edge_transform_and_lower_to_qnn(m, example_inputs, compile_spec)
72+
output_graph = format_delegated_graph(delegated_program.exported_program().graph_module)
73+
# Ensure QnnBackend is in the output graph
74+
assert "QnnBackend" in output_graph
75+
executorch_program = delegated_program.to_executorch(
76+
config=ExecutorchBackendConfig(extract_delegate_segments=False)
77+
)
78+
save_pte_program(executorch_program, "linear", args.output_folder)
79+
80+
if __name__ == "__main__":
81+
main()
82+
EOF
83+
84+
# ----------------------------
85+
# Wheel build and .so checks
86+
# ----------------------------
87+
echo "=== Building Wheel Package ==="
88+
source .ci/scripts/utils.sh
89+
install_executorch
90+
EXECUTORCH_BUILDING_WHEEL=1 python setup.py bdist_wheel
91+
unset EXECUTORCH_BUILDING_WHEEL
92+
93+
WHEEL_FILE=$(ls dist/*.whl | head -n 1)
94+
echo "Found wheel: $WHEEL_FILE"
95+
96+
PYTHON_VERSION=$1
97+
# ----------------------------
98+
# Check wheel does NOT contain qualcomm/sdk
99+
# ----------------------------
100+
echo "Checking wheel does not contain qualcomm/sdk..."
101+
SDK_FILES=$(unzip -l "$WHEEL_FILE" | awk '{print $4}' | grep "executorch/backends/qualcomm/sdk" || true)
102+
if [ -n "$SDK_FILES" ]; then
103+
echo "ERROR: Wheel package contains unexpected qualcomm/sdk files:"
104+
echo "$SDK_FILES"
105+
exit 1
106+
else
107+
echo "OK: No qualcomm/sdk files found in wheel"
108+
fi
109+
110+
# ----------------------------
111+
# Check .so files in the wheel
112+
# ----------------------------
113+
echo "Checking for .so files inside the wheel..."
114+
WHEEL_SO_FILES=$(unzip -l "$WHEEL_FILE" | awk '{print $4}' | grep "executorch/backends/qualcomm/python" || true)
115+
if [ -z "$WHEEL_SO_FILES" ]; then
116+
echo "ERROR: No .so files found in wheel under executorch/backends/qualcomm/python"
117+
exit 1
118+
else
119+
echo "Wheel contains the following .so files:"
120+
echo "$WHEEL_SO_FILES"
121+
fi
122+
123+
# ----------------------------
124+
# Helpers
125+
# ----------------------------
126+
get_site_packages_dir () {
127+
local PYBIN="$1"
128+
"$PYBIN" - <<'PY'
129+
import sysconfig, sys
130+
print(sysconfig.get_paths().get("purelib") or sysconfig.get_paths().get("platlib"))
131+
PY
132+
}
133+
134+
run_core_tests () {
135+
local PYBIN="$1" # path to python
136+
local PIPBIN="$2" # path to pip
137+
local LABEL="$3" # label to print (conda/venv)
138+
139+
echo "=== [$LABEL] Installing wheel & deps ==="
140+
"$PIPBIN" install --upgrade pip
141+
"$PIPBIN" install "$WHEEL_FILE"
142+
"$PIPBIN" install torch=="2.9.0.dev20250906" --index-url "https://download.pytorch.org/whl/nightly/cpu"
143+
"$PIPBIN" install --pre torchao --index-url "https://download.pytorch.org/whl/nightly/cpu"
144+
145+
echo "=== [$LABEL] Import smoke tests ==="
146+
"$PYBIN" -c "import executorch; print('executorch imported successfully')"
147+
"$PYBIN" -c "import executorch.backends.qualcomm; print('executorch.backends.qualcomm imported successfully')"
148+
149+
echo "=== [$LABEL] List installed executorch/backends/qualcomm/python ==="
150+
local SITE_DIR
151+
SITE_DIR="$(get_site_packages_dir "$PYBIN")"
152+
local SO_DIR="$SITE_DIR/executorch/backends/qualcomm/python"
153+
ls -l "$SO_DIR" || echo "Folder does not exist!"
154+
155+
echo "=== [$LABEL] Run export script to generate linear.pte ==="
156+
(cd "$REPO_ROOT" && "$PYBIN" "/tmp/script_qnn_wheel_test.py")
157+
158+
if [ -f "$REPO_ROOT/linear.pte" ]; then
159+
echo "[$LABEL] Model file linear.pte successfully created"
160+
else
161+
echo "ERROR: [$LABEL] Model file linear.pte was not created"
162+
exit 1
163+
fi
164+
}
165+
166+
# ----------------------------
167+
# Conda environment setup & tests
168+
# ----------------------------
169+
echo "=== Testing in Conda env ==="
170+
TEMP_ENV_DIR=$(mktemp -d)
171+
echo "Using temporary directory for conda: $TEMP_ENV_DIR"
172+
conda create -y -p "$TEMP_ENV_DIR/env" python=$PYTHON_VERSION
173+
# derive python/pip paths inside the conda env
174+
CONDA_PY="$TEMP_ENV_DIR/env/bin/python"
175+
CONDA_PIP="$TEMP_ENV_DIR/env/bin/pip"
176+
# Some images require conda run; keep pip/python direct to simplify path math
177+
run_core_tests "$CONDA_PY" "$CONDA_PIP" "conda"
178+
179+
# Cleanup conda env
180+
conda env remove -p "$TEMP_ENV_DIR/env" -y || true
181+
rm -rf "$TEMP_ENV_DIR"
182+
183+
# ----------------------------
184+
# Python venv setup & tests
185+
# ----------------------------
186+
echo "=== Testing in Python venv ==="
187+
TEMP_VENV_DIR=$(mktemp -d)
188+
echo "Using temporary directory for venv: $TEMP_VENV_DIR"
189+
python3 -m venv "$TEMP_VENV_DIR/venv"
190+
VENV_PY="$TEMP_VENV_DIR/venv/bin/python"
191+
VENV_PIP="$TEMP_VENV_DIR/venv/bin/pip"
192+
193+
# Ensure venv has wheel/build basics if needed
194+
"$VENV_PIP" install --upgrade pip
195+
196+
run_core_tests "$VENV_PY" "$VENV_PIP" "venv"
197+
198+
# Cleanup venv
199+
rm -rf "$TEMP_VENV_DIR"
200+
201+
echo "=== All tests completed! ==="

.ci/scripts/wheel/pre_build_script.sh

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,26 @@ set -euxo pipefail
99

1010
# This script is run before building ExecuTorch binaries
1111

12+
# Clone nested submodules for tokenizers - this is a workaround for recursive
13+
# submodule clone failing due to path length limitations on Windows. Eventually,
14+
# we should update the core job in test-infra to enable long paths before
15+
# checkout to avoid needing to do this.
16+
pushd extension/llm/tokenizers
17+
git submodule update --init
18+
popd
19+
20+
# On Windows, enable symlinks and re-checkout the current revision to create
21+
# the symlinked src/ directory. This is needed to build the wheel.
22+
UNAME_S=$(uname -s)
23+
if [[ $UNAME_S == *"MINGW"* || $UNAME_S == *"MSYS"* ]]; then
24+
echo "Enabling symlinks on Windows"
25+
git config core.symlinks true
26+
git checkout -f HEAD
27+
fi
28+
1229
# Manually install build requirements because `python setup.py bdist_wheel` does
1330
# not install them. TODO(dbort): Switch to using `python -m build --wheel`,
1431
# which does install them. Though we'd need to disable build isolation to be
1532
# able to see the installed torch package.
1633

17-
"${GITHUB_WORKSPACE}/${REPOSITORY}/install_requirements.sh" --example
34+
"${GITHUB_WORKSPACE}/${REPOSITORY}/install_requirements.sh" --example

.ci/scripts/wheel/test_windows.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
# All rights reserved.
4+
#
5+
# This source code is licensed under the BSD-style license found in the
6+
# LICENSE file in the root directory of this source tree.
7+
8+
from typing import List
9+
10+
import torch
11+
from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner
12+
from executorch.examples.models import Backend, Model, MODEL_NAME_TO_MODEL
13+
from executorch.examples.models.model_factory import EagerModelFactory
14+
from executorch.examples.xnnpack import MODEL_NAME_TO_OPTIONS
15+
from executorch.examples.xnnpack.quantization.utils import quantize as quantize_xnn
16+
from executorch.exir import EdgeCompileConfig, to_edge_transform_and_lower
17+
from executorch.extension.pybindings.portable_lib import (
18+
_load_for_executorch_from_buffer,
19+
)
20+
from test_base import ModelTest
21+
22+
23+
def test_model_xnnpack(model: Model, quantize: bool) -> None:
24+
model_instance, example_inputs, _, _ = EagerModelFactory.create_model(
25+
*MODEL_NAME_TO_MODEL[str(model)]
26+
)
27+
28+
model_instance.eval()
29+
ref_outputs = model_instance(*example_inputs)
30+
31+
if quantize:
32+
quant_type = MODEL_NAME_TO_OPTIONS[str(model)].quantization
33+
model_instance = torch.export.export_for_training(
34+
model_instance, example_inputs
35+
)
36+
model_instance = quantize_xnn(
37+
model_instance.module(), example_inputs, quant_type
38+
)
39+
40+
lowered = to_edge_transform_and_lower(
41+
torch.export.export(model_instance, example_inputs),
42+
partitioner=[XnnpackPartitioner()],
43+
compile_config=EdgeCompileConfig(
44+
_check_ir_validity=False,
45+
),
46+
).to_executorch()
47+
48+
loaded_model = _load_for_executorch_from_buffer(lowered.buffer)
49+
et_outputs = loaded_model([*example_inputs])
50+
51+
if isinstance(ref_outputs, torch.Tensor):
52+
ref_outputs = (ref_outputs,)
53+
54+
assert len(ref_outputs) == len(et_outputs)
55+
for i in range(len(ref_outputs)):
56+
torch.testing.assert_close(ref_outputs[i], et_outputs[i], atol=1e-4, rtol=1e-5)
57+
58+
59+
def run_tests(model_tests: List[ModelTest]) -> None:
60+
for model_test in model_tests:
61+
if model_test.backend == Backend.Xnnpack:
62+
test_model_xnnpack(model_test.model, quantize=False)
63+
else:
64+
raise RuntimeError(f"Unsupported backend {model_test.backend}.")
65+
66+
67+
if __name__ == "__main__":
68+
run_tests(
69+
model_tests=[
70+
ModelTest(
71+
model=Model.Mv3,
72+
backend=Backend.Xnnpack,
73+
),
74+
]
75+
)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
REM This is lightly modified from the torchvision Windows build logic.
2+
REM See https://github.com/pytorch/vision/blob/main/packaging/windows/internal/vc_env_helper.bat
3+
4+
@echo on
5+
6+
set VC_VERSION_LOWER=17
7+
set VC_VERSION_UPPER=18
8+
9+
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -legacy -products * -version [%VC_VERSION_LOWER%^,%VC_VERSION_UPPER%^) -property installationPath`) do (
10+
if exist "%%i" if exist "%%i\VC\Auxiliary\Build\vcvarsall.bat" (
11+
set "VS15INSTALLDIR=%%i"
12+
set "VS15VCVARSALL=%%i\VC\Auxiliary\Build\vcvarsall.bat"
13+
goto vswhere
14+
)
15+
)
16+
17+
:vswhere
18+
if "%VSDEVCMD_ARGS%" == "" (
19+
call "%VS15VCVARSALL%" x64 || exit /b 1
20+
) else (
21+
call "%VS15VCVARSALL%" x64 %VSDEVCMD_ARGS% || exit /b 1
22+
)
23+
24+
@echo on
25+
26+
if "%CU_VERSION%" == "xpu" call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat"
27+
28+
set DISTUTILS_USE_SDK=1
29+
30+
set args=%1
31+
shift
32+
:start
33+
if [%1] == [] goto done
34+
set args=%args% %1
35+
shift
36+
goto start
37+
38+
:done
39+
if "%args%" == "" (
40+
echo Usage: vc_env_helper.bat [command] [args]
41+
echo e.g. vc_env_helper.bat cl /c test.cpp
42+
)
43+
44+
set work_dir=%CD%
45+
if exist setup.py (
46+
echo "Creating symlink..."
47+
REM Setup a symlink to shorten the path length.
48+
REM Note that the ET directory has to be named "executorch".
49+
cd %GITHUB_WORKSPACE%
50+
if not exist et\ (
51+
mkdir et
52+
)
53+
cd et
54+
echo Work dir: %work_dir%
55+
if not exist executorch\ (
56+
mklink /d executorch %work_dir%
57+
)
58+
cd executorch
59+
)
60+
61+
%args% || exit /b 1

0 commit comments

Comments
 (0)