benchmarking/regression_detectors/regression_detectors.py (155 lines of code) (raw):
#!/usr/bin/env python
##############################################################################
# Copyright 2017-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
##############################################################################
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import json
import os
from regression_detectors.delay_detector.delay_detector import DelayRegressionDetector
from utils.custom_logger import getLogger
from utils.utilities import getDirectory
from utils.utilities import getFilename
detectors = {
"delay": DelayRegressionDetector,
}
def getRegressionDetectors():
return detectors
def checkRegressions(info, platform, framework, benchmark, reporters, meta, outdir):
if meta["metric"] not in detectors:
return
commit = info["treatment"]["commit"]
getLogger().info("Checking regression for " + commit)
regressions, infos = _detectRegression(info, meta, outdir)
if len(regressions):
from driver.benchmark_driver import runOneBenchmark
getLogger().info(
"Regression detected on {}, ".format(platform.getMangledName())
+ "verifying: {}".format(",".join(regressions))
)
for i in infos:
i["run_type"] = "verify"
runOneBenchmark(
i, benchmark, framework, platform, meta["backend"], reporters
)
verify_regressions, _ = _detectRegression(info, meta, outdir)
if len(verify_regressions) > 0:
# regression verified
regressed_info = infos[-2]
regressed_info["run_type"] = "regress"
regressed_info["regressed_types"] = verify_regressions
runOneBenchmark(
regressed_info,
benchmark,
framework,
platform,
meta["backend"],
reporters,
)
getLogger().info(
"Regression confirmed for commit: {}".format(
regressed_info["treatment"]["commit"]
)
)
getLogger().info("Regressed types: {}".format(",".join(verify_regressions)))
getLogger().info(
"Regression verifying completed for "
+ "{} on {}".format(platform.getMangledName(), commit)
)
else:
getLogger().info(
"No Regression found for "
+ "{} on {}".format(platform.getMangledName(), commit)
)
# Regress is identified if last two runs are both above threshhold.
def _detectOneBenchmarkRegression(data):
regressed = []
if "meta.txt" not in data:
getLogger().error("Meta is not found")
return regressed
meta = data.pop("meta.txt")
if len(meta) < 2:
return regressed, None
control_commits = {
x["control_commit"] if "control_commit" in x else x["commit"] for x in meta
}
control_change = len(control_commits) > 1
metric = meta[0]["metric"]
detector = detectors[metric]()
for filename, one_data in data.items():
if filename == "meta.txt":
continue
if len(one_data) < 2:
continue
if (
detector.isRegressed(filename, one_data[0], one_data[2:], control_change)
) and detector.isRegressed(filename, one_data[1], one_data[2:], control_change):
regressed.append(one_data[0]["type"])
if len(regressed) > 0:
infos = []
for x in meta:
idx = x["command"].index("--info")
infos.append(json.loads(x["command"][idx + 1]))
infos.reverse()
return regressed, infos
return regressed, None
def _detectRegression(info, meta, outdir):
dirs = _getBenchmarkRuns(info, meta, outdir)
data = _collectBenchmarkRunData(dirs)
return _detectOneBenchmarkRegression(data)
def _listdirs(path):
return [x for x in os.listdir(path) if os.path.isdir(path + x)]
def _listfiles(path):
return [x for x in os.listdir(path) if os.path.isfile(path + x)]
def _getLatestRun(dir):
runs = _listdirs(dir)
last_run = 0
while str(last_run) in runs:
last_run += 1
last_run -= 1
if last_run >= 0:
return os.path.join(dir, str(last_run))
else:
getLogger().critical(f"Latest run in directory {dir} doesn't exist.")
return None
def _getBenchmarkRuns(info, meta, outdir):
dir_name = os.path.join(
outdir,
getFilename(meta["platform"]),
getFilename(meta["framework"]),
getFilename(meta["net_name"]),
getFilename(meta["metric"]),
getFilename(meta["identifier"]),
)
assert "regression_commits" in info, "regression_commits field is missing from info"
dirs = []
for entry in info["regression_commits"]:
one_dir = os.path.jon(
dir_name, getDirectory(entry["commit"], entry["commit_time"])
)
if not os.path.isdir(one_dir):
continue
last_run = _getLatestRun(one_dir)
if last_run is None:
continue
dirs.append(last_run)
return dirs
def _collectBenchmarkRunData(runs):
data = {}
if len(runs) == 0:
# last run failed
return data
latest_run = runs.pop(0)
files = _listfiles(latest_run)
for filename in files:
latest_file = latest_run + filename
if not os.path.isfile(latest_file):
continue
with open(latest_file, "r") as f:
content = json.load(f)
d = [content]
for run in runs:
compare_file = run + filename
if not os.path.isfile(compare_file):
continue
with open(compare_file, "r") as cf:
content = json.load(cf)
d.append(content)
data[filename] = d
runs.insert(0, latest_run)
return data