Files
TheRock/build_tools/hack/ccache/compare_ccache_by_project.py
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

139 lines
4.4 KiB
Python

#!/usr/bin/env python
# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""Compare ccache hit rates per subproject across two log files.
Useful for comparing Linux vs Windows hit rates, or before/after a config
change, to identify which subprojects have cache hit regressions.
Usage:
python build_tools/hack/ccache/compare_ccache_by_project.py \
/path/to/linux/ccache.log /path/to/windows/ccache.log
"""
import re
import sys
from collections import Counter
def parse_by_project(log_file: str) -> dict:
"""Parse ccache log and return per-project hit/miss counts for clang."""
pid_data = {}
with open(log_file, "r", errors="replace") as f:
for line in f:
m = re.match(r"\[.*? (\d+)\s*\]", line)
if not m:
continue
pid = m.group(1)
if pid not in pid_data:
pid_data[pid] = {}
if "Source file:" in line:
mm = re.search(r"Source file: (.+)", line)
if mm:
pid_data[pid]["source"] = mm.group(1).strip()
elif "Compiler:" in line and "Compiler type" not in line:
mm = re.search(r"Compiler: (.+)", line)
if mm:
pid_data[pid]["compiler"] = mm.group(1).strip()
elif "Result: direct_cache_hit" in line:
pid_data[pid]["result"] = "hit"
elif "Result: cache_miss" in line:
if pid_data[pid].get("result") != "hit":
pid_data[pid]["result"] = "miss"
# Aggregate by project (clang compilations only, skip CMake probes)
projects = Counter()
project_hits = Counter()
for d in pid_data.values():
src = d.get("source", "")
comp = d.get("compiler", "")
result = d.get("result", "")
if not src or not result:
continue
if "clang" not in comp:
continue
if "TryCompile" in src or "CMakeScratch" in src or "cmTC_" in src:
continue
project = _extract_project(src)
projects[project] += 1
if result == "hit":
project_hits[project] += 1
return {p: (project_hits[p], projects[p]) for p in projects}
def _extract_project(src: str) -> str:
"""Extract project name from source path."""
src = src.replace("\\", "/")
# Source tree: .../projects/{name}/...
m = re.search(r"/projects/([^/]+)/", src)
if m:
return m.group(1).lower()
# Build tree: .../math-libs/{group}/{name}/... or .../math-libs/{name}/...
m = re.search(r"(?:math-libs|ml-libs)/(?:BLAS/)?([^/]+)/", src)
if m:
return m.group(1).lower()
# Third-party
m = re.search(r"third-party/([^/]+)/", src)
if m:
return "3p-" + m.group(1).lower()
return "?"
def main():
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} LOG1 LOG2")
sys.exit(1)
log1, log2 = sys.argv[1], sys.argv[2]
print("Parsing log 1...", file=sys.stderr)
data1 = parse_by_project(log1)
print("Parsing log 2...", file=sys.stderr)
data2 = parse_by_project(log2)
all_projects = sorted(set(data1.keys()) | set(data2.keys()))
total1_hits = sum(h for h, t in data1.values())
total1 = sum(t for h, t in data1.values())
total2_hits = sum(h for h, t in data2.values())
total2 = sum(t for h, t in data2.values())
print(f"\n{'Project':<30s} {'Log 1':>18s} {'Log 2':>18s} {'Gap':>8s}")
print(f"{'':<30s} {'hits/total rate':>18s} {'hits/total rate':>18s}")
print("-" * 78)
for proj in all_projects:
h1, t1 = data1.get(proj, (0, 0))
h2, t2 = data2.get(proj, (0, 0))
r1 = f"{100 * h1 / t1:.0f}%" if t1 > 0 else "-"
r2 = f"{100 * h2 / t2:.0f}%" if t2 > 0 else "-"
gap = ""
if t1 > 0 and t2 > 0:
diff = (h2 / t2 - h1 / t1) * 100
gap = f"{diff:+.0f}%"
s1 = f"{h1}/{t1} {r1:>5s}" if t1 > 0 else f"{'--':>12s}"
s2 = f"{h2}/{t2} {r2:>5s}" if t2 > 0 else f"{'--':>12s}"
print(f" {proj:<28s} {s1:>18s} {s2:>18s} {gap:>8s}")
print("-" * 78)
r1 = f"{100 * total1_hits / total1:.1f}%" if total1 > 0 else "-"
r2 = f"{100 * total2_hits / total2:.1f}%" if total2 > 0 else "-"
print(
f" {'TOTAL':<28s} {total1_hits}/{total1} {r1:>5s}"
f" {total2_hits}/{total2} {r2:>5s}"
)
if __name__ == "__main__":
main()