|  | """Scribe Uploader for Pytorch Benchmark Data | 
|  |  | 
|  | Currently supports data in pytest-benchmark format but can be extended. | 
|  |  | 
|  | New fields can be added just by modifying the schema in this file, schema | 
|  | checking is only here to encourage reusing existing fields and avoiding typos. | 
|  | """ | 
|  |  | 
|  | import argparse | 
|  | import time | 
|  | import json | 
|  | import os | 
|  | import requests | 
|  | import subprocess | 
|  | from collections import defaultdict | 
|  |  | 
|  |  | 
|  | class ScribeUploader: | 
|  | def __init__(self, category): | 
|  | self.category = category | 
|  |  | 
|  | def format_message(self, field_dict): | 
|  | assert 'time' in field_dict, "Missing required Scribe field 'time'" | 
|  | message = defaultdict(dict) | 
|  | for field, value in field_dict.items(): | 
|  | if field in self.schema['normal']: | 
|  | message['normal'][field] = str(value) | 
|  | elif field in self.schema['int']: | 
|  | message['int'][field] = int(value) | 
|  | elif field in self.schema['float']: | 
|  | message['float'][field] = float(value) | 
|  | else: | 
|  |  | 
|  | raise ValueError("Field {} is not currently used, " | 
|  | "be intentional about adding new fields".format(field)) | 
|  | return message | 
|  |  | 
|  | def _upload_intern(self, messages): | 
|  | for m in messages: | 
|  | json_str = json.dumps(m) | 
|  | cmd = ['scribe_cat', self.category, json_str] | 
|  | subprocess.run(cmd) | 
|  |  | 
|  | def upload(self, messages): | 
|  | if os.environ.get('SCRIBE_INTERN'): | 
|  | return self._upload_intern(messages) | 
|  | access_token = os.environ.get("SCRIBE_GRAPHQL_ACCESS_TOKEN") | 
|  | if not access_token: | 
|  | raise ValueError("Can't find access token from environment variable") | 
|  | url = "https://graph.facebook.com/scribe_logs" | 
|  | r = requests.post( | 
|  | url, | 
|  | data={ | 
|  | "access_token": access_token, | 
|  | "logs": json.dumps( | 
|  | [ | 
|  | { | 
|  | "category": self.category, | 
|  | "message": json.dumps(message), | 
|  | "line_escape": False, | 
|  | } | 
|  | for message in messages | 
|  | ] | 
|  | ), | 
|  | }, | 
|  | ) | 
|  | print(r.text) | 
|  | r.raise_for_status() | 
|  |  | 
|  | class PytorchBenchmarkUploader(ScribeUploader): | 
|  | def __init__(self): | 
|  | super().__init__('perfpipe_pytorch_benchmarks') | 
|  | self.schema = { | 
|  | 'int': [ | 
|  | 'time', 'rounds', | 
|  | ], | 
|  | 'normal': [ | 
|  | 'benchmark_group', 'benchmark_name', 'benchmark_executor', | 
|  | 'benchmark_fuser', 'benchmark_class', 'benchmark_time', | 
|  | 'pytorch_commit_id', 'pytorch_branch', 'pytorch_commit_time', 'pytorch_version', | 
|  | 'pytorch_git_dirty', | 
|  | 'machine_kernel', 'machine_processor', 'machine_hostname', | 
|  | 'circle_build_num', 'circle_project_reponame', | 
|  | ], | 
|  | 'float': [ | 
|  | 'stddev', 'min', 'median', 'max', 'mean', | 
|  | ] | 
|  | } | 
|  |  | 
|  | def post_pytest_benchmarks(self, pytest_json): | 
|  | machine_info = pytest_json['machine_info'] | 
|  | commit_info = pytest_json['commit_info'] | 
|  | upload_time = int(time.time()) | 
|  | messages = [] | 
|  | for b in pytest_json['benchmarks']: | 
|  | test = b['name'].split('[')[0] | 
|  | net_name = b['params']['net_name'] | 
|  | benchmark_name = '{}[{}]'.format(test, net_name) | 
|  | executor = b['params']['executor'] | 
|  | fuser = b['params']['fuser'] | 
|  | m = self.format_message({ | 
|  | "time": upload_time, | 
|  | "benchmark_group": b['group'], | 
|  | "benchmark_name": benchmark_name, | 
|  | "benchmark_executor": executor, | 
|  | "benchmark_fuser": fuser, | 
|  | "benchmark_class": b['fullname'], | 
|  | "benchmark_time": pytest_json['datetime'], | 
|  | "pytorch_commit_id": commit_info['id'], | 
|  | "pytorch_branch": commit_info['branch'], | 
|  | "pytorch_commit_time": commit_info['time'], | 
|  | "pytorch_version": None, | 
|  | "pytorch_git_dirty": commit_info['dirty'], | 
|  | "machine_kernel": machine_info['release'], | 
|  | "machine_processor": machine_info['processor'], | 
|  | "machine_hostname": machine_info['node'], | 
|  | "circle_build_num": os.environ.get("CIRCLE_BUILD_NUM"), | 
|  | "circle_project_reponame": os.environ.get("CIRCLE_PROJECT_REPONAME"), | 
|  | "stddev": b['stats']['stddev'], | 
|  | "rounds": b['stats']['rounds'], | 
|  | "min": b['stats']['min'], | 
|  | "median": b['stats']['median'], | 
|  | "max": b['stats']['max'], | 
|  | "mean": b['stats']['mean'], | 
|  | }) | 
|  | messages.append(m) | 
|  | self.upload(messages) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | parser = argparse.ArgumentParser(description=__doc__) | 
|  | parser.add_argument("--pytest_bench_json", type=argparse.FileType('r'), | 
|  | help='Upload json data formatted by pytest-benchmark module') | 
|  | args = parser.parse_args() | 
|  | if args.pytest_bench_json: | 
|  | benchmark_uploader = PytorchBenchmarkUploader() | 
|  | json_data = json.load(args.pytest_bench_json) | 
|  | benchmark_uploader.post_pytest_benchmarks(json_data) |