|
| 1 | +import re |
| 2 | +import os |
| 3 | +import sys |
| 4 | +import csv |
| 5 | +import subprocess |
| 6 | +import tempfile |
| 7 | +from functools import partial |
| 8 | +from dataclasses import dataclass |
| 9 | +from pathlib import Path |
| 10 | +from typing import Union, Sequence |
| 11 | + |
| 12 | + |
| 13 | +BASELINE = '8dd1aa8539060a511d0f85779ae2c8019162f567' |
| 14 | +BENCH_EXAMPLES = [('kernelregression', 10), ('psd', 10), ('fluidsim', 10), ('regression', 10)] |
| 15 | + |
| 16 | + |
| 17 | +def run(*args, capture=False, env=None): |
| 18 | + print('> ' + ' '.join(map(str, args))) |
| 19 | + return subprocess.run(args, check=True, text=True, capture_output=capture, env=env) |
| 20 | + |
| 21 | + |
| 22 | +def read(*args, **kwargs): |
| 23 | + return run(*args, capture=True, **kwargs).stdout |
| 24 | + |
| 25 | + |
| 26 | +def read_stderr(*args, **kwargs): |
| 27 | + return run(*args, capture=True, **kwargs).stderr |
| 28 | + |
| 29 | + |
| 30 | +def build(commit): |
| 31 | + if os.path.exists(commit): |
| 32 | + print(f'Skipping the build of {commit}') |
| 33 | + else: |
| 34 | + run('git', 'checkout', commit) |
| 35 | + run('make', 'install', env=dict(os.environ, PREFIX=commit)) |
| 36 | + dex_bin = Path.cwd() / commit / 'dex' |
| 37 | + return dex_bin |
| 38 | + |
| 39 | + |
| 40 | +def benchmark(baseline_bin, latest_bin): |
| 41 | + with tempfile.TemporaryDirectory() as tmp: |
| 42 | + def clean(bin, uniq): |
| 43 | + run(bin, 'clean', env={'XDG_CACHE_HOME': Path(tmp) / uniq}) |
| 44 | + def bench(bin, uniq, bench_name, path): |
| 45 | + return parse_result( |
| 46 | + read_stderr(bin, 'script', path, '+RTS', '-s', |
| 47 | + env={'XDG_CACHE_HOME': Path(tmp) / uniq})) |
| 48 | + baseline_clean = partial(clean, baseline_bin, 'baseline') |
| 49 | + baseline_bench = partial(bench, baseline_bin, 'baseline') |
| 50 | + latest_clean = partial(clean, latest_bin, 'latest') |
| 51 | + latest_bench = partial(bench, latest_bin, 'latest') |
| 52 | + results = [] |
| 53 | + for example, repeats in BENCH_EXAMPLES: |
| 54 | + path = Path('examples') / (example + '.dx') |
| 55 | + # warm-up the caches |
| 56 | + baseline_clean() |
| 57 | + baseline_bench(example, path) |
| 58 | + latest_clean() |
| 59 | + latest_bench(example, path) |
| 60 | + for i in range(repeats): |
| 61 | + print(f'Iteration {i}') |
| 62 | + baseline_alloc, baseline_time = baseline_bench(example, path) |
| 63 | + latest_alloc, latest_time = latest_bench(example, path) |
| 64 | + print(baseline_alloc, '->', latest_alloc) |
| 65 | + print(baseline_time, '->', latest_time) |
| 66 | + results.append(Result(example, 'alloc', latest_alloc)) |
| 67 | + results.append(Result(example, 'time_rel', latest_time / baseline_time)) |
| 68 | + return results |
| 69 | + |
| 70 | + |
| 71 | +@dataclass |
| 72 | +class Result: |
| 73 | + benchmark: str |
| 74 | + measure: str |
| 75 | + value: Union[int, float] |
| 76 | + |
| 77 | + |
| 78 | +ALLOC_PATTERN = re.compile(r"^\s*([0-9,]+) bytes allocated in the heap", re.M) |
| 79 | +TIME_PATTERN = re.compile(r"^\s*Total\s*time\s*([0-9.]+)s", re.M) |
| 80 | +def parse_result(output): |
| 81 | + alloc_line = ALLOC_PATTERN.search(output) |
| 82 | + if alloc_line is None: |
| 83 | + raise RuntimeError("Couldn't extract total allocations") |
| 84 | + total_alloc = int(alloc_line.group(1).replace(',', '')) |
| 85 | + time_line = TIME_PATTERN.search(output) |
| 86 | + if time_line is None: |
| 87 | + raise RuntimeError("Couldn't extract total time") |
| 88 | + total_time = float(time_line.group(1)) |
| 89 | + return total_alloc, total_time |
| 90 | + |
| 91 | + |
| 92 | +def save(commit, results: Sequence[Result], datapath, commitpath): |
| 93 | + with open(datapath, 'a', newline='') as datafile: |
| 94 | + writer = csv.writer(datafile, delimiter=',', quotechar='"', dialect='unix') |
| 95 | + for r in results: |
| 96 | + writer.writerow((commit, r.benchmark, r.measure, r.value)) |
| 97 | + with open(commitpath, 'a', newline='') as commitfile: |
| 98 | + writer = csv.writer(commitfile, delimiter=',', quotechar='"', dialect='unix') |
| 99 | + date = read('git', 'show', '-s', '--format=%ct', commit, '--').strip() |
| 100 | + writer.writerow([commit, date]) |
| 101 | + |
| 102 | + |
| 103 | +def main(argv): |
| 104 | + if len(argv) != 3: |
| 105 | + raise ValueError("Expected three arguments!") |
| 106 | + datapath, commitpath, commit = argv |
| 107 | + print('Building baseline: {BASELINE}') |
| 108 | + baseline_bin = build(BASELINE) |
| 109 | + print(f'Building latest: {commit}') |
| 110 | + latest_bin = build(commit) |
| 111 | + results = benchmark(baseline_bin, latest_bin) |
| 112 | + save(commit, results, datapath, commitpath) |
| 113 | + print('DONE!') |
| 114 | + |
| 115 | + |
| 116 | +if __name__ == '__main__': |
| 117 | + main(sys.argv[1:]) |
0 commit comments