Skip to content

Commit d71daf4

Browse files
author
pytorchbot
committed
2024-10-09 nightly release (a6b213b)
1 parent 8865273 commit d71daf4

File tree

1,059 files changed

+14328
-11074
lines changed

Some content is hidden

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

1,059 files changed

+14328
-11074
lines changed

.ci/docker/ci_commit_pins/pytorch.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
aec9b2ab77389967ef39bb9c10662fd0fe3e185a
1+
d1b87e26e5c4343f5b56bb1e6f89b479b389bfac

.ci/docker/requirements-ci.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mpmath==1.3.0
2-
numpy==1.21.3; python_version == '3.10'
2+
numpy==1.22.0; python_version == '3.10'
33
numpy==1.23.2; python_version == '3.11'
44
numpy; python_version >= '3.12'
55
PyYAML==6.0.1

.ci/scripts/test_llama.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ echo "Creating tokenizer.bin"
213213
$PYTHON_EXECUTABLE -m extension.llm.tokenizer.tokenizer -t tokenizer.model -o tokenizer.bin
214214

215215

216-
RUNTIME_ARGS="--model_path=${EXPORTED_MODEL_NAME} --tokenizer_path=tokenizer.bin --prompt=Once --temperature=0 --seq_len=10"
216+
RUNTIME_ARGS="--model_path=${EXPORTED_MODEL_NAME} --tokenizer_path=tokenizer.bin --prompt=Once --temperature=0 --seq_len=10 --warmup=1"
217217
# Check build tool.
218218
echo "Running ${EXPORTED_MODEL_NAME} in portable mode"
219219
if [[ "${BUILD_TOOL}" == "buck2" ]]; then
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
#!/usr/bin/env python3
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+
import json
9+
import logging
10+
import os
11+
import re
12+
import time
13+
import zipfile
14+
from argparse import Action, ArgumentParser, Namespace
15+
from io import BytesIO
16+
from logging import info, warning
17+
from typing import Any, List, Optional
18+
from urllib import error, request
19+
20+
21+
logging.basicConfig(level=logging.INFO)
22+
23+
24+
BENCHMARK_RESULTS_FILENAME = "benchmark_results.json"
25+
ARTIFACTS_FILENAME_REGEX = re.compile(r"(android|ios)-artifacts-(?P<job_id>\d+).json")
26+
27+
28+
class ValidateArtifacts(Action):
29+
def __call__(
30+
self,
31+
parser: ArgumentParser,
32+
namespace: Namespace,
33+
values: Any,
34+
option_string: Optional[str] = None,
35+
) -> None:
36+
if os.path.isfile(values) and values.endswith(".json"):
37+
setattr(namespace, self.dest, values)
38+
return
39+
40+
parser.error(f"{values} is not a valid JSON file (*.json)")
41+
42+
43+
class ValidateOutputDir(Action):
44+
def __call__(
45+
self,
46+
parser: ArgumentParser,
47+
namespace: Namespace,
48+
values: Any,
49+
option_string: Optional[str] = None,
50+
) -> None:
51+
if os.path.isdir(values):
52+
setattr(namespace, self.dest, values)
53+
return
54+
55+
parser.error(f"{values} is not a valid directory")
56+
57+
58+
def parse_args() -> Any:
59+
from argparse import ArgumentParser
60+
61+
parser = ArgumentParser("extract benchmark results from AWS Device Farm artifacts")
62+
parser.add_argument(
63+
"--artifacts",
64+
type=str,
65+
required=True,
66+
action=ValidateArtifacts,
67+
help="the list of artifacts from AWS in JSON format",
68+
)
69+
parser.add_argument(
70+
"--output-dir",
71+
type=str,
72+
required=True,
73+
action=ValidateOutputDir,
74+
help="the directory to keep the benchmark results",
75+
)
76+
parser.add_argument(
77+
"--repo",
78+
type=str,
79+
required=True,
80+
help="which GitHub repo this workflow run belongs to",
81+
)
82+
parser.add_argument(
83+
"--head-branch",
84+
type=str,
85+
required=True,
86+
help="the head branch that runs",
87+
)
88+
parser.add_argument(
89+
"--workflow-name",
90+
type=str,
91+
required=True,
92+
help="the name of the benchmark workflow",
93+
)
94+
parser.add_argument(
95+
"--workflow-run-id",
96+
type=int,
97+
required=True,
98+
help="the id of the benchmark workflow",
99+
)
100+
parser.add_argument(
101+
"--workflow-run-attempt",
102+
type=int,
103+
required=True,
104+
help="which retry of the workflow this is",
105+
)
106+
107+
return parser.parse_args()
108+
109+
110+
def extract_android_benchmark_results(
111+
job_name: str, artifact_type: str, artifact_s3_url: str
112+
) -> List:
113+
"""
114+
The benchmark results from Android have already been stored in CUSTOMER_ARTIFACT
115+
artifact, so we will just need to get it
116+
117+
Return the list of benchmark results.
118+
"""
119+
if artifact_type != "CUSTOMER_ARTIFACT":
120+
return []
121+
122+
try:
123+
with request.urlopen(artifact_s3_url) as data:
124+
with zipfile.ZipFile(BytesIO(data.read())) as customer_artifact:
125+
for name in customer_artifact.namelist():
126+
if BENCHMARK_RESULTS_FILENAME in name:
127+
return json.loads(customer_artifact.read(name))
128+
129+
except error.HTTPError:
130+
warning(f"Fail to {artifact_type} {artifact_s3_url}")
131+
return []
132+
except json.decoder.JSONDecodeError:
133+
# This is to handle the case where there is no benchmark results
134+
warning(f"Fail to load the benchmark results from {artifact_s3_url}")
135+
return []
136+
137+
138+
def extract_job_id(artifacts_filename: str) -> int:
139+
"""
140+
Extract the job id from the artifacts filename
141+
"""
142+
m = ARTIFACTS_FILENAME_REGEX.match(os.path.basename(artifacts_filename))
143+
if not m:
144+
return 0
145+
return int(m.group("job_id"))
146+
147+
148+
def transform(
149+
app_type: str,
150+
benchmark_results: List,
151+
repo: str,
152+
head_branch: str,
153+
workflow_name: str,
154+
workflow_run_id: int,
155+
workflow_run_attempt: int,
156+
job_name: str,
157+
job_id: int,
158+
) -> List:
159+
"""
160+
Transform the benchmark results into the format writable into the benchmark database
161+
"""
162+
# Overwrite the device name here with the job name as it has more information about
163+
# the device, i.e. Samsung Galaxy S22 5G instead of just Samsung
164+
for r in benchmark_results:
165+
r["deviceInfo"]["device"] = job_name
166+
167+
# TODO (huydhn): This is the current schema of the database oss_ci_benchmark_v2,
168+
# and I'm trying to fit ET benchmark results into it, which is kind of awkward.
169+
# However, the schema is going to be updated soon
170+
return [
171+
{
172+
# GH-info to identify where the benchmark is run
173+
"repo": repo,
174+
"head_branch": head_branch,
175+
"workflow_id": workflow_run_id,
176+
"run_attempt": workflow_run_attempt,
177+
"job_id": job_id,
178+
# The model
179+
"name": f"{r['benchmarkModel']['name']} {r['benchmarkModel'].get('backend', '')}".strip(),
180+
"dtype": (
181+
r["benchmarkModel"]["quantization"]
182+
if r["benchmarkModel"]["quantization"]
183+
else "unknown"
184+
),
185+
# The metric value
186+
"metric": r["metric"],
187+
"actual": r["actualValue"],
188+
"target": r["targetValue"],
189+
# The device
190+
"device": r["deviceInfo"]["device"],
191+
"arch": r["deviceInfo"].get("os", ""),
192+
# Not used here, just set it to something unique here
193+
"filename": workflow_name,
194+
"test_name": app_type,
195+
"runner": job_name,
196+
}
197+
for r in benchmark_results
198+
]
199+
200+
201+
def main() -> None:
202+
args = parse_args()
203+
204+
# Across all devices
205+
all_benchmark_results = []
206+
207+
with open(args.artifacts) as f:
208+
for artifact in json.load(f):
209+
app_type = artifact.get("app_type", "")
210+
# We expect this to be set to either ANDROID_APP or IOS_APP
211+
if not app_type or app_type not in ["ANDROID_APP", "IOS_APP"]:
212+
info(
213+
f"App type {app_type} is not recognized in artifact {json.dumps(artifact)}"
214+
)
215+
continue
216+
217+
job_name = artifact["job_name"]
218+
artifact_type = artifact["type"]
219+
artifact_s3_url = artifact["s3_url"]
220+
221+
if app_type == "ANDROID_APP":
222+
benchmark_results = extract_android_benchmark_results(
223+
job_name, artifact_type, artifact_s3_url
224+
)
225+
if benchmark_results:
226+
benchmark_results = transform(
227+
app_type,
228+
benchmark_results,
229+
args.repo,
230+
args.head_branch,
231+
args.workflow_name,
232+
args.workflow_run_id,
233+
args.workflow_run_attempt,
234+
job_name,
235+
extract_job_id(args.artifacts),
236+
)
237+
all_benchmark_results.extend(benchmark_results)
238+
239+
if app_type == "IOS_APP":
240+
# TODO (huydhn): Implement the logic for iOS next
241+
pass
242+
243+
if all_benchmark_results:
244+
output_file = os.path.basename(args.artifacts)
245+
with open(f"{args.output_dir}/{output_file}", "w") as f:
246+
json.dump(all_benchmark_results, f)
247+
248+
249+
if __name__ == "__main__":
250+
main()

