Skip recompiling dependents when a module interface is unchanged (gfortran)#1289
Skip recompiling dependents when a module interface is unchanged (gfortran)#1289krystophny wants to merge 1 commit into
Conversation
When a dependency is recompiled but its gfortran .mod interface is byte-identical, dependents need not recompile. build_target caches each object's module-interface fingerprint and the fingerprints of its dependencies, then prunes a cascade victim whose source and dependency interfaces are all unchanged. Limited to gfortran, whose .mod files are a stable interface signal; other compilers keep the conservative cascade.
4d60a2e to
1e0b2b6
Compare
Benchmark: incremental rebuild on real projectsMeasured the edit-rebuild loop on two Fortran projects: fpm itself and fortran-lang/stdlib ( Compiler gfortran 12, default (debug) profile, single run, so wall-clock carries some noise; the recompile counts are exact. fpm (208 objects in the full build)
stdlib (182 modules)
Reading the numbersThe win is the third row: a body-only change to a widely-used module. The No-op and leaf edits are unchanged, as expected. When the interface really changes (last row), dependents rebuild as before, with no measurable overhead; the branch even trims the cascade slightly (374 vs 439, 55 vs 61) because second-order dependents whose own interface stayed stable are pruned too. Limited to gfortran, whose |
What
fpm recompiles every object that
uses a module whenever that module's sourcechanges, even when only the implementation changed (a procedure body or a
comment). For a low-level module that rebuilds the whole dependent tree. This
skips dependents whose module interfaces are unchanged.
Impact
In fortplot (290 sources), a one-line edit to a core module recompiled 214 of 289
objects before this change; now it recompiles 1. Editing a procedure body no
longer cascades; editing its interface still does.
This matches CMake on gfortran: the same rebuild set as both the Ninja and the
Unix Makefiles generators on the same fixture. fpm previously rebuilt the full
transitive closure on any change, like a naive
b.o: a.oMakefile.How
.modbyte-identical on an implementation-only editand rewrites it on an interface change, so the
.modis a reliable interfacesignal.
.modfiles it providesand of its dependencies'
.modhashes, next to the existing.digest.build_target, skip an object whose source and whose dependencies' moduleinterfaces are all unchanged, even if a dependency was rebuilt. The scheduler is
untouched: cascade victims stay queued and are pruned at build time, the shape
of ninja's
restat.and nvfortran can follow.
Addresses #967.
Verification
Two-module package; edit only a procedure body in the used module, then rebuild:
Editing the interface instead correctly recompiles
m_impl,m_user,main.New unit test covers prune / interface-changed / source-changed / missing-record:
Full unit suite: 187 passed, 0 failed.
Merge order
This PR is independent of most PRs in the parallel-build series but has a
textual overlap with #1298 (mkdir fork gap): both edit the
! criticalblock at
build_targetinfpm_backend.F90. This PR keeps the unnamedcritical; #1298 renames it to
critical(run_command). Whichever mergessecond needs a rebase on the other.
Independent PRs (any order):
Stacked chain (merge in order):
Fork-safety fix:
After the above: