Skip to content

[RFQ] Content hashes at home#9738

Open
oharboe wants to merge 3 commits intoThe-OpenROAD-Project:masterfrom
Pinata-Consulting:content-cache
Open

[RFQ] Content hashes at home#9738
oharboe wants to merge 3 commits intoThe-OpenROAD-Project:masterfrom
Pinata-Consulting:content-cache

Conversation

@oharboe
Copy link
Collaborator

@oharboe oharboe commented Mar 12, 2026

ccache Performance Benchmark for OpenROAD Bazel Build

Why ccache?

Bazel's built-in action cache is keyed on the action graph (flags, BUILD files,
dependency versions), not on source-file contents. Common developer workflows
-- switching branches, editing a BUILD file, or running bazel clean -- invalidate
the action cache even when most source files are unchanged.

ccache caches based on preprocessed source content, so it survives these
invalidations and delivers near-instant cache hits for unchanged translation units.

Environment

Item Value
CPU Intel(R) Core(TM) i9-9900KF CPU @ 3.60GHz (16 cores)
RAM 30Gi
OS Ubuntu 25.10
ccache ccache version 4.11.2
Bazel bazel 8.5.0
Commit 82027a28d3
Sources 1005 .cpp/.cc files, 815 .h/.hpp files, 33 modules
Build targets 83 cc_library targets (excluding gui)
Runs per scenario 3 (median reported)

Developer Personas (from git history)

We analyzed the last 500 commits to identify typical developer workflows:

Persona Description Simulated Change Example Contributors
Single-tool bug fix Fix a bug in one module 1 .cc file in rsz Thinh Nguyen, Jaehyun Kim
Algorithm improvement Improve algorithm in one tool 3 .cpp files in grt Augusto Berndt, Eder Monteiro
Cross-cutting refactor Rename API, clang-tidy cleanup 20 files across 5 modules Henner Zeller

Methodology

  1. Full build with --config=ccache to warm ccache (all 83 cc_library targets)
  2. For each scenario, repeat 3 times:
    a. Append a unique comment to the scenario's source files (real content change, not just touch)
    b. bazel clean to invalidate Bazel's action cache
    c. Rebuild with ccache -- record wall-clock time and hit rate
    d. Restore files via git checkout
    e. Re-apply same edits, bazel clean again
    f. Rebuild without ccache (CCACHE_DISABLE=1) -- record wall-clock time
    g. Restore files
  3. Bazel disk cache and remote cache disabled (--disk_cache= --remote_cache=) to isolate ccache effect
  4. Machine was dedicated to the benchmark (no other builds running)
  5. Median of 3 runs reported

Results

Scenario Files Changed With ccache (s) Without ccache (s) Speedup ccache Hits
Single-tool bug fix 1 143.1 1521.3 10.6x 5790/5790
Algorithm improvement 3 147.6 1522.4 10.3x 5790/5790
Cross-cutting refactor 20 154.0 1522.8 9.8x 5790/5790

Key Findings

  • 10x speedup across all scenarios -- ccache reduces rebuild time from ~25 minutes
    to ~2.5 minutes after bazel clean.
  • 100% cache hit rate (5790/5790) -- even the cross-cutting refactor touching 20 files
    across 5 modules achieves full hits because Bazel recompiles all targets after
    bazel clean, but ccache recognizes unchanged preprocessed content.
  • Consistent with-ccache times (~145s) -- the number of changed files barely affects
    rebuild time because ccache serves all unchanged translation units from cache.
    The small increase from 143s to 154s reflects the additional files that must be
    genuinely recompiled.
  • Without-ccache times are stable (~1522s) -- confirming the machine was uncontested
    and results are reliable.

When does this help?

Any workflow that invalidates Bazel's action cache while leaving most source unchanged:

  • bazel clean -- full action cache wipe, but ccache still has all objects
  • Branch switching -- different action graph, same source content
  • BUILD file edits -- invalidates the target's action cache even if no source changed
  • Merging origin/master -- MODULE.bazel or BUILD changes cascade through the action graph
  • Flag changes -- different -c opt vs -c dbg etc.

Setup

Add to your user.bazelrc:

echo 'build --config=ccache' >> user.bazelrc

Or invoke explicitly:

bazel build --config=ccache //...

Reproducing

The benchmark script is at tools/ccache_benchmark.sh:

bash tools/ccache_benchmark.sh --runs=3

@oharboe oharboe changed the title Content cache at home Content hashes at home Mar 12, 2026
@oharboe
Copy link
Collaborator Author

oharboe commented Mar 12, 2026

@hzeller Thoughts?

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces ccache support to improve local build times, which is a valuable enhancement for developer productivity. The implementation via a patch to toolchains_llvm and a new Bazel configuration is well-executed. The included refactoring of genrules to more specific bazel_skylib rules is also a good cleanup. I have one suggestion to make the ccache integration more robust for users with customized environments.

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

1 similar comment
@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@hzeller
Copy link
Collaborator

hzeller commented Mar 12, 2026

I always comment out the llvm toolchain, so I don't know what it does. But usually bazel does employ some content-based hashing, but @QuantamHD might know more.

