[Git][ghc/ghc][master] TestRunner: Added --chart to display a chart of performance tests
Marge Bot
gitlab at gitlab.haskell.org
Tue Jun 4 05:09:11 UTC 2019
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
286827be by David Eichmann at 2019-06-04T05:09:05Z
TestRunner: Added --chart to display a chart of performance tests
This uses the Chart.js javascript library.
Everything is put into a standalone .html file and opened with the
default browser.
I also simplified the text output to use the same data as the chart.
You can now use a commit range with git's ".." syntax.
The --ci option will use results from CI (you'll need to fetch them
first):
$ git fetch https://gitlab.haskell.org/ghc/ghc-performance-notes.git refs/notes/perf:refs/notes/ci/perf
$ python3 testsuite/driver/perf_notes.py --ci --chart --test-env x86_64-darwin --test-name T9630 master~500..master
- - - - -
5 changed files:
- + testsuite/driver/js/Chart-2.8.0.min.js
- + testsuite/driver/js/tooltip.js
- testsuite/driver/perf_notes.py
- testsuite/driver/testlib.py
- testsuite/driver/testutil.py
Changes:
=====================================
testsuite/driver/js/Chart-2.8.0.min.js
=====================================
The diff for this file was not included because it is too large.
=====================================
testsuite/driver/js/tooltip.js
=====================================
@@ -0,0 +1,108 @@
+/*
+ * This is mostly copied from the example in https://www.chartjs.org/docs/latest/configuration/tooltip.html#external-custom-tooltips.
+ */
+
+setCustomTooltip = function(chartInput, extraTextByLabel) {
+ chartInput.options = chartInput.options || {};
+ chartInput.options.tooltips = chartInput.options.tooltips || {};
+ chartInput.options.tooltips.enabled = false;
+ chartInput.options.tooltips.custom = function (tooltipModel) {
+ // `this` will be the overall tooltip
+ var canvas = this._chart.canvas;
+ return customTooltip(canvas, tooltipModel, extraTextByLabel);
+ }
+
+ return chartInput;
+}
+customTooltip = function(canvas, tooltipModel, extraTextByLabel) {
+ // Tooltip Element
+ var tooltipEl = document.getElementById('chartjs-tooltip');
+
+ // Create element on first render
+ if (!tooltipEl) {
+ tooltipEl = document.createElement('div');
+ tooltipEl.id = 'chartjs-tooltip';
+ tooltipEl.innerHTML = '<table style="background: #cccd"></table>';
+ document.body.appendChild(tooltipEl);
+ }
+
+ // Hide if no tooltip
+ if (tooltipModel.opacity === 0) {
+ tooltipEl.style.opacity = 0;
+ return;
+ }
+
+ // Set caret Position
+ tooltipEl.classList.remove('above', 'below', 'no-transform');
+ if (tooltipModel.yAlign) {
+ tooltipEl.classList.add(tooltipModel.yAlign);
+ } else {
+ tooltipEl.classList.add('no-transform');
+ }
+
+ function getBody(bodyItem) {
+ return bodyItem.lines;
+ }
+
+ // Set Text
+ if (tooltipModel.body) {
+ var titleLines = tooltipModel.title || [];
+ var bodyLines = tooltipModel.body.map(getBody);
+
+ var innerHtml = '<thead>';
+
+ titleLines.forEach(function(title) {
+ innerHtml += '<tr><th>' + title + '</th></tr>';
+ });
+ innerHtml += '</thead><tbody>';
+
+ bodyLines.forEach(function(body, i) {
+ var colors = tooltipModel.labelColors[i];
+ var style = 'background:' + colors.backgroundColor;
+ style += '; border-color:' + colors.borderColor;
+ style += '; border-width: 2px';
+ var span = '<span style="' + style + '"></span>';
+ innerHtml += '<tr><td>' + span + body + '</td></tr>';
+ });
+
+ // Set extra text.
+ if (tooltipModel.dataPoints[0])
+ {
+ var tooltipItem = tooltipModel.dataPoints[0];
+ var extra = extraTextByLabel[tooltipItem.label];
+ innerHtml += '<tr><td><hr />' + escapeHtml(extra) + '</td></tr>';
+ }
+
+ innerHtml += '</tbody>';
+
+ var tableRoot = tooltipEl.querySelector('table');
+ tableRoot.innerHTML = innerHtml;
+ }
+
+ var position = canvas.getBoundingClientRect();
+
+ // Display, position, and set styles for font
+ tooltipEl.style.opacity = 1;
+ tooltipEl.style.position = 'absolute';
+ tooltipEl.style.left = '10px'
+ tooltipEl.style.top = '10px'
+ tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily;
+ tooltipEl.style.fontSize = tooltipModel.bodyFontSize + 'px';
+ tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle;
+ tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
+ tooltipEl.style.pointerEvents = 'none';
+}
+
+function escapeHtml(unsafe) {
+ if(unsafe) {
+ return unsafe
+ .replace(/&/g, "&")
+ .replace(/</g, "<")
+ .replace(/>/g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'")
+ .replace(/\n/g, "</br>");
+ } else {
+ return '';
+ }
+ }
=====================================
testsuite/driver/perf_notes.py
=====================================
@@ -9,6 +9,9 @@
# (which defaults to 'local' if not given by --test-env).
#
+import colorsys
+import tempfile
+import json
import argparse
import re
import subprocess
@@ -18,7 +21,7 @@ import sys
from collections import namedtuple
from math import ceil, trunc
-from testutil import passed, failBecause
+from testutil import passed, failBecause, testing_metrics
# Check if "git rev-parse" can be run successfully.
@@ -115,12 +118,21 @@ def get_allowed_perf_changes(commit='HEAD'):
global _get_allowed_perf_changes_cache
commit = commit_hash(commit)
if not commit in _get_allowed_perf_changes_cache:
- commitByteStr = subprocess.check_output(\
- ['git', '--no-pager', 'log', '-n1', '--format=%B', commit])
_get_allowed_perf_changes_cache[commit] \
- = parse_allowed_perf_changes(commitByteStr.decode())
+ = parse_allowed_perf_changes(get_commit_message(commit))
return _get_allowed_perf_changes_cache[commit]
+# Get the commit message of any commit <ref>.
+# This is cached (keyed on the full commit hash).
+_get_commit_message = {}
+def get_commit_message(commit='HEAD'):
+ global _get_commit_message
+ commit = commit_hash(commit)
+ if not commit in _get_commit_message:
+ _get_commit_message[commit] = subprocess.check_output(\
+ ['git', '--no-pager', 'log', '-n1', '--format=%B', commit]).decode()
+ return _get_commit_message[commit]
+
def parse_allowed_perf_changes(commitMsg):
# Helper regex. Non-capturing unless postfixed with Cap.
s = r"(?:\s*\n?\s+)" # Space, possible new line with an indent.
@@ -297,21 +309,27 @@ def baseline_commit_log(commit):
global _baseline_depth_commit_log
commit = commit_hash(commit)
if not commit in _baseline_depth_commit_log:
- n = BaselineSearchDepth
- output = subprocess.check_output(['git', 'log', '--format=%H', '-n' + str(n), commit]).decode()
- hashes = list(filter(is_commit_hash, output.split('\n')))
-
- # We only got 10 results (expecting 75) in a CI pipeline (issue #16662).
- # It's unclear from the logs what went wrong. Since no exception was
- # thrown, we can assume the `git log` call above succeeded. The best we
- # can do for now is improve logging.
- actualN = len(hashes)
- if actualN != n:
- print("Expected " + str(n) + " hashes, but git gave " + str(actualN) + ":\n" + output)
- _baseline_depth_commit_log[commit] = hashes
+ _baseline_depth_commit_log[commit] = commit_log(commit, BaselineSearchDepth)
return _baseline_depth_commit_log[commit]
+# Get the commit hashes for the last n commits from and
+# including the input commit. The output commits are all commit hashes.
+# str -> [str]
+def commit_log(commitOrRange, n=None):
+ nArgs = ['-n' + str(n)] if n != None else []
+ output = subprocess.check_output(['git', 'log', '--format=%H'] + nArgs + [commitOrRange]).decode()
+ hashes = list(filter(is_commit_hash, output.split('\n')))
+
+ # We only got 10 results (expecting 75) in a CI pipeline (issue #16662).
+ # It's unclear from the logs what went wrong. Since no exception was
+ # thrown, we can assume the `git log` call above succeeded. The best we
+ # can do for now is improve logging.
+ actualN = len(hashes)
+ if n != None and actualN != n:
+ print("Expected " + str(n) + " hashes, but git gave " + str(actualN) + ":\n" + output)
+ return hashes
+
# Cache of baseline values. This is a dict of dicts indexed on:
# (useCiNamespace, commit) -> (test_env, test, metric, way) -> baseline
# (bool , str ) -> (str , str , str , str) -> float
@@ -355,7 +373,6 @@ def baseline_metric(commit, name, test_env, metric, way):
# gets the metric of a given commit
# (Bool, Int) -> (float | None)
def commit_metric(useCiNamespace, depth):
- global _commit_metric_cache
currentCommit = depth_to_commit(depth)
# Get test environment.
@@ -364,44 +381,7 @@ def baseline_metric(commit, name, test_env, metric, way):
# This can happen when no best fit ci test is found.
return None
- # Check for cached value.
- cacheKeyA = (useCiNamespace, currentCommit)
- cacheKeyB = (effective_test_env, name, metric, way)
- if cacheKeyA in _commit_metric_cache:
- return _commit_metric_cache[cacheKeyA].get(cacheKeyB)
-
- # Cache miss.
- # Calculate baselines from the current commit's git note.
- # Note that the git note may contain data for other tests. All tests'
- # baselines will be collected and cached for future use.
- allCommitMetrics = get_perf_stats(
- currentCommit,
- namespace(useCiNamespace))
-
- # Collect recorded values by cacheKeyB.
- values_by_cache_key_b = {}
- for perfStat in allCommitMetrics:
- currentCacheKey = (perfStat.test_env, perfStat.test, \
- perfStat.metric, perfStat.way)
- currentValues = values_by_cache_key_b.setdefault(currentCacheKey, [])
- currentValues.append(float(perfStat.value))
-
- # Calculate and baseline (average of values) by cacheKeyB.
- baseline_by_cache_key_b = {}
- for currentCacheKey, currentValues in values_by_cache_key_b.items():
- baseline_by_cache_key_b[currentCacheKey] = Baseline( \
- PerfStat( \
- currentCacheKey[0],
- currentCacheKey[1],
- currentCacheKey[3],
- currentCacheKey[2],
- sum(currentValues) / len(currentValues)),
- currentCommit,
- depth)
-
- # Save baselines to the cache.
- _commit_metric_cache[cacheKeyA] = baseline_by_cache_key_b
- return baseline_by_cache_key_b.get(cacheKeyB)
+ return get_commit_metric(namespace(useCiNamespace), currentCommit, effective_test_env, name, metric, way)
# Searches through previous commits trying local then ci for each commit in.
def search(useCiNamespace, depth):
@@ -414,7 +394,7 @@ def baseline_metric(commit, name, test_env, metric, way):
# Check for a metric on this commit.
current_metric = commit_metric(useCiNamespace, depth)
if current_metric != None:
- return current_metric
+ return Baseline(current_metric, depth_to_commit(depth), depth)
# Metric is not available.
# If tried local, now try CI.
@@ -432,6 +412,60 @@ def baseline_metric(commit, name, test_env, metric, way):
# Start search from parent commit using local name space.
return search(False, 1)
+# Same as get_commit_metric(), but converts the result to a string or keeps it
+# as None.
+def get_commit_metric_value_str_or_none(gitNoteRef, commit, test_env, name, metric, way):
+ metric = get_commit_metric(gitNoteRef, commit, test_env, name, metric, way)
+ if metric == None:
+ return None
+ return str(metric.value)
+
+# gets the average commit metric from git notes.
+# gitNoteRef: git notes ref sapce e.g. "perf" or "ci/perf"
+# commit: git commit
+# test_env: test environment
+# name: test name
+# metric: test metric
+# way: test way
+# returns: PerfStat | None if stats don't exist for the given input
+def get_commit_metric(gitNoteRef, commit, test_env, name, metric, way):
+ global _commit_metric_cache
+ assert test_env != None
+ commit = commit_hash(commit)
+
+ # Check for cached value.
+ cacheKeyA = (gitNoteRef, commit)
+ cacheKeyB = (test_env, name, metric, way)
+ if cacheKeyA in _commit_metric_cache:
+ return _commit_metric_cache[cacheKeyA].get(cacheKeyB)
+
+ # Cache miss.
+ # Calculate baselines from the current commit's git note.
+ # Note that the git note may contain data for other tests. All tests'
+ # baselines will be collected and cached for future use.
+ allCommitMetrics = get_perf_stats(commit, gitNoteRef)
+
+ # Collect recorded values by cacheKeyB.
+ values_by_cache_key_b = {}
+ for perfStat in allCommitMetrics:
+ currentCacheKey = (perfStat.test_env, perfStat.test, \
+ perfStat.metric, perfStat.way)
+ currentValues = values_by_cache_key_b.setdefault(currentCacheKey, [])
+ currentValues.append(float(perfStat.value))
+
+ # Calculate and baseline (average of values) by cacheKeyB.
+ baseline_by_cache_key_b = {}
+ for currentCacheKey, currentValues in values_by_cache_key_b.items():
+ baseline_by_cache_key_b[currentCacheKey] = PerfStat( \
+ currentCacheKey[0],
+ currentCacheKey[1],
+ currentCacheKey[3],
+ currentCacheKey[2],
+ sum(currentValues) / len(currentValues))
+
+ # Save baselines to the cache.
+ _commit_metric_cache[cacheKeyA] = baseline_by_cache_key_b
+ return baseline_by_cache_key_b.get(cacheKeyB)
# Check test stats. This prints the results for the user.
# actual: the PerfStat with actual value.
@@ -492,18 +526,32 @@ def check_stats_change(actual, baseline, tolerance_dev, allowed_perf_changes = {
return (change, result)
+# Generate a css color (rgb) string based off of the hash of the input.
+def hash_rgb_str(x):
+ res = 10000.0
+ rgb = colorsys.hsv_to_rgb((abs(int(hash(x))) % res)/res, 1.0, 0.9)
+ return "rgb(" + str(int(rgb[0] * 255)) + ", " + str(int(rgb[1] * 255)) + ", " + str(int(rgb[2] * 255)) + ")"
+
if __name__ == '__main__':
parser = argparse.ArgumentParser()
- parser.add_argument("--test-env",
- help="The given test environment to be compared.")
- parser.add_argument("--test-name",
- help="If given, filters table to include only \
- tests matching the given regular expression.")
parser.add_argument("--add-note", nargs=3,
help="Development only. --add-note N commit seed \
Adds N fake metrics to the given commit using the random seed.")
+ parser.add_argument("--chart", nargs='?', default=None, action='store', const='./PerformanceChart.html',
+ help='Create a chart of the results an save it to the given file. Default to "./PerformanceChart.html".')
+ parser.add_argument("--ci", action='store_true',
+ help="Use ci results. You must fetch these with:\n " \
+ + "$ git fetch https://gitlab.haskell.org/ghc/ghc-performance-notes.git refs/notes/perf:refs/notes/ci/perf")
+ parser.add_argument("--test-env",
+ help="The given test environment to be compared. Use 'local' for localy run results. If using --ci, see .gitlab-ci file for TEST_ENV settings.")
+ parser.add_argument("--test-name",
+ help="Filters for tests matching the given regular expression.")
+ parser.add_argument("--metric",
+ help="Test metric (one of " + str(testing_metrics()) + ").")
+ parser.add_argument("--way",
+ help="Test way (one of " + str(testing_metrics()) + ").")
parser.add_argument("commits", nargs=argparse.REMAINDER,
- help="The rest of the arguments will be the commits that will be used.")
+ help="Either a list of commits or a single commit range (e.g. HEAD~10..HEAD).")
args = parser.parse_args()
env = 'local'
@@ -517,16 +565,29 @@ if __name__ == '__main__':
# Main logic of the program when called from the command-line.
#
+ ref = 'perf'
+ if args.ci:
+ ref = 'ci/perf'
+ commits = args.commits
if args.commits:
- for c in args.commits:
- metrics += [CommitAndStat(c, stat) for stat in get_perf_stats(c)]
+ # Commit range
+ if len(commits) == 1 and ".." in commits[0]:
+ commits = list(reversed(commit_log(commits[0])))
+ for c in commits:
+ metrics += [CommitAndStat(c, stat) for stat in get_perf_stats(c, ref)]
+
+ if args.metric:
+ metrics = [test for test in metrics if test.stat.metric == args.metric]
+
+ if args.way:
+ metrics = [test for test in metrics if test.stat.way == args.way]
if args.test_env:
metrics = [test for test in metrics if test.stat.test_env == args.test_env]
if args.test_name:
nameRe = re.compile(args.test_name)
- metrics = [test for test in metrics if nameRe.search(test.test)]
+ metrics = [test for test in metrics if nameRe.search(test.stat.test)]
if args.add_note:
def note_gen(n, commit, delta=''):
@@ -548,66 +609,119 @@ if __name__ == '__main__':
note_gen(args.add_note[0],args.add_note[1],args.add_note[2])
#
- # String utilities for pretty-printing
+ # Chart
#
+ def metricAt(commit, testName, testMetric):
+ values2 = [float(t.stat.value) for t in metrics if t.commit == commit \
+ and t.stat.test == testName \
+ and t.stat.metric == testMetric]
+ if values2 == []:
+ return None
+ else:
+ return (sum(values2) / len(values2))
- row_fmt = '{:18}' * len(args.commits)
- commits = row_fmt.format(*[c[:10] for c in args.commits])
-
- def cmtline(insert):
- return row_fmt.format(*[insert for c in args.commits]).strip()
-
- def header(unit):
- first_line = "{:27}{:30}".format(' ',' ') + cmtline(unit)
- second_line = ("{:27}{:30}".format('Test','Metric') + commits).strip()
-
- # Test Metric c1 c2 c3 ...
- print("-" * (len(second_line)+1))
- print(first_line)
- print(second_line)
- print("-" * (len(second_line)+1))
-
- def commit_string(test, flag):
- def delta(v1, v2):
- return round((100 * (v1 - v2)/v2),2)
-
- # Get the average value per commit (or None if that commit contains no metrics).
- # Note: if the test environment is not set, this will combine metrics from all test environments.
- averageValuesOrNones = []
- for commit in args.commits:
- values = [float(t.stat.value) for t in metrics if t.commit == commit and t.stat.test == test]
- if values == []:
- averageValuesOrNones.append(None)
- else:
- averageValuesOrNones.append(sum(values) / len(values))
-
- if flag == 'metrics':
- strings = [str(v) if v != None else '-' for v in averageValuesOrNones]
- if flag == 'percentages':
- # If the baseline commit has no stats, then we can not produce any percentages.
- baseline = averageValuesOrNones[0]
- if baseline == None:
- strings = ['-' for v in averageValuesOrNones]
- else:
- baseline = float(baseline)
- strings = ['-' if val == None else str(delta(baseline,float(val))) + '%' for val in averageValuesOrNones]
-
- return row_fmt.format(*strings).strip()
+ testSeries = list(set([(test.stat.test_env, test.stat.test, test.stat.metric, test.stat.way) for test in metrics]))
#
- # The pretty-printed output
+ # Use Chart.js to visualize the data.
#
- header('commit')
- # Printing out metrics.
- all_tests = sorted(set([(test.stat.test, test.stat.metric) for test in metrics]))
- for test, metric in all_tests:
- print("{:27}{:30}".format(test, metric) + commit_string(test,'metrics'))
+ if args.chart:
+ commitMsgs = dict([(h, get_commit_message(h)) for h in commits])
+ chartData = {
+ 'type': 'line',
+ 'data': {
+ 'labels': [commitMsgs[h].split("\n")[0] + " (" + \
+ (h[:8] if is_commit_hash(h) else h) + \
+ ")" for h in commits],
+ 'datasets': [{
+ 'label': name + "(" + way + ") " + metric + " - " + env,
+ 'data': [get_commit_metric_value_str_or_none(ref, commit, env, name, metric, way) \
+ for commit in commits],
+
+ 'fill': 'false',
+ 'spanGaps': 'true',
+ 'lineTension': 0,
+ 'backgroundColor': hash_rgb_str((env, name, metric, way)),
+ 'borderColor': hash_rgb_str((env, name, metric, way))
+ } for (env, name, metric, way) in testSeries]
+ },
+ 'options': {}
+ }
+
- # Has no meaningful output if there is no commit to compare to.
- if not singleton_commit:
- header('percent')
+ # Try use local Chart.js file else use online version.
+ tooltipjsFilePath = sys.path[0] + "/js/tooltip.js"
+ chartjsFilePath = sys.path[0] + "/js/Chart-2.8.0.min.js"
+ tooltipjsTag = None
+ try:
+ tooltipjsFile = open(tooltipjsFilePath, "r")
+ tooltipjsTag = '<script>' + tooltipjsFile.read() + '</script>'
+ tooltipjsFile.close()
+ except:
+ print("Failed to load custom tooltip: " + chartjsFilePath + ".")
+ tooltipjsTag = None
+ try:
+ chartjsFile = open(chartjsFilePath, "r")
+ chartjsTag = '<script>' + chartjsFile.read() + '</script>'
+ chartjsFile.close()
+ except:
+ print("Failed to load " + chartjsFilePath + ", reverting to online Chart.js.")
+ chartjsTag = '<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>'
+
+ file = open(args.chart, "w+t")
+ print(\
+ "<html>" + \
+ '<head>\n' + \
+ (tooltipjsTag if tooltipjsTag != None else '') + \
+ chartjsTag + \
+ '</head>' + \
+ '<body style="padding: 20px"><canvas id="myChart"></canvas><script>' + \
+ "var ctx = document.getElementById('myChart').getContext('2d');" + \
+ "var commitMsgs = " + json.dumps(commitMsgs, indent=2) + ";" + \
+ "var chartData = " + json.dumps(chartData, indent=2) + ";" + \
+ (("var chart = new Chart(ctx, setCustomTooltip(chartData, commitMsgs));") \
+ if tooltipjsTag != None else \
+ ("var chart = new Chart(ctx, chartData);")) + \
+ '</script></body>' + \
+ "</html>"\
+ , file=file)
+ file.close()
+ exit(0)
+
+ #
+ # String utilities for pretty-printing
+ #
- # Printing out percentages.
- for test, metric in all_tests:
- print("{:27}{:30}".format(test, metric) + commit_string(test,'percentages'))
+ # T1234 T1234
+ # max_bytes max_bytes
+ # normal normal
+ # commit x86_64-darwin i386-linux-deb9
+ # --------------------------------------------
+ # HEAD 9123 9123
+ # HEAD~1 10023 10023
+ # HEAD~2 21234 21234
+ # HEAD~3 20000 20000
+
+ # Data is already in colum major format, so do that, calculate column widths
+ # then transpose and print each row.
+ def strMetric(x):
+ return '{:.2f}'.format(x.value) if x != None else ""
+
+ headerCols = [ ["","","","Commit"] ] \
+ + [ [name, metric, way, env] for (env, name, metric, way) in testSeries ]
+ dataCols = [ commits ] \
+ + [ [strMetric(get_commit_metric(ref, commit, env, name, metric, way)) \
+ for commit in commits ] \
+ for (env, name, metric, way) in testSeries ]
+ colWidths = [max([2+len(cell) for cell in colH + colD]) for (colH,colD) in zip(headerCols, dataCols)]
+ col_fmts = ['{:>' + str(w) + '}' for w in colWidths]
+
+ def printCols(cols):
+ for row in zip(*cols):
+ # print(list(zip(col_fmts, row)))
+ print(''.join([f.format(cell) for (f,cell) in zip(col_fmts, row)]))
+
+ printCols(headerCols)
+ print('-'*(sum(colWidths)+2))
+ printCols(dataCols)
=====================================
testsuite/driver/testlib.py
=====================================
@@ -19,7 +19,7 @@ import collections
import subprocess
from testglobals import config, ghc_env, default_testopts, brokens, t, TestResult
-from testutil import strip_quotes, lndir, link_or_copy_file, passed, failBecause, failBecauseStderr, str_fail, str_pass
+from testutil import strip_quotes, lndir, link_or_copy_file, passed, failBecause, failBecauseStderr, str_fail, str_pass, testing_metrics
from cpu_features import have_cpu_feature
import perf_notes as Perf
from perf_notes import MetricChange
@@ -384,9 +384,6 @@ def collect_compiler_stats(metric='all',deviation=20):
def collect_stats(metric='all', deviation=20):
return lambda name, opts, m=metric, d=deviation: _collect_stats(name, opts, m, d)
-def testing_metrics():
- return ['bytes allocated', 'peak_megabytes_allocated', 'max_bytes_used']
-
# This is an internal function that is used only in the implementation.
# 'is_compiler_stats_test' is somewhat of an unfortunate name.
# If the boolean is set to true, it indicates that this test is one that
=====================================
testsuite/driver/testutil.py
=====================================
@@ -57,6 +57,10 @@ def lndir(srcdir, dstdir):
os.mkdir(dst)
lndir(src, dst)
+# All possible test metric strings.
+def testing_metrics():
+ return ['bytes allocated', 'peak_megabytes_allocated', 'max_bytes_used']
+
# On Windows, os.symlink is not defined with Python 2.7, but is in Python 3
# when using msys2, as GHC does. Unfortunately, only Administrative users have
# the privileges necessary to create symbolic links by default. Consequently we
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/commit/286827be471f9efa67303d57b979e0c32cb8936e
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/commit/286827be471f9efa67303d57b979e0c32cb8936e
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20190604/b3982df8/attachment-0001.html>
More information about the ghc-commits
mailing list