+{html_doc_end}"""
- # Get all unique sizes (columns) and test names (rows)
- # Using ordered dictionaries to maintain insertion order from loading, then sorting keys
- # Or simply sort the keys after extraction:
- all_sizes = sorted(list(set(size for test_data in results.values() for size in test_data.keys())))
+ all_sizes = sorted(
+ list(set(size for test_data in results.values() for size in test_data.keys()))
+ )
all_test_names = sorted(list(results.keys()))
- html_string = """
-
-
Criterion Benchmark Results
-
Each cell links to the detailed Criterion report for that specific benchmark size.
-
Note: Values shown are the midpoint of the mean confidence interval, formatted for readability.
-
-
-
-
Benchmark Name
+ table_content = """
+
Each cell links to the detailed Criterion.rs report for that specific benchmark size.
+
Note: Values shown are the midpoint of the mean confidence interval, formatted for readability.
"""
- # Add size headers
for size in all_sizes:
- html_string += f"
{html.escape(size)}
\n"
+ table_content += f"
{html.escape(size)}
\n"
- html_string += """
-
-
-
+ table_content += """
+
+
+
"""
- # Add data rows
for test_name in all_test_names:
- html_string += f"
\n"
- html_string += f"
{html.escape(test_name)}
\n"
+ table_content += f"
\n"
+ table_content += f"
{html.escape(test_name)}
\n"
- # Iterate through all possible sizes to ensure columns align
for size in all_sizes:
cell_data = results.get(test_name, {}).get(size)
- mean_value = pd.NA # Default value
- full_report_url = "#" # Default link to self or dummy
+ mean_value = pd.NA
+ full_report_url = "#"
- if cell_data and 'json' in cell_data and 'html_path_relative_to_criterion_root' in cell_data:
+ if (
+ cell_data
+ and "json" in cell_data
+ and "html_path_relative_to_criterion_root" in cell_data
+ ):
try:
- # Extract mean from JSON
- mean_data = cell_data['json'].get("mean")
+ mean_data = cell_data["json"].get("mean")
if mean_data and "confidence_interval" in mean_data:
ci = mean_data["confidence_interval"]
if "lower_bound" in ci and "upper_bound" in ci:
- lower, upper = ci["lower_bound"], ci["upper_bound"]
- if isinstance(lower, (int, float)) and isinstance(upper, (int, float)):
- mean_value = (lower + upper) / 2.0
- else:
- print(f"Warning: Non-numeric bounds for {test_name} ({size}).", file=sys.stderr)
+ lower, upper = ci["lower_bound"], ci["upper_bound"]
+ if isinstance(lower, (int, float)) and isinstance(
+ upper, (int, float)
+ ):
+ mean_value = (lower + upper) / 2.0
+ else:
+ print(
+ f"Warning: Non-numeric bounds for {test_name} ({size}).",
+ file=sys.stderr,
+ )
else:
- print(f"Warning: Missing confidence_interval bounds for {test_name} ({size}).", file=sys.stderr)
+ print(
+ f"Warning: Missing confidence_interval bounds for {test_name} ({size}).",
+ file=sys.stderr,
+ )
else:
- print(f"Warning: Missing 'mean' data for {test_name} ({size}).", file=sys.stderr)
-
- # Construct the full relative URL
- relative_report_path = cell_data['html_path_relative_to_criterion_root']
- full_report_url = f"{html_base_path}{relative_report_path}"
- # Ensure forward slashes and resolve potential double slashes if html_base_path ends in /
- full_report_url = str(Path(full_report_url)).replace('\\', '/')
+ print(
+ f"Warning: Missing 'mean' data for {test_name} ({size}).",
+ file=sys.stderr,
+ )
+ relative_report_path = cell_data[
+ "html_path_relative_to_criterion_root"
+ ]
+ joined_path = Path(html_base_path) / relative_report_path
+ full_report_url = str(joined_path).replace("\\", "/")
except Exception as e:
- print(f"Error processing cell data for {test_name} ({size}): {e}", file=sys.stderr)
- # Keep mean_value as NA and URL as '#'
+ print(
+ f"Error processing cell data for {test_name} ({size}): {e}",
+ file=sys.stderr,
+ )
- # Format the mean value for display
formatted_mean = format_nanoseconds(mean_value)
- # Create the link cell
- # Only make it a link if a valid report path was found
if full_report_url and full_report_url != "#":
- html_string += f'
"""
-
- return html_string
+ return f"{html_doc_start}{table_content}{html_doc_end}"
if __name__ == "__main__":
DEFAULT_CRITERION_PATH = "target/criterion"
- # Default relative path from benchmark_results.html to the criterion root on the hosted site
- # Assumes benchmark_results.html is in .../doc//benchmarks/
- # And target/criterion is copied to .../doc//target/criterion/
- # So the path from benchmarks/ to target/criterion/ is ../target/criterion/
- DEFAULT_HTML_BASE_PATH = "../target/criterion/"
+ DEFAULT_OUTPUT_FILE = "./target/criterion/index.html"
+ DEFAULT_HTML_BASE_PATH = ""
parser = argparse.ArgumentParser(
description="Load Criterion benchmark results from JSON files and generate an HTML table with links to reports."
@@ -250,52 +349,66 @@ if __name__ == "__main__":
"--criterion-dir",
type=str,
default=DEFAULT_CRITERION_PATH,
- help=f"Path to the main 'target/criterion' directory (default: {DEFAULT_CRITERION_PATH}) on the runner.",
+ help=f"Path to the main 'target/criterion' directory (default: {DEFAULT_CRITERION_PATH}) containing benchmark data.",
)
parser.add_argument(
"--html-base-path",
type=str,
default=DEFAULT_HTML_BASE_PATH,
- help=f"Relative URL path from the output HTML file to the hosted 'target/criterion' directory (default: {DEFAULT_HTML_BASE_PATH}).",
+ help=(
+ f"Prefix for HTML links to individual benchmark reports. "
+ f"This is prepended to each report's relative path (e.g., 'benchmark_name/report/index.html'). "
+ f"If the main output HTML (default: '{DEFAULT_OUTPUT_FILE}') is in the 'target/criterion/' directory, "
+ f"this should typically be empty (default: '{DEFAULT_HTML_BASE_PATH}'). "
+ ),
)
parser.add_argument(
"--output-file",
type=str,
- default="benchmark_results.html",
- help="Name of the output HTML file (default: benchmark_results.html)."
+ default=DEFAULT_OUTPUT_FILE,
+ help=f"Path to save the generated HTML summary report (default: {DEFAULT_OUTPUT_FILE}).",
)
-
args = parser.parse_args()
criterion_path = Path(args.criterion_dir)
+ output_file_path = Path(args.output_file)
+
+ try:
+ output_file_path.parent.mkdir(parents=True, exist_ok=True)
+ except OSError as e:
+ print(
+ f"Error: Could not create output directory {output_file_path.parent}: {e}",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+
all_results = load_criterion_reports(criterion_path)
+ # Generate HTML output regardless of whether results were found (handles "no results" page)
+ html_output = generate_html_table_with_links(all_results, args.html_base_path)
+
if not all_results:
print("\nNo benchmark results found or loaded.")
- # Still create an empty file or a file with an error message
- try:
- with open(args.output_file, "w", encoding="utf-8") as f:
- f.write("
Criterion Benchmark Results
No benchmark results found or loaded.
")
- print(f"Created empty/error HTML file: {args.output_file}")
- except IOError as e:
- print(f"Error creating empty/error HTML file {args.output_file}: {e}", file=sys.stderr)
- sys.exit(1) # Indicate failure if no data was loaded successfully
+ # Fallthrough to write the "no results" page generated by generate_html_table_with_links
+ else:
+ print("\nSuccessfully loaded benchmark results.")
+ # pprint(all_results) # Uncomment for debugging
- print("\nSuccessfully loaded benchmark results.")
- # pprint(all_results) # Uncomment for debugging
-
- print(f"Generating HTML table with links using base path: {args.html_base_path}")
- html_output = generate_html_table_with_links(all_results, args.html_base_path)
+ print(
+ f"Generating HTML report with links using HTML base path: '{args.html_base_path}'"
+ )
try:
- with open(args.output_file, "w", encoding="utf-8") as f:
+ with output_file_path.open("w", encoding="utf-8") as f:
f.write(html_output)
- print(f"\nSuccessfully wrote HTML table to {args.output_file}")
- sys.exit(0) # Exit successfully
+ print(f"\nSuccessfully wrote HTML report to {output_file_path}")
+ if not all_results:
+ sys.exit(1) # Exit with error code if no results, though file is created
+ sys.exit(0)
except IOError as e:
- print(f"Error writing HTML output to {args.output_file}: {e}", file=sys.stderr)
+ print(f"Error writing HTML output to {output_file_path}: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
- print(f"An unexpected error occurred while writing HTML: {e}", file=sys.stderr)
- sys.exit(1)
\ No newline at end of file
+ print(f"An unexpected error occurred while writing HTML: {e}", file=sys.stderr)
+ sys.exit(1)