The frontend was the one phase with no timers inside it — 180 processes
and 9.9s of Atlas's cold build, entirely opaque. `tStage` around
`commandCheck` and `tWriteNif` around `writeNifModule` close that, and
with the loading slots ast2nif already had, a `nim m` process is now
fully accounted:
startup (exec+runtime+config) 0.17 s 2%
loading imported `.s.bif` 4.57 s 46%
writing this module's `.s.bif` 1.84 s 18%
sem + parse 3.41 s 34%
Two thirds of the frontend is artifact I/O, not compilation. That is the
per-module model's structural cost: 180 processes each rebuild their
imports' interfaces from nothing.
One part of it is not structural at all. `interfHidden` accounts for
1.05s of the loading: 1.70M hidden-symbol stubs against 0.29M exported
ones, built by every `nim m` for every module it imports. The table is
reached ONLY through `modulegraphs.interfSelect` with `optImportHidden`,
and that flag is set in exactly one place — an `import x {.all.}`. The
backend already skips building it for this reason; the frontend cannot
skip it unconditionally, but it does not have to build it eagerly either.
Measured with a probe rather than estimated: skipping it takes
`InterfTables` 1161ms -> 80ms, the frontend 9.98s -> 8.93s, and the whole
Atlas build 22.19s -> 20.47s wall.
Not taking it in this commit. The correct form is lazy population on
first `interfSelect(true)` — deciding up front cannot work, because a
macro-generated `{.all.}` import is invisible syntactically and guessing
wrong loses symbols silently. `loaderCtx` is the hook (the module index
survives in the DecodeContext), but this is symbol visibility, and it
deserves its own pass rather than the tail of a long one.
`bnode.nim` carries the numbers and the design note. All behind
`-d:icBNodeProf`. ic 41/41, `koch boot -d:release` equal executables.