Skip to content

Add ccache support to Dockerfiles and build workflow#892

Draft
lukovdm wants to merge 28 commits intostormchecker:masterfrom
lukovdm:cache-actions
Draft

Add ccache support to Dockerfiles and build workflow#892
lukovdm wants to merge 28 commits intostormchecker:masterfrom
lukovdm:cache-actions

Conversation

@lukovdm
Copy link
Copy Markdown
Contributor

@lukovdm lukovdm commented Mar 19, 2026

The PR CI actions where slowing me down quite a bit thus I investigated a bit into how it could be sped up. I added caching of the homebrew downloads and added ccache in all builds with a github cached ccache.

@lukovdm
Copy link
Copy Markdown
Contributor Author

lukovdm commented Mar 19, 2026

It seems like using ccache together with precompiled headers (PCH) does not really work. You get a really low ccache hit rate like this:

Cacheable calls:                     57 / 649 ( 8.78%)
  Hits:                              57 /  57 (100.0%)
    Direct:                          57 /  57 (100.0%)
  Misses:                             0 /  57 ( 0.00%)
Uncacheable calls:                  592 / 649 (91.22%)
  Could not use precompiled header: 592 / 592 (100.0%)

I have done a quick benchmark (on a 24 core machine) of turning PCH on and off with and without a warm cache. These are the results:

PCH cache state build (s) cacheable calls hits misses uncacheable calls
on cold 178.33 57/649(8.78%) 0/57(0.00%) 57/57(100.0%) 592/649(91.22%)
on warm 172.48 57/649(8.78%) 57/57(100.0%) 0/57(0.00%) 592/649(91.22%)
off cold 200.38 648/648(100.0%) 0/648(0.00%) 648/648(100.0%) 0
off warm 56.97 648/648(100.0%) 648/648(100.0%) 0/648(0.00%) 0

So with a cold cache disabling PCH costs time. But with a warm cache all compile calls actually hit the cache, and it is thus much faster. Most time was taken up by the dependencies.

For the GitHub actions CI we would always have a warmish cache, and thus it seems like it would be worth it to me to disable PCH for them.

For people building storm themselves I don't know, it depends on how much we value cold cache builds versus warm cache builds.

@volkm
Copy link
Copy Markdown
Contributor

volkm commented Mar 20, 2026

Nice idea @lukovdm! I think it is good to try to decrease the building times in the CI.
I like the idea of using a cache to have incremental builds.

Some thoughts:

  • I would probably opt to use the caching for PRs and branches other than master. That way, all feature branches have faster CI by incrementally building new commits. Seeing that most PRs nowadays have multiple iterations, this would decrease the computation times. On the master branch, I would still build from scratch to catch potential issues not detected by the incremental build. In general, I would also run more tests on the master than on the feature branch.
  • Interesting to see that PCH have such a negative influence on the cache. Could this be somehow mitigated? I briefly googled and it seems that it should not be a general issues (at least people implemented fixes throughout the years).
  • Do you see any cache issues with dependencies? On my local machine, it sometimes feels to me that a large rebuild is triggered even though not much has changed. I suspect a bit that some of the 3rdpary dependencies could have an influence.
  • In CMake, I suggest to add a new option STORM_COMPILE_WITH_PCH which allows to enable/disable the PCH. This is similar to STORM_COMPILE_WITH_CCACHE. Edit: According to precompiled headers #451 there is cmake_disable_precompiled_headers
  • Why are all the includes necessary now? It is interesting that it compiled before.

@lukovdm
Copy link
Copy Markdown
Contributor Author

lukovdm commented Mar 20, 2026

Thanks for the questions.

Also, some GitHub workflows are not properly caching yet, but the once that are, are running in 4-10 minutes.

@volkm
Copy link
Copy Markdown
Contributor

volkm commented Mar 20, 2026

Thanks for the detailed answers.

One thing I noticed looking at the latest CI run, for example here is that carl and sylvan seem to be rebuild each time. I think that might be due to the way how we configure the fetchcontent.

@sjunges
Copy link
Copy Markdown
Contributor

sjunges commented Mar 20, 2026

Thanks Luko!

Do you have any profiling of the compilation process with a warm start? We do have some ways of profiling storm compilation processes and it would be nice to know where the time is lost.