One problem we have is that we have currently have super coarse grained libraries due to the historic way OpenROAD is compiled, so many files are compiled into one library instead have having many small libraries. So if any of the files in these change, the compilation that builds that probably has to rebuild all as they use these param files (don't know exactly).

Anyway, if ccache works for you, you should use it. Longer term, we should structure the project to be more fine-grained.

@oharboe
Copy link
Collaborator Author

oharboe commented Mar 12, 2026

The idea is to have ccache mitigate the coarse grained libraries.

@oharboe

This comment was marked as outdated.

@maliberty
Copy link
Member

This is painful during active rebasing where header/flag churn causes frequent cache misses.

If headers change you should recompile. Flags change rarely. How often do you encounter this? It's never been a pain point for me.

@oharboe
Copy link
Collaborator Author

oharboe commented Mar 12, 2026

This is painful during active rebasing where header/flag churn causes frequent cache misses.

If headers change you should recompile. Flags change rarely. How often do you encounter this? It's never been a pain point for me.

I find myself recompiling all the time.

@vvbandeira
Copy link
Member

Insufficient disk space on the Jenkins worker for the Bazel build cache and sandbox. This is an infrastructure issue, not a code issue.

Yesterday we reduced the workers disk from 500GB to 100GB to allow more workers with the same resources.
Even after all the dependencies are installed I am seeing less than 20GB of disk used and ~80GB available.
@oharboe, is it normal or expected for Bazel to use more than 80GB of local disk?

@jenkins-worker-central1-c3d-1kx6ey:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1   99G   17G   78G  18% /

oharboe and others added 2 commits March 12, 2026 15:04
Patch toolchains_llvm cc_wrapper.sh to use ccache if installed.
Falls through to plain clang when ccache is absent.

Tested: after bazel clean --expunge and removing ~/.cache/bazel-disk-cache,
bazel build --config=ccache //src/drt rebuilt with 1339/1339 (100%)
ccache direct hits in 3m49s. A trivial comment change to
MakeTritonRoute.cpp produced exactly 1 ccache miss + 1338 hits,
confirming ccache correctly detects changed preprocessed content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
Apply ccache integration to the toolchains_llvm osx_cc_wrapper
template, matching the Linux cc_wrapper ccache support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe
Copy link
Collaborator Author

oharboe commented Mar 12, 2026

I have not studied disk usage.

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@QuantamHD
Copy link
Collaborator

I think this is not a good solution, and a better solution would be to break up the existing coarse grained targets into fine grained rules rather than mask this problem with another layer of caching.

@oharboe
Copy link
Collaborator Author

oharboe commented Mar 13, 2026

I think this is not a good solution, and a better solution would be to break up the existing coarse grained targets into fine grained rules rather than mask this problem with another layer of caching.

Makes sense to me if we can have it in some reasonable amount of time. This could be a stopgap. I have an experiment to measure the benefit of this, which is interesting information, regardless of solution.

@oharboe oharboe changed the title Content hashes at home [RFQContent hashes at home Mar 13, 2026
@oharboe oharboe changed the title [RFQContent hashes at home [RFQ] Content hashes at home Mar 13, 2026
Benchmark measuring ccache benefit for typical developer workflows
(single-tool fix, algorithm improvement, cross-cutting refactor).
Results show 10x speedup across all scenarios with 100% cache hit rate
after `bazel clean`, reducing rebuild from ~25 min to ~2.5 min.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe
Copy link
Collaborator Author

oharboe commented Mar 13, 2026

@QuantamHD @hzeller @maliberty Fantastic if it is true: 10x faster while we wait for bazel migration to complete at which point we can retire this. Unless bazel grows this feature and we can also retire this.

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

1 similar comment
@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@oharboe oharboe requested a review from hzeller March 13, 2026 22:00
@oharboe
Copy link
Collaborator Author

oharboe commented Mar 14, 2026

@maliberty @hzeller @QuantamHD Killer use-ase: caching across git work trees...

OpenROAD Build Optimization: ccache Integration (PR #9738)

Executive Summary

Pull Request #9738 introduces ccache support into the OpenROAD Bazel build system. While Bazel has a built-in action cache, it is often invalidated by common developer workflows (switching branches, editing BUILD files, or using git worktree). This PR adds a secondary layer of caching that looks at source content rather than action graphs.

The Problem: Why Bazel Cache Isn't Enough

Bazel's native cache is "fragile" in local development because it is keyed on the Action Graph. If any of the following change, Bazel may force a recompile even if the .cpp file is identical:

  • Toolchain changes: Updating compiler versions or flags.
  • Workspace paths: Moving code or using different git worktree locations.
  • Metadata changes: Modifying a BUILD file or MODULE.bazel, even if it doesn't affect the specific C++ logic.

The Solution: How ccache Helps

ccache functions as a compiler wrapper. It hashes the preprocessed source code and the compiler options. If the resulting hash matches a previous build, it retrieves the object file from a global cache directory (usually ~/.cache/ccache) instead of recompiling.

[Image of ccache architecture showing how it intercepts compiler calls and interacts with the cache]

Key Use Case: Cross-Worktree & Branch Caching

The most significant benefit of this PR is for developers managing multiple tasks:

  • Git Worktrees: Usually, each worktree has its own Bazel output base. Without ccache, you have to compile the same code from scratch for every new worktree. With this PR, worktrees share the global ccache storage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants