Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions run_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
import os
import pathlib
import sys
import json
import sqlite3

from multiprocessing import Process
from typing import Dict, List
from npbench.infrastructure import (Benchmark, generate_framework, LineCount,
Test, utilities as util)



def run_benchmark(benchname, fname, preset, validate, repeat, timeout,
ignore_errors, save_strict, load_strict):
frmwrk = generate_framework(fname, save_strict, load_strict)
Expand All @@ -19,6 +23,63 @@ def run_benchmark(benchname, fname, preset, validate, repeat, timeout,
test.run(preset, validate, repeat, timeout, ignore_errors)



def filter_out_completed_benchmarks(
framework_name: str,
preset: str,
all_benchmarks: List[str],
benchname_to_shortname_mapping: Dict[str, str],
) -> List[str]:


db_path = pathlib.Path("npbench.db")

# No DB → nothing measured yet
if not db_path.exists():
print("Database does not exist, running all benchmarks")
return all_benchmarks

try:
with sqlite3.connect(db_path) as conn:
cur = conn.cursor()

# Check if results table exists
cur.execute("""
SELECT name FROM sqlite_master
WHERE type='table' AND name='results'
""")
if cur.fetchone() is None:
print("Results table does not exist, running all benchmarks")
return all_benchmarks

# Query measured benchmarks
cur.execute("""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to also check whether sufficient executions have been recorded? To be clear, all executions for a specific benchmark (in a specific run) are recorded together. Therefore, a partial benchmark, e.g., with 5 out of 10 desired repetitions, is not possible. So, such a feature would only make sense if, in subsequent jobs, the number of repetitions was increased.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. I was just not sure how to detect whether all desired repetitions had been completed. We can assume that if a timeout occurs and the desired repetitions are R, then there should be 2 possible values in the database at any given time. (R and another integer that is less than R in value, but not 3 unique values).

I can check for this. What do you think?

SELECT DISTINCT benchmark
FROM results
WHERE framework = ? AND preset = ?
""", (framework_name, preset))

measured_benchmarks = [row[0] for row in cur.fetchall()]

except sqlite3.Error as e:
# Any SQLite issue → be conservative
print(f"SQLite error ({e}), running all benchmarks")
return all_benchmarks

remaining_benchmarks = [
bn
for bn in all_benchmarks
if benchname_to_shortname_mapping[bn] not in measured_benchmarks
]

print(
f"Skipping {measured_benchmarks} for framework {framework_name} "
f"as they are already measured and in the database"
)

return remaining_benchmarks


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-f",
Expand Down Expand Up @@ -57,13 +118,33 @@ def run_benchmark(benchname, fname, preset, validate, repeat, timeout,
type=util.str2bool,
nargs="?",
default=False)
parser.add_argument("-e",
"--skip-existing-benchmarks",
type=util.str2bool,
nargs="?",
default=False)
args = vars(parser.parse_args())

parent_folder = pathlib.Path(__file__).parent.absolute()
bench_dir = parent_folder.joinpath("bench_info")
pathlist = pathlib.Path(bench_dir).rglob('*.json')
benchnames = [os.path.basename(path)[:-5] for path in pathlist]
benchnames.sort()


if args["skip_existing_benchmarks"]:
benchname_to_shortname_mapping = dict()
json_dir = bench_dir
for json_file in json_dir.glob("*.json"):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you just get the benchmark names from line 132 here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to load the JSON to get the short benchmark name, since the database already has it.

JSON files' names do not necessarily have long names, so I have to build the mapping. This dictionary needs to load all the JSONs and read the "short_name" field anyway.

with open(json_file, "r") as f:
data = json.load(f)

short_name = data["benchmark"]["short_name"]
benchname = os.path.basename(json_file).replace(".json", "")
benchname_to_shortname_mapping[benchname] = short_name

benchnames = filter_out_completed_benchmarks(args["framework"], args["preset"], benchnames, benchname_to_shortname_mapping)

failed = []
for benchname in benchnames:
p = Process(target=run_benchmark,
Expand Down