I second Matthias: For the main branch, PCH seems preferable, in particular as I care about the cold start compile time a lot. (Although this is shifting with more people using binaries).
Should we use cmake_disable_precompiled_headers in the STORM_DEVELOPER mode?

If your goal is to speedup the CI, I think it also really gets time to exclude some tests --, for runs where the CI is failing during compilation, that probably doesn't matter that much, but we sometimes have to wait for CI as we cannot run too many things in parallel...

@volkm
Copy link
Copy Markdown
Contributor

volkm commented Mar 20, 2026

I opened a discussion for a CI revision #894

@volkm
Copy link
Copy Markdown
Contributor

volkm commented Mar 20, 2026

Related to #763

@lukovdm
Copy link
Copy Markdown
Contributor Author

lukovdm commented Mar 20, 2026

I got some profiles.

  • Ignoring the dependencies, with a cold cache, with PCH takes 3m40s, without PCH takes 4m25s. This difference is caused by most files taking a few extra seconds, not one that takes way longer.
  • A warm Ccache and PCH results in all dependencies except spot to be compiled basically instantly. Spot is not affected.
  • Warm ccache without pch is dominated by spot:
image

@volkm
Copy link
Copy Markdown
Contributor

volkm commented Mar 20, 2026

Thanks for the investigation. Looks like spot will be recompiled any time. I think we should figure out why this is happening and whether we can prevent this.

@lukovdm
Copy link
Copy Markdown
Contributor Author

lukovdm commented Mar 20, 2026

I got spot to use ccache and this helped quite a bit again. These benchmarks also have ass tests enabled:

pch cache_state build_seconds cacheable_calls hits misses uncacheable_calls
off cold 331.26 1558/2004(77.74%) 81/1558(5.20%) 1477/1558(94.80%) 446/2004(22.26%)
off warm 22.46 1528/1961(77.92%) 1528/1528(100.0%) 0/1528(0.00%) 433/1961(22.08%)
on cold 286.60 526/2007(26.21%) 24/526(4.56%) 502/526(95.44%) 1481/2007(73.79%)
on warm 239.90 496/1964(25.25%) 496/496(100.0%) 0/496(0.00%) 1468/1964(74.75%)

And warm cache with PCH off:
image

@sjunges
Copy link
Copy Markdown
Contributor

sjunges commented Mar 20, 2026

Ok, what I gather from this are a few questios:

  • The tests are still really slow. I know I never optimized for them, but why do they not profit from the cache.
  • I wonder whether there is more that can be compiled while spot is being built.
  • Spot configure seems to be one of the main bottlenecks.

@sjunges
Copy link
Copy Markdown
Contributor

sjunges commented Mar 20, 2026

Regarding spot: We could

  1. use system-versions of spot again
  2. or we could cache the configure step? (autotools supports cache for the configure step).
  3. There also seem to be a few a configure options (such as disabling python bindings) which probably are useful.

@volkm
Copy link
Copy Markdown
Contributor

volkm commented Mar 23, 2026

  • The time for the tests could be mostly the linking time? But ideally they do not need to be rebuild if nothing changed.
  • The storm CMake target requires the resources target which contains spot. Other resources such as Cudd, sylvan can be build in parallel with Spot, but Spot takes significantly longer than those.
  • The Python bindings should already be disabled.

Things to try:

  • System version of Spot: we could install spot via homebrew and try to install the Debian-based packages for Debian and Ubuntu (https://spot.lre.epita.fr/install.html). We currently only use preinstalled Spot in the Docker storm-dependencies.
  • Caching the configuration steps could be a good option. But in principle, the reconfiguration should only be run if things changed anyway. So it would be good to figure out why it is reconfigured in the first place.

lukovdm added 2 commits March 26, 2026 14:48
…om different sources. Currently we have the old every PR commit trigger and a PR label trigger. They can have different configs.
Comment thread Dockerfile

# Build Storm
#############
RUN if ! command -v ccache >/dev/null 2>&1; then \
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.

Can you write some comments here to clarify what this does (and why) for other users.

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.

Also can we make the usage of ccache configurable?

@sjunges
Copy link
Copy Markdown
Contributor

sjunges commented Apr 16, 2026

Code/build changes LGTM; @volkm can you go over the github action changes?

Copy link
Copy Markdown
Contributor

@volkm volkm left a comment

Choose a reason for hiding this comment

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

Thanks for tackling this undertaking of revising our CI. I like the new structure and it should decrease the time for CI and make maintenance a bit easier. Great work!

I have a number of comments, I hope most are just small ones.

Comment thread CMakeLists.txt
set(STORM_DEBUG_SPOT ON)
set(STORM_DEBUG_SYLVAN ON)
set(STORM_WARNING_AS_ERROR ON)
# Turn off PCH for faster ccache usage on warm caches.
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.

Suggested change
# Turn off PCH for faster ccache usage on warm caches.
# Turn off PCH for faster ccache usage on warm caches. PCH compiled files cannot be properly cached currently.

Comment thread CMakeLists.txt
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)

