|
| 1 | +# This file is part of Hypothesis, which may be found at |
| 2 | +# https://github.com/HypothesisWorks/hypothesis/ |
| 3 | +# |
| 4 | +# Copyright the Hypothesis Authors. |
| 5 | +# Individual contributors are listed in AUTHORS.rst and the git log. |
| 6 | +# |
| 7 | +# This Source Code Form is subject to the terms of the Mozilla Public License, |
| 8 | +# v. 2.0. If a copy of the MPL was not distributed with this file, You can |
| 9 | +# obtain one at https://mozilla.org/MPL/2.0/. |
| 10 | + |
| 11 | +import json |
| 12 | +import statistics |
| 13 | +from pathlib import Path |
| 14 | + |
| 15 | +import matplotlib.pyplot as plt |
| 16 | +import seaborn as sns |
| 17 | + |
| 18 | +data_path = Path(__file__).parent / "data.json" |
| 19 | +with open(data_path) as f: |
| 20 | + data = json.loads(f.read()) |
| 21 | + |
| 22 | +old_runs = data["old"] |
| 23 | +new_runs = data["new"] |
| 24 | +all_runs = old_runs + new_runs |
| 25 | + |
| 26 | +# every run should involve the same functions |
| 27 | +names = set() |
| 28 | +for run in all_runs: |
| 29 | + names.add(frozenset(run.keys())) |
| 30 | + |
| 31 | +intersection = frozenset.intersection(*names) |
| 32 | +diff = frozenset.union(*[intersection.symmetric_difference(n) for n in names]) |
| 33 | + |
| 34 | +print(f"skipping these tests which were not present in all runs: {', '.join(diff)}") |
| 35 | +names = list(intersection) |
| 36 | + |
| 37 | +# the similar invariant for number of minimal calls per run is not true: functions |
| 38 | +# may make a variable number of minimal() calls. |
| 39 | +# it would be nice to compare identically just the ones which don't vary, to get |
| 40 | +# a very fine grained comparison instead of averaging. |
| 41 | +# sizes = [] |
| 42 | +# for run in all_runs: |
| 43 | +# sizes.append(tuple(len(value) for value in run.values())) |
| 44 | +# assert len(set(sizes)) == 1 |
| 45 | + |
| 46 | +new_names = [] |
| 47 | +for name in names: |
| 48 | + if all(all(x == 0 for x in run[name]) for run in all_runs): |
| 49 | + print(f"no shrinks for {name}, skipping") |
| 50 | + continue |
| 51 | + new_names.append(name) |
| 52 | +names = new_names |
| 53 | + |
| 54 | + |
| 55 | +# name : average calls |
| 56 | +old_values = {} |
| 57 | +new_values = {} |
| 58 | +for name in names: |
| 59 | + |
| 60 | + # mean across the different minimal() calls in a single test function, then |
| 61 | + # median across the n iterations we ran that for to reduce error |
| 62 | + old_vals = [statistics.mean(run[name]) for run in old_runs] |
| 63 | + new_vals = [statistics.mean(run[name]) for run in new_runs] |
| 64 | + old_values[name] = statistics.median(old_vals) |
| 65 | + new_values[name] = statistics.median(new_vals) |
| 66 | + |
| 67 | +# name : (absolute difference, times difference) |
| 68 | +diffs = {} |
| 69 | +for name in names: |
| 70 | + old = old_values[name] |
| 71 | + new = new_values[name] |
| 72 | + diff = old - new |
| 73 | + diff_times = (old - new) / old |
| 74 | + if 0 < diff_times < 1: |
| 75 | + diff_times = (1 / (1 - diff_times)) - 1 |
| 76 | + diffs[name] = (diff, diff_times) |
| 77 | + |
| 78 | + print(f"{name} {int(diff)} ({int(old)} -> {int(new)}, {round(diff_times, 1)}✕)") |
| 79 | + |
| 80 | +diffs = dict(sorted(diffs.items(), key=lambda kv: kv[1][0])) |
| 81 | +diffs_value = [v[0] for v in diffs.values()] |
| 82 | +diffs_percentage = [v[1] for v in diffs.values()] |
| 83 | + |
| 84 | +print( |
| 85 | + f"mean: {int(statistics.mean(diffs_value))}, median: {int(statistics.median(diffs_value))}" |
| 86 | +) |
| 87 | + |
| 88 | + |
| 89 | +# https://stackoverflow.com/a/65824524 |
| 90 | +def align_axes(ax1, ax2): |
| 91 | + ax1_ylims = ax1.axes.get_ylim() |
| 92 | + ax1_yratio = ax1_ylims[0] / ax1_ylims[1] |
| 93 | + |
| 94 | + ax2_ylims = ax2.axes.get_ylim() |
| 95 | + ax2_yratio = ax2_ylims[0] / ax2_ylims[1] |
| 96 | + |
| 97 | + if ax1_yratio < ax2_yratio: |
| 98 | + ax2.set_ylim(bottom=ax2_ylims[1] * ax1_yratio) |
| 99 | + else: |
| 100 | + ax1.set_ylim(bottom=ax1_ylims[1] * ax2_yratio) |
| 101 | + |
| 102 | + |
| 103 | +ax1 = sns.barplot(diffs_value, color="b", alpha=0.7, label="shrink call change") |
| 104 | +ax2 = plt.twinx() |
| 105 | +sns.barplot(diffs_percentage, color="r", alpha=0.7, label=r"n✕ change", ax=ax2) |
| 106 | + |
| 107 | +ax1.set_title("old shrinks - new shrinks (aka shrinks saved, higher is better)") |
| 108 | +ax1.set_xticks([]) |
| 109 | +align_axes(ax1, ax2) |
| 110 | +legend = ax1.legend(labels=["shrink call change", "n✕ change"]) |
| 111 | +legend.legend_handles[0].set_color("b") |
| 112 | +legend.legend_handles[1].set_color("r") |
| 113 | + |
| 114 | +plt.show() |
0 commit comments