diff --git a/.github/workflows/ccache-probe.yml b/.github/workflows/ccache-probe.yml new file mode 100644 index 000000000..96e790900 --- /dev/null +++ b/.github/workflows/ccache-probe.yml @@ -0,0 +1,110 @@ +--- +# Throwaway diagnostic: why does master's ccache serve only ~14% of calls when a +# whole pull-request cache is restored? +# +# It builds the Arch leg's real command (`--server --debug --test`, the same as +# .github/workflows/desktop-build.yml) three times, with no Actions cache at all, +# so the repository's 10 GB quota is untouched: +# +# 1. cold, with a 6G bound that cannot be the limit: `du` and the ccache stats +# give the true working set of one configuration. +# 2. again from a deleted build directory with the same image, sources and +# flags: anything below ~95% hits means the hash inputs differ, not the size. +# 3. again after `ccache --recompress 1`, which is the candidate fix if one +# configuration turns out to be larger than a per-leg bound: it measures what +# the same configuration costs when the objects are compressed. +# +# Two runs of this workflow also compare the image id and the compiler's mtime, +# which is what ccache hashes by default (compiler_check = mtime). +name: ccache probe + +on: + push: + branches: + - ci/ccache-probe + pull_request: + branches: + - master + +concurrency: + group: ${{ github.workflow }} @ ${{ github.ref_name }} + cancel-in-progress: true + +jobs: + probe: + name: Arch probe + runs-on: ubuntu-latest + timeout-minutes: 150 + env: + NAME: Arch + CACHE: ${{ github.workspace }}/.cache/Arch + # Far above any candidate working set: a cache that evicts cannot be measured. + CCACHE_BOUND: 6G + # The leg's own invocation, so the probe cannot measure a configuration the + # workflow does not build. + LEG_ARGS: --server --debug --test --cmake-generator Ninja + + steps: + - name: "Checkout" + uses: actions/checkout@v7 + + - name: "Build image and record the compiler's identity" + shell: bash + run: | + source .ci/docker.sh --build + echo "=== disk available to the build" + df -h / | sed 's/^/ /' + echo "=== image" + docker image inspect --format 'image id: {{.Id}}' "$IMAGE_NAME" + docker image inspect --format 'created: {{.Created}}' "$IMAGE_NAME" + echo "=== compiler as ccache sees it (compiler_check = mtime hashes mtime and size)" + docker run --rm "$IMAGE_NAME" bash -c 'for c in c++ cc gcc g++; do p="$(command -v $c)"; [ -n "$p" ] && stat -c "%n size=%s mtime=%Y" "$p"; done; c++ --version | head -1' + + - name: "Build 1 cold, 6G bound" + shell: bash + run: | + source .ci/docker.sh + RUN $LEG_ARGS --ccache "$CCACHE_BOUND" + + # du cannot see a size the cache was not allowed to keep, which is why the + # bound is 6G here. The stats travel inside the cache directory, so the + # numbers below are this run's, not a restored history. + - name: "Working set of one configuration, uncompressed" + shell: bash + run: | + source .ci/docker.sh + echo "=== ccache dir on the host" + du -sh "$CCACHE_DIR" + find "$CCACHE_DIR" -type f -name '*.result' | wc -l | sed 's/^/result files: /' + find "$CCACHE_DIR" -type f -name '*.manifest' | wc -l | sed 's/^/manifest files: /' + echo "=== config in effect" + docker run --rm --mount "type=bind,source=$CCACHE_DIR,target=/.ccache" \ + --env CCACHE_DIR=/.ccache "$IMAGE_NAME" ccache --show-config | grep -E 'compiler_check|hash_dir|compression|direct_mode|depend_mode' + + - name: "Build 2, same image, same sources, deleted build directory" + shell: bash + run: | + rm -rf build + source .ci/docker.sh + RUN $LEG_ARGS --ccache "$CCACHE_BOUND" + + # A compressed cache is the only way one configuration can fit a per-leg + # bound, so measure it on the cache build 2 just used. + - name: "Working set after recompressing to level 1" + shell: bash + run: | + source .ci/docker.sh + docker run --rm --mount "type=bind,source=$CCACHE_DIR,target=/.ccache" \ + --env CCACHE_DIR=/.ccache "$IMAGE_NAME" ccache --recompress 1 + du -sh "$CCACHE_DIR" + + - name: "Build 3, compressed cache, deleted build directory" + shell: bash + run: | + rm -rf build + source .ci/docker.sh + # Writes stay compressed, so this is the configuration a fix would ship. + export RUN_ARGS="$RUN_ARGS -e CCACHE_COMPRESS=1" + RUN $LEG_ARGS --ccache "$CCACHE_BOUND" + echo "=== cache after build 3" + du -sh "$CCACHE_DIR"