Skip to content

Commit 866265e

Browse files
authored
Merge branch 'main' into mtk-android
2 parents 50b7da6 + 2c43190 commit 866265e

File tree

276 files changed

+5557
-2215
lines changed

Some content is hidden

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

276 files changed

+5557
-2215
lines changed

.ci/docker/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ case "${IMAGE_NAME}" in
4141
LINTRUNNER=""
4242
CLANG_VERSION=12
4343
# From https://developer.android.com/ndk/downloads
44-
ANDROID_NDK_VERSION=r26c
44+
ANDROID_NDK_VERSION=r27b
4545
;;
4646
*)
4747
echo "Invalid image name ${IMAGE_NAME}"

.ci/scripts/build_llama_android.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ install_executorch_and_backend_lib() {
1919
cmake -DBUCK2="${BUCK2}" \
2020
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \
2121
-DANDROID_ABI="${ANDROID_ABI}" \
22-
-DANDROID_PLATFORM=android-23 \
2322
-DCMAKE_INSTALL_PREFIX=cmake-android-out \
2423
-DCMAKE_BUILD_TYPE=Release \
2524
-DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \
@@ -41,16 +40,15 @@ build_llama_runner() {
4140
cmake -DBUCK2="${BUCK2}" \
4241
-DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK"/build/cmake/android.toolchain.cmake \
4342
-DANDROID_ABI="${ANDROID_ABI}" \
44-
-DANDROID_PLATFORM=android-23 \
4543
-DCMAKE_INSTALL_PREFIX=cmake-android-out \
4644
-DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=python \
4745
-DEXECUTORCH_BUILD_XNNPACK=ON \
4846
-DEXECUTORCH_BUILD_KERNELS_OPTIMIZED=ON \
4947
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
5048
-DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON \
51-
-Bcmake-android-out/examples/models/llama2 examples/models/llama2
49+
-Bcmake-android-out/examples/models/llama examples/models/llama
5250

53-
cmake --build cmake-android-out/examples/models/llama2 -j4 --config Release
51+
cmake --build cmake-android-out/examples/models/llama -j4 --config Release
5452
}
5553
install_flatc_from_source
5654
install_executorch_and_backend_lib

.ci/scripts/test_llama.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ cmake_install_executorch_libraries() {
125125

126126
cmake_build_llama_runner() {
127127
echo "Building llama runner"
128-
dir="examples/models/llama2"
128+
dir="examples/models/llama"
129129
retry cmake \
130130
-DCMAKE_INSTALL_PREFIX=cmake-out \
131131
-DCMAKE_BUILD_TYPE=Debug \
@@ -206,7 +206,7 @@ if [[ "${QNN}" == "ON" ]]; then
206206
EXPORT_ARGS="${EXPORT_ARGS} -kv -v --qnn --disable_dynamic_shape"
207207
fi
208208
# Add dynamically linked library location
209-
$PYTHON_EXECUTABLE -m examples.models.llama2.export_llama ${EXPORT_ARGS}
209+
$PYTHON_EXECUTABLE -m examples.models.llama.export_llama ${EXPORT_ARGS}
210210

211211
# Create tokenizer.bin.
212212
echo "Creating tokenizer.bin"
@@ -219,15 +219,15 @@ echo "Running ${EXPORTED_MODEL_NAME} in portable mode"
219219
if [[ "${BUILD_TOOL}" == "buck2" ]]; then
220220
# Run model.
221221
# shellcheck source=/dev/null
222-
$BUCK run examples/models/llama2:main -- ${RUNTIME_ARGS} > result.txt
222+
$BUCK run examples/models/llama:main -- ${RUNTIME_ARGS} > result.txt
223223
elif [[ "${BUILD_TOOL}" == "cmake" ]]; then
224224
cmake_install_executorch_libraries
225225
cmake_build_llama_runner
226226
# Run llama runner
227227
NOW=$(date +"%H:%M:%S")
228228
echo "Starting to run llama runner at ${NOW}"
229229
# shellcheck source=/dev/null
230-
cmake-out/examples/models/llama2/llama_main ${RUNTIME_ARGS} > result.txt
230+
cmake-out/examples/models/llama/llama_main ${RUNTIME_ARGS} > result.txt
231231
NOW=$(date +"%H:%M:%S")
232232
echo "Finished at ${NOW}"
233233
else

.ci/scripts/test_llava.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ cmake_install_executorch_libraries_for_android() {
5656
cmake \
5757
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
5858
-DANDROID_ABI=arm64-v8a \
59-
-DANDROID_PLATFORM=android-23 \
6059
${EXECUTORCH_COMMON_CMAKE_ARGS} \
6160
-B${BUILD_DIR} .
6261

@@ -93,7 +92,6 @@ cmake_build_llava_runner_for_android() {
9392
cmake \
9493
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
9594
-DANDROID_ABI=arm64-v8a \
96-
-DANDROID_PLATFORM=android-23 \
9795
${LLAVA_COMMON_CMAKE_ARGS} \
9896
-DCMAKE_PREFIX_PATH="$python_lib" \
9997
-DLLAVA_RUNNER_NO_TORCH_DUMMY_IMAGE=ON \

.ci/scripts/test_model.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ run_portable_executor_runner() {
7575
test_model() {
7676
if [[ "${MODEL_NAME}" == "llama2" ]]; then
7777
# Install requirements for export_llama
78-
bash examples/models/llama2/install_requirements.sh
79-
# Test export_llama script: python3 -m examples.models.llama2.export_llama
80-
"${PYTHON_EXECUTABLE}" -m examples.models.llama2.export_llama -c examples/models/llama2/params/demo_rand_params.pth -p examples/models/llama2/params/demo_config.json
78+
bash examples/models/llama/install_requirements.sh
79+
# Test export_llama script: python3 -m examples.models.llama.export_llama
80+
"${PYTHON_EXECUTABLE}" -m examples.models.llama.export_llama -c examples/models/llama/params/demo_rand_params.pth -p examples/models/llama/params/demo_config.json
8181
run_portable_executor_runner
8282
rm "./${MODEL_NAME}.pte"
8383
fi

.github/scripts/extract_benchmark_results.py

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import logging
1010
import os
1111
import re
12-
import time
1312
import zipfile
1413
from argparse import Action, ArgumentParser, Namespace
1514
from io import BytesIO
@@ -26,12 +25,15 @@
2625

2726
# iOS-related regexes and variables
2827
IOS_TEST_SPEC_REGEX = re.compile(
29-
r"Test Case\s+'-\[(?P<test_class>\w+)\s+(?P<test_name>\w+)\]'\s+measured\s+\[(?P<metric>.+)\]\s+average:\s+(?P<value>[\d\.]+),"
28+
r"Test Case\s+'-\[(?P<test_class>\w+)\s+(?P<test_name>[\w\+]+)\]'\s+measured\s+\[(?P<metric>.+)\]\s+average:\s+(?P<value>[\d\.]+),"
3029
)
3130
IOS_TEST_NAME_REGEX = re.compile(
32-
r"test_(?P<method>forward|load|generate)_(?P<model_name>\w+)_pte.*iOS_(?P<ios_ver>\w+)_iPhone(?P<iphone_ver>\w+)"
31+
r"test_(?P<method>forward|load|generate)_(?P<model_name>[\w\+]+)_pte.*iOS_(?P<ios_ver>\w+)_iPhone(?P<iphone_ver>\w+)"
32+
)
33+
# The backend name could contain +, i.e. tinyllama_xnnpack+custom+qe_fp32
34+
IOS_MODEL_NAME_REGEX = re.compile(
35+
r"(?P<model>[^_]+)_(?P<backend>[\w\+]+)_(?P<dtype>\w+)"
3336
)
34-
IOS_MODEL_NAME_REGEX = re.compile(r"(?P<model>[^_]+)_(?P<backend>\w+)_(?P<dtype>\w+)")
3537

3638

3739
class ValidateArtifacts(Action):
@@ -159,19 +161,8 @@ def initialize_ios_metadata(test_name: str) -> Dict[str, any]:
159161
ios_ver = m.group("ios_ver").replace("_", ".")
160162
iphone_ver = m.group("iphone_ver").replace("_", ".")
161163

162-
# NB: This looks brittle, but unless we can return iOS benchmark results in JSON
163-
# format by the test, the mapping is needed to match with Android test
164-
if method == "load":
165-
metric = "model_load_time(ms)"
166-
elif method == "forward":
167-
metric = (
168-
"generate_time(ms)"
169-
if "llama" in model_name
170-
else "avg_inference_latency(ms)"
171-
)
172-
elif method == "generate":
173-
metric = "token_per_sec"
174-
164+
# The default backend and quantization dtype if the script couldn't extract
165+
# them from the model name
175166
backend = ""
176167
quantization = "unknown"
177168

@@ -194,8 +185,9 @@ def initialize_ios_metadata(test_name: str) -> Dict[str, any]:
194185
"availMem": 0,
195186
"totalMem": 0,
196187
},
197-
"metric": metric,
188+
"method": method,
198189
# These fields will be populated later by extract_ios_metric
190+
"metric": "",
199191
"actualValue": 0,
200192
"targetValue": 0,
201193
}
@@ -210,10 +202,38 @@ def extract_ios_metric(
210202
"""
211203
Map the metric name from iOS xcresult to the benchmark result
212204
"""
213-
if metric_name == "Clock Monotonic Time, s":
214-
# The benchmark value is in ms
215-
benchmark_result["actualValue"] = metric_value * 1000
216-
elif metric_name == "Tokens Per Second, t/s":
205+
method = benchmark_result.get("method", "")
206+
if not method:
207+
return benchmark_result
208+
209+
# NB: This looks brittle, but unless we can return iOS benchmark results in JSON
210+
# format by the test, the mapping is needed to match with Android test
211+
if method == "load":
212+
if metric_name == "Clock Monotonic Time, s":
213+
benchmark_result["metric"] = "model_load_time(ms)"
214+
benchmark_result["actualValue"] = metric_value * 1000
215+
216+
elif metric_name == "Memory Peak Physical, kB":
217+
# NB: Showing the value in mB is friendlier IMO
218+
benchmark_result["metric"] = "peak_load_mem_usage(mb)"
219+
benchmark_result["actualValue"] = metric_value / 1024
220+
221+
elif method == "forward":
222+
if metric_name == "Clock Monotonic Time, s":
223+
benchmark_result["metric"] = (
224+
"generate_time(ms)"
225+
if "llama" in test_name
226+
else "avg_inference_latency(ms)"
227+
)
228+
benchmark_result["actualValue"] = metric_value * 1000
229+
230+
elif metric_name == "Memory Peak Physical, kB":
231+
# NB: Showing the value in mB is friendlier IMO
232+
benchmark_result["metric"] = "peak_inference_mem_usage(mb)"
233+
benchmark_result["actualValue"] = metric_value / 1024
234+
235+
elif method == "generate" and metric_name == "Tokens Per Second, t/s":
236+
benchmark_result["metric"] = "token_per_sec"
217237
benchmark_result["actualValue"] = metric_value
218238

219239
return benchmark_result
@@ -235,31 +255,33 @@ def extract_ios_benchmark_results(
235255

236256
with request.urlopen(artifact_s3_url) as data:
237257
current_test_name = ""
258+
current_metric_name = ""
238259
current_record = {}
239260

240261
for line in data.read().decode("utf8").splitlines():
241262
s = IOS_TEST_SPEC_REGEX.search(line)
242263
if not s:
243264
continue
244265

245-
test_class = s.group("test_class")
246266
test_name = s.group("test_name")
247267
metric_name = s.group("metric")
248268
metric_value = float(s.group("value"))
249269

250-
if test_name != current_test_name:
251-
if current_record:
270+
if test_name != current_test_name or metric_name != current_metric_name:
271+
if current_record and current_record.get("metric", ""):
252272
# Save the benchmark result in the same format used by Android
253273
benchmark_results.append(current_record.copy())
254274

255275
current_test_name = test_name
276+
current_metric_name = metric_name
256277
current_record = initialize_ios_metadata(current_test_name)
257278

258279
current_record = extract_ios_metric(
259280
current_record, test_name, metric_name, metric_value
260281
)
261282

262-
benchmark_results.append(current_record.copy())
283+
if current_record and current_record.get("metric", ""):
284+
benchmark_results.append(current_record.copy())
263285

264286
return benchmark_results
265287

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import argparse
8+
import os
9+
import re
10+
11+
from typing import List
12+
13+
# Provided by the PyGithub pip package.
14+
from github import Auth, Github
15+
from github.Repository import Repository
16+
17+
18+
def parse_args():
19+
parser = argparse.ArgumentParser(
20+
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
21+
)
22+
parser.add_argument(
23+
"--repo",
24+
type=str,
25+
help='The github repo to modify: e.g. "pytorch/executorch".',
26+
required=True,
27+
)
28+
parser.add_argument(
29+
"--pr",
30+
type=int,
31+
help="Number of the PR in the stack to check and create corresponding PR",
32+
required=True,
33+
)
34+
return parser.parse_args()
35+
36+
37+
def extract_stack_from_body(pr_body: str) -> List[int]:
38+
"""Extracts a list of PR numbers from a ghexport-generated PR body.
39+
40+
The base of the stack is in index 0.
41+
"""
42+
43+
# Expected format. The `__->__` could appear on any line. Stop parsing
44+
# after the blank line. This would return [1, 2, 3].
45+
"""
46+
Stack from [ghstack](https://github.com/ezyang/ghstack) (oldest at bottom):
47+
* #3
48+
* __->__ #2
49+
* #1
50+
51+
<PR description details>
52+
"""
53+
54+
prs = []
55+
ghstack_begin = (
56+
"Stack from [ghstack](https://github.com/ezyang/ghstack) (oldest at bottom):"
57+
)
58+
ghstack_begin_seen = False
59+
for line in pr_body.splitlines():
60+
if ghstack_begin in line:
61+
ghstack_begin_seen = True
62+
if not ghstack_begin_seen:
63+
continue
64+
match = re.match(r"\*(?:.*?)? #(\d+)", line)
65+
if match:
66+
# It's a bullet followed by an integer.
67+
prs.append(int(match.group(1)))
68+
return list(reversed(prs))
69+
70+
71+
def get_pr_stack_from_number(pr_number: int, repo: Repository) -> List[int]:
72+
pr_stack = extract_stack_from_body(repo.get_pull(pr_number).body)
73+
74+
if not pr_stack:
75+
raise Exception(
76+
f"Could not find PR stack in body of #{pr_number}. "
77+
+ "Please make sure that the PR was created with ghstack."
78+
)
79+
80+
return pr_stack
81+
82+
83+
def create_prs_for_orig_branch(pr_stack: List[int], repo: Repository):
84+
# For the first PR, we want to merge to `main` branch, and we will update
85+
# as we go through the stack
86+
orig_branch_merge_base = "main"
87+
for i in range(len(pr_stack)):
88+
pr = repo.get_pull(pr_stack[i])
89+
if not pr.is_merged():
90+
print("The PR (and stack above) is not merged yet, skipping")
91+
return
92+
# Check for invariant: For the current PR, it must be gh/user/x/base <- gh/user/x/head
93+
assert pr.base.ref.replace("base", "head") == pr.head.ref
94+
# The PR we want to create is then "branch_to_merge" <- gh/user/x/orig
95+
# gh/user/x/orig is the clean diff between gh/user/x/base <- gh/user/x/head
96+
orig_branch_merge_head = pr.base.ref.replace("base", "orig")
97+
bot_metadata = f"""This PR was created by the merge bot to help merge the original PR into the main branch.
98+
ghstack PR number: https://github.com/pytorch/executorch/pull/{pr.number}
99+
^ Please use this as the source of truth for the PR details, comments, and reviews
100+
ghstack PR base: https://github.com/pytorch/executorch/tree/{pr.base.ref}
101+
ghstack PR head: https://github.com/pytorch/executorch/tree/{pr.head.ref}
102+
Merge bot PR base: https://github.com/pytorch/executorch/tree/{orig_branch_merge_base}
103+
Merge bot PR head: https://github.com/pytorch/executorch/tree/{orig_branch_merge_head}"""
104+
105+
existing_orig_pr = repo.get_pulls(
106+
head="pytorch:" + orig_branch_merge_head,
107+
base=orig_branch_merge_base,
108+
state="open",
109+
)
110+
if existing_orig_pr.totalCount > 0:
111+
print(
112+
f"PR for {orig_branch_merge_head} already exists {existing_orig_pr[0]}"
113+
)
114+
# We don't need to create/edit because the head PR is merged and orig is finalized.
115+
else:
116+
repo.create_pull(
117+
base=orig_branch_merge_base,
118+
head=orig_branch_merge_head,
119+
title=pr.title,
120+
body=bot_metadata,
121+
)
122+
# Advance the base for the next PR
123+
orig_branch_merge_base = orig_branch_merge_head
124+
125+
126+
def main():
127+
args = parse_args()
128+
129+
with Github(auth=Auth.Token(os.environ["GITHUB_TOKEN"])) as gh:
130+
repo = gh.get_repo(args.repo)
131+
create_prs_for_orig_branch(get_pr_stack_from_number(args.pr, repo), repo)
132+
133+
134+
if __name__ == "__main__":
135+
main()

.github/workflows/android-perf.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ jobs:
160160
161161
if [[ ${{ matrix.model }} =~ ^stories* ]]; then
162162
# Install requirements for export_llama
163-
PYTHON_EXECUTABLE=python bash examples/models/llama2/install_requirements.sh
163+
PYTHON_EXECUTABLE=python bash examples/models/llama/install_requirements.sh
164164
# Test llama2
165165
if [[ ${{ matrix.delegate }} == "xnnpack" ]]; then
166166
DELEGATE_CONFIG="xnnpack+custom+qe"

0 commit comments

Comments
 (0)