Kache caches single-source nvcc -c and nvcc -dc object compilations. If it cannot prove that an invocation is safe to cache, it runs the real compiler unchanged.
Setup
For CMake:
export CUDACXX="kache nvcc"
# or, non-invasively, as a launcher:
cmake -DCMAKE_CUDA_COMPILER_LAUNCHER=kache ...
For Make and build scripts that honor NVCC:
export NVCC="kache nvcc"
Kache recognizes the nvcc command with or without a path prefix, including nvcc.exe.
What is cached
Kache caches the single-source -c device compile as a whole:
nvcc -c src/kernel.cu -o build/kernel.o -arch=sm_80
For separate compilation, -dc and -c -rdc=true also cache the object. Device linking and final linking run through nvcc uncached:
nvcc -dc src/kernel.cu -o build/kernel.o
nvcc -dlink build/kernel.o build/helper.o -o build/device-link.o
nvcc is a driver: one -c invocation fans out to the device frontend, one compiler pair per GPU architecture, the fatbinary bundler, and the host C++ compiler. Kache caches the whole invocation as one entry. On a miss it runs nvcc end to end and stores the final object; on a hit it restores the object (and the -MF dep-info sidecar, when requested) and skips the toolchain.
Why the key is sound
nvcc -E is not a safe cache key: it always defines __CUDA_ARCH__, so host-only code guarded by #ifndef __CUDA_ARCH__ is invisible in its output. Kache does not hash preprocessed output. The key is built from:
nvcc's version and the host compiler's version (a host upgrade changes the object, probed vianvcc --dryrun);- the modeled flags verbatim —
-dc/-rdcmode, GPU-arch/-gencode/-code,-std,-O, defines, and the-Xcompiler/-Xptxasforwarding flags; - the dependency closure: the raw content of the source plus every header
nvcc -Mreports. Hashing raw contents (not preprocessed output) captures host-only code, so any source or header edit invalidates the entry. - the driver environment:
NVCC_PREPEND_FLAGS/NVCC_APPEND_FLAGSare folded in verbatim, so the same checkout with different driver flags keys apart.
Remote sharing
nvcc objects are portable across machines and checkouts: Kache injects host -ffile-prefix-map rules (via -Xcompiler) that strip clone-local roots, and pins SOURCE_DATE_EPOCH so __DATE__ / __TIME__ are stable. The same source built at two different absolute paths produces the same key. Disable path normalization with KACHE_NVCC_PATH_NORMALIZE=0 (keys become machine-local).
What passes through
Anything Kache does not model runs the real nvcc uncached, including:
- link and device-link (
-dlink) modes,--lib, multi-source compiles; - standalone
-ptx/-cubin/-fatbin/--optix-iremission; - device debug (
-G); - machine-resolved values (
-march=native,-arch=native, …) — one key must never cover different objects, so these refuse even when allow-listed; - dependency generation without an explicit output (
-M,-MM) or without-MF(-MD,-MMDalone) — combine-MD/-MMDwith-MF <file>instead; - compiles without
-o; -keep/--save-temps, profiling (--time), response files (@file);- any flag outside the built-in table (see below).
To opt an unmodeled-but-safe flag into caching, add it to the extra allow-list ([cc] extra_allowlist_flags or KACHE_CC_EXTRA_ALLOWLIST_FLAGS), which the nvcc adapter also honors. List explicit flag values. Flags that hide preprocessor inputs (-Xcompiler -I…, -Xcompiler -D…) stay refused: they would escape dependency tracking.
NVCC_PREPEND_FLAGS / NVCC_APPEND_FLAGS codegen tuning (-O2, -gencode …) is cached normally. Values that would change what is built or hide inputs (-G, -I…, -march=native) pass through uncached instead.