if(STORM_COMPILE_WITH_PCH)
message(STATUS "STORM - PCH is enabled which will lead to significantly lower ccache hit rates. Consider disabling PCH if you plan to rebuild storm multiple times.")
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.

Suggested change
message(STATUS "STORM - PCH is enabled which will lead to significantly lower ccache hit rates. Consider disabling PCH if you plan to rebuild storm multiple times.")
message(STATUS "STORM - PCH is enabled which will lead to significantly lower ccache hit rates. Consider disabling PCH if you plan to rebuild Storm multiple times.")

Comment thread CMakeLists.txt
mark_as_advanced(CCACHE_FOUND)
if(CCACHE_FOUND)
message(STATUS "Storm - Using ccache")
set(ENV{CCACHE_SLOPPINESS} "pch_defines,time_macros,include_file_mtime,include_file_ctime")
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.

Can you add an explanations for this selection here?

Comment thread CMakeLists.txt
"<optional>" "<ostream>" "<istream>" "<list>" "<set>" "<fstream>" "<string>" "<boost/optional.hpp>" "<boost/variant.hpp>"
"<boost/container/flat_set.hpp>" "<boost/dynamic_bitset.hpp>" "<boost/range/irange.hpp>")

function(storm_target_precompile_headers target)
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.

Maybe move to resources/cmake/macros or to src/CMakeLists.txt?

Comment thread Dockerfile
# Build Storm
# (This can be adapted to only build 'storm' or 'binaries' depending on custom needs)
RUN make -j $no_threads
RUN ccache --max-size=3G && ccache --zero-stats && make -j $no_threads && ccache --show-stats --verbose
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.

Can we make the magic number 3G configurable?
Is showing the ccache stats necessary? Users who simply build Storm probably do not care about it.

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 do we actually need a call to ccache? Should STORM_COMPILE_WITH_CCACHE not automatically take care of it?

type: string
default: "Debug"
Developer:
description: "Enable developer mode (ON/OFF)"
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.

In the interest of nicer handling and especially avoiding invalid input, there could be two options for improvement:

  1. Use custom options "ON"/"OFF" is inputs using input type choice
  2. Use boolean as input and convert to ON/OFF via something like ${{ input && 'ON' || 'OFF' }}

restore-keys: |
buildtest-brew-${{ inputs.distro }}-
- name: Ensure ccache directory exists (macOS)
run: mkdir -p /Users/runner/.ccache
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.

Suggested change
run: mkdir -p /Users/runner/.ccache
if: inputs.restore_cache
run: mkdir -p /Users/runner/.ccache

brew update
brew install automake boost ccache cln ginac glpk hwloc libarchive xerces-c z3
- name: Prepare ccache
run: |
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.

Also guarded by save_cache or restore_cache? Also in more places later on.

--build-arg cmake_args="-DSTORM_WARNING_AS_ERROR=ON -DSTORM_COMPILE_WITH_PCH=OFF"
# Omitting arguments developer, disable_*, cln_exact, cln_ratfunc, all_sanitizers
- name: Export ccache from Docker image
run: |
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.

Should be guarded by save_cache or restore_cache? Also in more places.

- Summary table in stdout
- CSV at <build-root>/results.csv
- Per-run ccache stats in <build-root>/results/
- Optional traces at <build-root>/results/trace-<pch>-<cache>.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.

Suggested change
- Optional traces at <build-root>/results/trace-<pch>-<cache>.json
- Optional traces at <build-root>/results/trace-<pch>-<cache>.json

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants