#!/usr/bin/env python3 import argparse import dateutil import foundation import json import os import sys from foundation import (config, logger, util,) from dateutil import parser ######################################## # Base support class. ######################################## class ResultVisitor: """ Super class for all possible visitors of benchmark results. """ def __init__(self): pass def handleResults(self, results): """ Handle results (which is a decoded python dictionary) """ pass def storeResults(self, dataset_dir): """ Store results to a JSON file. """ pass ######################################## # Parser helpers. ######################################## def configureArgumentParser(): parser = argparse.ArgumentParser( description="Cycles benchmark parser script.") parser.add_argument("-b", "--benchmark-dir", help="Directory with benchmark results", default="") parser.add_argument("-d", "--dataset-dir", help="Directory where datasets will be stored", default="") return parser ######################################## # Configuration helpers. ######################################## def injectDefaultConfiguration(config): """ For a specified configuration object, set all possible properties to their default value. """ root_dir = util.getBundleRootDirectory() section = {"benchmark_dir": "", "dataset_dir": ""} config['parser'] = section def injectArgparseConfiguration(config, args): """ Override settings wit harguments passed from the command line. """ section = config['parser'] if args.benchmark_dir: section['benchmark_dir'] = args.benchmark_dir if args.dataset_dir: section['dataset_dir'] = args.dataset_dir def readConfiguration(args): """ Read configuration file and return BenchmarkConfig with all the settings we will need to use. """ config = foundation.config.BenchmarkConfig() injectDefaultConfiguration(config) config.readGlobalConfig("parser") injectArgparseConfiguration(config, args) return config def checkConfiguration(config): """ Check whether configuration is complete and usable. """ logger.INFO("Validating configuration...") section = config['parser'] # Check whether directories are correct. if not os.path.exists(section["benchmark_dir"]): logger.INFO(" Benchmark directory does not exist.") return False if not os.path.exists(section["dataset_dir"]): logger.INFO(" Dataset directory does not exist.") return False return True ######################################## # Results iteration implementation. ######################################## def visitBencharkResult(directory, visitors): """ Take all actions needed when new benchmark result is found. """ results_filename = os.path.join(directory, "results.json") if not os.path.exists(results_filename): return with open(results_filename) as results_file: results = json.load(results_file) # Check results are usable. if 'stats' not in results: return for visitor in visitors: visitor.handleResults(results) def iterateBenchmarks(directory, visitors): """ Iterate over all benchmar results for a specific configuration. """ for filename in sorted(os.listdir(directory)): full_filename = os.path.join(directory, filename) if os.path.isdir(full_filename): visitBencharkResult(full_filename, visitors) def iterateBenchmarksRoot(directory, visitors): """ Iterate over all benchmark results, startting from top level where all benchmarks machines are storing their results. """ for filename in sorted(os.listdir(directory)): full_filename = os.path.join(directory, filename) if os.path.isdir(full_filename): iterateBenchmarks(full_filename, visitors) ######################################## # Main logic. ######################################## class LatestResultVisitor(ResultVisitor): def __init__(self): ResultVisitor.__init__(self) self.devices_ = {} def copyUsableStats(self, device, results): stats = results['stats'] timestamp = dateutil.parser.parse(results['timestamp']) for scene_name in stats: stat = stats[scene_name] # Ignore benchmark results which crashed or aborted. if "result" not in stat or stat['result'] != 'OK': continue # Ignore benchmark results which ar eolder than existing ones. if scene_name in device and \ "timestamp" in device[scene_name] and \ timestamp < device[scene_name]["timestamp"]: continue device_scene_stat = dict(stat) device_scene_stat.pop("result") device_scene_stat['timestamp'] = timestamp device[scene_name] = device_scene_stat def handleResults(self, results): if 'device_info' not in results or \ 'stats' not in results: return device_info = results['device_info'] device_name = util.deviceInfoAsString(device_info) # If there were no stats for the device, if device_name not in self.devices_: self.devices_[device_name] = {} self.copyUsableStats(self.devices_[device_name], results) def storeResults(self, dataset_dir): # Firts of all, gather all possible scenes. all_scenes = set() for device_name, stats in self.devices_.items(): for scene_name in stats.keys(): all_scenes.add(scene_name) all_scenes = sorted(list(all_scenes)) # Gather datasets in format of charts. datasets = [] device_index = 0 for device_name in sorted(self.devices_.keys()): stats = self.devices_[device_name] data = [] for scene_name in all_scenes: if scene_name not in stats: # TODO(sergey): How to indicate missing dataset? data.append(0) continue scene_stats = stats[scene_name] data.append(scene_stats['pipeline_render_time']) dataset = { "label": device_name, "data": data, } datasets.append(dataset) device_index += 1 # Prepare python dict before converting it to JSON. data = { "labels": all_scenes, "datasets": datasets, } code = "var data = " + json.dumps(data, sort_keys=True, indent=2) + ";" # Save dataset to disk. filename = os.path.join(dataset_dir, "latest_snapshot.js") with open(filename, "w") as f: f.write(code) def main(): parser = configureArgumentParser() args = parser.parse_args() logger.init() # Read configuration file, so we know what we will be doing. config = readConfiguration(args) if not checkConfiguration(config): logger.ERROR("Configuration is not complete or valid, aborting.") return False # Construct list of all visitors whic hwe want. latest_results_visitor = LatestResultVisitor() # Do actual parse. logger.INFO("Iterating over all benchmark results...") visitors = [latest_results_visitor] iterateBenchmarksRoot(config['parser']['benchmark_dir'], visitors) # Store results. logger.INFO("Storing results...") dataset_dir = config['parser']['dataset_dir'] for visitor in visitors: visitor.storeResults(dataset_dir) return True if __name__ == "__main__": if not main(): sys.exit(1)