Skip to content

Commit eeced40

Browse files
authored
Add a GHA to upload benchmark results (#5742)
This adds a GHA to upload benchmark results. I'm using the existing `torchci-oss-ci-benchmark` database for now until the new generic database from https://fburl.com/gdoc/ossgtvte is ready. So, it's backed by dynamoDB instead of S3 for now. I also include a quick test with some dummy Android benchmark records. * [x] Get the list of artifacts #5727 * [x] On ExecuTorch side, extract the benchmark JSON from the artifacts pytorch/executorch#5808 * [x] Create a new GitHub action on test-infra to upload the JSON #5742 * [x] Create a new GHA role to upload the results meta-pytorch/pytorch-gha-infra#483
1 parent 8fc9e9b commit eeced40

File tree

5 files changed

+188
-0
lines changed

5 files changed

+188
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Upload benchmark results
2+
3+
inputs:
4+
benchmark-results-dir:
5+
description: 'The path to the directory with all the results in JSON format'
6+
required: True
7+
dry-run:
8+
default: 'true'
9+
10+
runs:
11+
using: composite
12+
steps:
13+
- name: Install dependencies
14+
shell: bash
15+
run: |
16+
set -eux
17+
python3 -mpip install boto3==1.35.33
18+
19+
# TODO (huydhn): Once the generic benchmark database is ready, this will be
20+
# uploaded to S3 instead
21+
- name: Upload benchmark results to DynamoDB
22+
shell: bash
23+
env:
24+
BENCHMARK_RESULTS_DIR: ${{ inputs.benchmark-results-dir }}
25+
DRY_RUN: ${{ inputs.dry-run }}
26+
run: |
27+
set -eux
28+
29+
if [[ "${DRY_RUN}" == "true" ]]; then
30+
python3 "${GITHUB_ACTION_PATH}/../../scripts/upload_benchmark_results.py" \
31+
--benchmark-results-dir "${BENCHMARK_RESULTS_DIR}" \
32+
--dry-run
33+
else
34+
python3 "${GITHUB_ACTION_PATH}/../../scripts/upload_benchmark_results.py" \
35+
--benchmark-results-dir "${BENCHMARK_RESULTS_DIR}"
36+
fi
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 286.98526, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "generate_time(ms)", "actual": 405.055521, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "token_per_sec", "actual": 306.53265, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 308.598385, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "generate_time(ms)", "actual": 522.4928639999999, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "token_per_sec", "actual": 243.51297, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 280.957917, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "generate_time(ms)", "actual": 1366.6017709999999, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "token_per_sec", "actual": 90.70632, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 289.104427, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "generate_time(ms)", "actual": 733.888385, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223108, "name": "llama2", "dtype": "q8", "metric": "token_per_sec", "actual": 174.28572, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "avg_inference_latency(ms)", "actual": 23.0566825, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 18.705938, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "avg_inference_latency(ms)", "actual": 42.8922188, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 14.722292, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22 5G", "arch": "Android 13", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "avg_inference_latency(ms)", "actual": 10.9387811, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 13.23677, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22 Ultra 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22 Ultra 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "avg_inference_latency(ms)", "actual": 63.4430574, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "model_load_time(ms)", "actual": 16.301875, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}, {"repo": "pytorch/executorch", "head_branch": "main", "workflow_id": 12345, "run_attempt": 1, "job_id": 31017223431, "name": "mv2 xnnpack", "dtype": "q8", "metric": "load_status", "actual": 0.0, "target": 0.0, "device": "Samsung Galaxy S22+ 5G", "arch": "Android 12", "filename": "android-perf", "test_name": "ANDROID_APP", "runner": "Samsung Galaxy S22+ 5G"}]
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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 hashlib
9+
import json
10+
import logging
11+
import os
12+
import time
13+
from argparse import Action, ArgumentParser, Namespace
14+
from decimal import Decimal
15+
16+
from logging import info
17+
from typing import Any, Callable, Dict, List, Optional
18+
19+
import boto3
20+
21+
logging.basicConfig(level=logging.INFO)
22+
23+
24+
class ValidateDir(Action):
25+
def __call__(
26+
self,
27+
parser: ArgumentParser,
28+
namespace: Namespace,
29+
values: Any,
30+
option_string: Optional[str] = None,
31+
) -> None:
32+
if os.path.isdir(values):
33+
setattr(namespace, self.dest, values)
34+
return
35+
36+
parser.error(f"{values} is not a valid directory")
37+
38+
39+
def parse_args() -> Any:
40+
from argparse import ArgumentParser
41+
42+
parser = ArgumentParser("upload the benchmark results to OSS benchmark database")
43+
parser.add_argument(
44+
"--benchmark-results-dir",
45+
type=str,
46+
required=True,
47+
action=ValidateDir,
48+
help="the directory with all the benchmark results in JSON format",
49+
)
50+
parser.add_argument(
51+
"--dry-run",
52+
action="store_true",
53+
)
54+
parser.add_argument(
55+
"--dynamodb-table",
56+
type=str,
57+
default="torchci-oss-ci-benchmark",
58+
help="the name of the DynamoDB table to upload to",
59+
)
60+
61+
return parser.parse_args()
62+
63+
64+
# DynamoDB use Decimal, not float
65+
class DecimalEncoder(json.JSONEncoder):
66+
def default(self, o: Any) -> Any:
67+
if isinstance(o, Decimal):
68+
return str(o)
69+
return super().default(o)
70+
71+
72+
# TODO (huydhn): This can be replaced by S3 path once we move to S3
73+
def generate_partition_key(doc: Dict[str, Any]) -> str:
74+
"""
75+
Generate an unique partition key for the document on DynamoDB
76+
"""
77+
repo = doc["repo"]
78+
workflow_id = doc["workflow_id"]
79+
job_id = doc["job_id"]
80+
test_name = doc["test_name"]
81+
filename = doc["filename"]
82+
83+
hash_content = hashlib.md5(
84+
json.dumps(doc, cls=DecimalEncoder).encode("utf-8")
85+
).hexdigest()
86+
return f"{repo}/{workflow_id}/{job_id}/{test_name}/{filename}/{hash_content}"
87+
88+
89+
def upload_to_dynamodb(
90+
dynamodb_table: str,
91+
docs: List[Any],
92+
generate_partition_key: Optional[Callable[[Dict[str, Any]], str]],
93+
dry_run: bool = True,
94+
) -> None:
95+
"""
96+
Copied from upload stats script
97+
"""
98+
info(f"Writing {len(docs)} documents to DynamoDB {dynamodb_table}")
99+
if not dry_run:
100+
# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html#batch-writing
101+
with boto3.resource("dynamodb").Table(dynamodb_table).batch_writer() as batch:
102+
for doc in docs:
103+
doc["timestamp"] = int(round(time.time() * 1000))
104+
if generate_partition_key:
105+
doc["dynamoKey"] = generate_partition_key(doc)
106+
batch.put_item(Item=doc)
107+
108+
109+
def main() -> None:
110+
args = parse_args()
111+
112+
for file in os.listdir(args.benchmark_results_dir):
113+
if not file.endswith(".json"):
114+
continue
115+
116+
filepath = os.path.join(args.benchmark_results_dir, file)
117+
info(f"Loading {filepath}")
118+
119+
with open(filepath) as f:
120+
upload_to_dynamodb(
121+
dynamodb_table=args.dynamodb_table,
122+
# NB: DynamoDB only accepts decimal number, not float
123+
docs=json.load(f, parse_float=Decimal),
124+
generate_partition_key=generate_partition_key,
125+
dry_run=args.dry_run,
126+
)
127+
128+
129+
if __name__ == "__main__":
130+
main()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Test upload-benchmark-results
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- .github/scripts/upload_benchmark_results.py
7+
- .github/workflows/test_upload_benchmark_results.ym
8+
- .github/actions/upload-benchmark-results/*
9+
10+
jobs:
11+
test:
12+
runs-on: linux.2xlarge
13+
steps:
14+
- uses: actions/checkout@v3
15+
16+
- name: Test upload the benchmark results
17+
uses: ./.github/actions/upload-benchmark-results
18+
with:
19+
benchmark-results-dir: .github/scripts/benchmark-results-dir-for-testing
20+
dry-run: true

0 commit comments

Comments
 (0)