.github/workflows/android-perf.yml

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ jobs:
176176
fi
177177
echo "::endgroup::"
178178
179-
build-llm-demo:
180-
name: build-llm-demo
179+
build-benchmark-app:
180+
name: build-benchmark-app
181181
uses: pytorch/test-infra/.github/workflows/linux_job.yml@main
182182
needs: set-parameters
183183
with:
@@ -211,7 +211,7 @@ jobs:
211211
uses: pytorch/test-infra/.github/workflows/mobile_job.yml@main
212212
needs:
213213
- set-parameters
214-
- build-llm-demo
214+
- build-benchmark-app
215215
- export-models
216216
strategy:
217217
matrix:
@@ -228,13 +228,84 @@ jobs:
228228
# This is the ARN of ExecuTorch project on AWS
229229
project-arn: arn:aws:devicefarm:us-west-2:308535385114:project:02a2cf0f-6d9b-45ee-ba1a-a086587469e6
230230
device-pool-arn: ${{ matrix.device }}
231-
# Uploaded to S3 from the previous job, the name of the app comes from the project itself.
232-
# Unlike models there are limited numbers of build flavor for apps, and the model controls whether it should build with bpe/tiktoken tokenizer.
233-
# It's okay to build all possible apps with all possible flavors in job "build-llm-demo". However, in this job, once a model is given, there is only
234-
# one app+flavor that could load and run the model.
235231
android-app-archive: https://gha-artifacts.s3.amazonaws.com/${{ github.repository }}/${{ github.run_id }}/artifacts/minibench/app-debug.apk
236232
android-test-archive: https://gha-artifacts.s3.amazonaws.com/${{ github.repository }}/${{ github.run_id }}/artifacts/minibench/app-debug-androidTest.apk
237233
# NB: Need to set the default spec here so that it works for periodic too
238234
test-spec: ${{ inputs.test_spec || 'https://ossci-android.s3.amazonaws.com/executorch/android-llm-device-farm-test-spec.yml' }}
239235
# Uploaded to S3 from the previous job
240236
extra-data: https://gha-artifacts.s3.amazonaws.com/${{ github.repository }}/${{ github.run_id }}/artifacts/${{ matrix.model }}_${{ matrix.delegate }}/model.zip
237+
238+
upload-benchmark-results:
239+
needs:
240+
- benchmark-on-device
241+
if: always()
242+
runs-on: linux.2xlarge
243+
environment: upload-benchmark-results
244+
permissions:
245+
id-token: write
246+
contents: read
247+
steps:
248+
- uses: actions/checkout@v3
249+
with:
250+
submodules: false
251+
252+
- name: Authenticate with AWS
253+
uses: aws-actions/configure-aws-credentials@v4
254+
with:
255+
role-to-assume: arn:aws:iam::308535385114:role/gha_workflow_upload-benchmark-results
256+
# The max duration enforced by the server side
257+
role-duration-seconds: 18000
258+
aws-region: us-east-1
259+
260+
- name: Setup conda
261+
uses: pytorch/test-infra/.github/actions/setup-miniconda@main
262+
with:
263+
python-version: '3.10'
264+
265+
- name: Download the list of artifacts from S3
266+
env:
267+
ARTIFACTS_S3_DIR: s3://gha-artifacts/device_farm/${{ github.run_id }}/${{ github.run_attempt }}/artifacts/
268+
shell: bash
269+
run: |
270+
set -eux
271+
${CONDA_RUN} python -mpip install awscli==1.32.18
272+
273+
mkdir -p artifacts
274+
pushd artifacts
275+
${CONDA_RUN} aws s3 sync "${ARTIFACTS_S3_DIR}" .
276+
popd
277+
278+
ls -lah artifacts
279+
280+
- name: Extract the benchmark results JSON
281+
shell: bash
282+
run: |
283+
set -eux
284+
285+
mkdir -p benchmark-results
286+
287+
for ARTIFACTS_BY_JOB in artifacts/*.json; do
288+
[ -f "${ARTIFACTS_BY_JOB}" ] || break
289+
echo "${ARTIFACTS_BY_JOB}"
290+
${CONDA_RUN} python .github/scripts/extract_benchmark_results.py \
291+
--artifacts "${ARTIFACTS_BY_JOB}" \
292+
--output-dir benchmark-results \
293+
--repo ${{ github.repository }} \
294+
--head-branch ${{ github.head_ref || github.ref_name }} \
295+
--workflow-name "${{ github.workflow }}" \
296+
--workflow-run-id ${{ github.run_id }} \
297+
--workflow-run-attempt ${{ github.run_attempt }}
298+
done
299+
300+
ls -lah benchmark-results
301+
302+
for BENCHMARK_RESULTS in benchmark-results/*.json; do
303+
cat "${BENCHMARK_RESULTS}"
304+
echo
305+
done
306+
307+
- name: Upload the benchmark results
308+
uses: pytorch/test-infra/.github/actions/upload-benchmark-results@main
309+
with:
310+
benchmark-results-dir: 'benchmark-results'
311+
dry-run: false

.github/workflows/android.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ on:
1515
- install_requirements.sh
1616
- examples/demo-apps/android/**
1717
- extension/android/**
18+
- extension/benchmark/android/**
1819
- extension/module/**
1920
workflow_dispatch:
2021

0 commit comments

Comments
 (0)