From 0b91da04b35cc42cd0f6f62a0a0ebd1198448447 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sun, 9 Jan 2022 18:41:02 +0100 Subject: [PATCH] Add benchmarking plots --- 2021/README.md | 12 +++++ 2021/create_timing_plots.py | 97 +++++++++++++++++++++++++++++++++++++ 2021/cumulative-time.svg | 1 + 2021/individual-time.svg | 1 + 4 files changed, 111 insertions(+) create mode 100755 2021/create_timing_plots.py create mode 100644 2021/cumulative-time.svg create mode 100644 2021/individual-time.svg diff --git a/2021/README.md b/2021/README.md index bbbf89b..dc7705c 100644 --- a/2021/README.md +++ b/2021/README.md @@ -20,3 +20,15 @@ OPTIONS: -i, --input Read input from the given file instead of stdin -t, --time Print time taken ``` + +## That goal was achieved + +Runtime benchmarked with [Criterion], reading input directly from memory to avoid disk IO +inconsistencies. + +![Cumulative time](./cumulative-time.svg) + +![Time by day](./individual-time.svg) + + +[Criterion]: https://github.com/bheisler/criterion.rs diff --git a/2021/create_timing_plots.py b/2021/create_timing_plots.py new file mode 100755 index 0000000..10aef43 --- /dev/null +++ b/2021/create_timing_plots.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +import json +from pathlib import Path +from typing import Dict + +import numpy as np +import matplotlib.pyplot as plt + + +def read_timings() -> Dict[int, Dict]: + timings = {} + + for day in Path('target/criterion/part1').iterdir(): + with open(day / 'new' / 'estimates.json', mode='rb') as f: + timings[int(day.parts[-1])] = { + 1: json.load(f) + } + + for day in Path('target/criterion/part2').iterdir(): + with open(day / 'new' / 'estimates.json', mode='rb') as f: + timings[int(day.parts[-1])][2] = json.load(f) + + return timings + + +def plot_cumulative_time(timings: Dict[int, Dict]): + plt.clf() + + times = [0] + + for day in range(min(timings.keys()), max(timings.keys()) + 1): + times.append(timings[day][1]['mean']['point_estimate']) + if day < 25: + times.append(timings[day][2]['mean']['point_estimate']) + else: + times.append(0) + + cumulative = np.cumsum(times) + # Convert from nanoseconds to seconds + cumulative /= 1e9 + + x = np.arange(0.0, 25.5, 0.5) + + plt.plot(x, cumulative, label="Cumulative time", drawstyle='steps-post') + plt.plot([0, 25], [0, 0.5], label="Target time") + plt.ylabel('Cumulative time (s)') + plt.xlabel('Days completed') + + plt.legend() + plt.tight_layout() + + plt.xlim(0, 25) + plt.ylim(0, 0.5) + + plt.savefig('cumulative-time.svg') + + +def plot_individual_times(timings: Dict[int, Dict]): + plt.clf() + + def plot(parts, **kwargs): + x = np.arange(1, len(parts) + 1) + + values = np.array(list(part['mean']['point_estimate'] for part in parts)) + upper = np.array(list(part['mean']['confidence_interval']['upper_bound'] for part in parts)) + lower = np.array(list(part['mean']['confidence_interval']['lower_bound'] for part in parts)) + + # Convert from ns to s + yerr = np.array([upper - values, lower - values]) / 1e9 + values = values / 1e9 + + plt.bar(x, values, yerr=yerr, align='edge', log=True, **kwargs) + pass + + plot(list(timings[day][1] for day in range(1, 26)), label="Part 1", width=-0.4) + plot(list(timings[day][2] for day in range(1, 25)), label="Part 2", width=0.4) + + plt.ylabel('Runtime (s)') + plt.xlabel('Day') + + plt.xlim(0, 26) + plt.xticks(np.arange(1, 26)) + + plt.legend() + plt.tight_layout() + + plt.savefig('individual-time.svg') + + +def main(): + timings = read_timings() + plot_cumulative_time(timings) + plot_individual_times(timings) + + +if __name__ == '__main__': + main() diff --git a/2021/cumulative-time.svg b/2021/cumulative-time.svg new file mode 100644 index 0000000..82ee8aa --- /dev/null +++ b/2021/cumulative-time.svg @@ -0,0 +1 @@ + diff --git a/2021/individual-time.svg b/2021/individual-time.svg new file mode 100644 index 0000000..6a1794f --- /dev/null +++ b/2021/individual-time.svg @@ -0,0 +1 @@ +