mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
IC: memoize the FileId -> FileIndex resolution, halving a cold --ic:on build
`oldLineInfo` resolved every decoded token's line info by copying a path out of the buffer's filename pool and hashing it through `msgs.fileInfoIdx`. Every time — no cache, on a step that runs once per node the decoder loads and once per statement the generator emits. On a 68-module target that is 259k calls at 5.2us. Memoized per pool it is 34ms, and the cold build goes from 21.5s to 10.1s. `revTab` could not serve this: it is keyed by a `FileId` in the WRITER's global `pool.files`, while a decoded token's `FileId` indexes the buffer's OWN filename pool — a fresh one per `bif`-loaded module. So the cache is keyed by (pool, FileId), as a `seq` because ids are small and dense within one pool. `readPool` holds a REFERENCE rather than a raw pointer, and that is the whole safety argument: it keeps the pool alive, so a freed pool cannot be replaced by a new one at the same address and answer from the wrong file table. Verified: `tests/ic` 39/39; the cursor-vs-`PNode` differential grinder clean over ~150k graded nodes on `tools/icgrind` — and confirmed live by sabotaging the pool-identity check, which makes it fire on `info` immediately; all 67 `.c` files an `--ic:on` build generates byte-identical to before; all 216 a non-IC build generates likewise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE
This commit is contained in:
@@ -130,6 +130,16 @@ type
|
||||
revTab: Table[FileId, FileIndex] # reverse mapping for oldLineInfo
|
||||
man: LineInfoManager
|
||||
config: ConfigRef
|
||||
# The READ direction's cache, which `revTab` cannot serve: `revTab` is keyed
|
||||
# by a `FileId` in the WRITER's global `pool.files`, while a decoded token's
|
||||
# `FileId` indexes the buffer's OWN filename pool. So the cache has to be
|
||||
# keyed by (pool, FileId), and it is a `seq` because `FileId`s are small and
|
||||
# dense within one pool. `readPool` holds a REFERENCE rather than a raw
|
||||
# pointer on purpose: it keeps the pool alive, so a freed pool cannot be
|
||||
# replaced by a new one at the same address and silently answer from the
|
||||
# wrong file table.
|
||||
readPool: Pool
|
||||
readTab: seq[FileIndex]
|
||||
|
||||
proc newLineInfoWriter(config: ConfigRef): LineInfoWriter =
|
||||
# `fileK` starts invalid so the one-entry cache never collides with a real
|
||||
@@ -178,12 +188,26 @@ proc oldLineInfo(w: var LineInfoWriter; info: NifLineInfo; p: Pool): TLineInfo =
|
||||
## it to a `TLineInfo`. `info.file` indexes the loaded buffer's OWN filename
|
||||
## pool `p` (= `cursorPool(n)`), which is the shared `icPool` for a text-parsed
|
||||
## module but a fresh per-file pool for a `bif`-loaded one.
|
||||
##
|
||||
## Memoized per pool. Resolving a name costs a string copy out of the pool
|
||||
## plus a hash of a full path, and the generator asks for a node's line info
|
||||
## on essentially every statement it emits — 259k times on a 68-module build,
|
||||
## which was 1.36s of the 1.88s the cursor-driven generator spent.
|
||||
if info.file == NoFile:
|
||||
result = unknownLineInfo
|
||||
else:
|
||||
let filePath = p.filenames[info.file]
|
||||
let fileIdx = msgs.fileInfoIdx(w.config, AbsoluteFile filePath)
|
||||
result = TLineInfo(line: info.line.uint16, col: info.col.int16, fileIndex: fileIdx)
|
||||
if p != w.readPool:
|
||||
w.readPool = p
|
||||
w.readTab = @[]
|
||||
let id = int(uint32(info.file))
|
||||
if id >= w.readTab.len:
|
||||
let oldLen = w.readTab.len
|
||||
w.readTab.setLen(id + 1)
|
||||
for i in oldLen ..< w.readTab.len: w.readTab[i] = astli.InvalidFileIdx
|
||||
if w.readTab[id] == astli.InvalidFileIdx:
|
||||
w.readTab[id] = msgs.fileInfoIdx(w.config, AbsoluteFile p.filenames[info.file])
|
||||
result = TLineInfo(line: info.line.uint16, col: info.col.int16,
|
||||
fileIndex: w.readTab[id])
|
||||
|
||||
|
||||
# ------------- Writer ---------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user