arm64: clear pooled assembler nodes when a compile returns them - #617
arm64: clear pooled assembler nodes when a compile returns them#617cyclistmass wants to merge 1 commit into
Conversation
The instruction and label pools are permanent roots, and return-dll-nodes parks nodes with their slots intact. A parked instruction keeps SOURCE and PARSED-OPERANDS, and a parked label keeps REFS and, in the vinsn path, a vinsn-label NAME. Until reuse clears them, the pools hold the finished compile's whole IR graph, which survives the ephemeral generations and tenures. About 5.4 KB tenures per compile (measured at d61da8d). One ANSI suite run makes 29 full GCs on arm64 against 5 for x86-64 CCL 1.12.2 under the same GC configuration. A loop of 20,000 trivial COMPILE calls adds 3 full GCs on arm64 against 0 on x86-64. x86-64 pools only frags and lap labels, and those carry no operand graph. Clear the reference-carrying slots in an unwind that runs before each section returns to the pool. The walk handles both node types, because labels splice into the same section as instructions and return to *instruction-freelist*. The numeric slots keep their values, and reuse resets them. A per-pool clear hook in return-dll-nodes is the more general fix, but this change stays inside compiler/ARM64/ so the other ports carry no risk. Signed-off-by: Mauro DiBenedetto <maurodibenedetto@gmail.com>
|
I wonder if freelisting the instructions and labels is even worth it. It could be better to cons away, and the egc deal with it. How hard would it be for you to run that test, i.e., have make-instruction always return a freshly-allocated instruction instance? |
|
I ran it, and it points your way: with the pool gone the compiler is faster than First, the label half generalizes. I told you above that nothing in the tree While confirming that: arm64's Second, the instruction pool measures worse than it looks. Instrumenting The A/B. One image, one process, arms swapped by redefinition, 8 pairs with Worth being explicit about the baseline: arm A is this branch, so it already
4.4% faster, 5.9% more consing, and less GC time — which is your point about A generated 120-form lambda, 12 compiles per rep, same protocol: 999 → 987 ms, Both arms emit the same code. I checked that against a noise floor rather than Two things bias the measurement against the winning arm, so treat 4.4% as a So I think this PR is the wrong fix. It makes pooling cheaper; not pooling is I wrote that patch rather than just offering it. It is on a separate branch, Both freelist bindings go, both pool-reading wrappers go, and the two struct Built and run on linuxarm64 at that base: ANSI 21679 tests, 0 failures; Throw it away if you would rather keep the pool — it costs you nothing to |
The instruction and label pools live for the whole compile, and
return-dll-nodesparks nodes with their slots intact. A parked instructionkeeps
SOURCE,TEMPLATEandPARSED-OPERANDS. A parked label keepsREFS,and on the vinsn path a
vinsn-labelalso keepsNAME. The pools therefore hold the finishedcompile's whole IR graph until reuse clears those slots. That graph survives
the ephemeral generations and tenures, at about 5.4 KB per compile.
Cost over one ANSI suite run:
Total collections are 4913 in both halves. The same collections happen, and
fewer of them promote. A 20,000-call
COMPILEloop on the same image adds 3full and 15 generation-2 collections before the patch, and 1 and 2 after.
I re-measured at your
dd80005e, with this patch as the only change:stage-11 CPU 49.73 s to 47.84 s.
The patch clears the reference-carrying slots in an unwind that runs before
each section returns to the pool. The walk handles both node types, because
labels splice into the same section as instructions and return to
*instruction-freelist*. Numeric slots keep their values, and reuse resetsthem.
The patch adds
reset-instruction-elements, and the two arm64 call sites thatown a pooled section wrap their body so it runs before the section returns.
The change stays inside
compiler/ARM64/. A clear hook inreturn-dll-nodeswould cover every backend, and I have measured only arm64, so I have left the
others alone. Two things I did check. Nothing in the tree ever returns a node to
*x86-lap-label-freelist*-- there is a defvar and an allocation site and nothird use -- so an x86 lap label is never parked in the first place. x86-64 does
pool frags through
*frag-freelist*, andmake-fragclears them on reuserather than on return, which is the same shape as this bug; whether it retains
anything is a separate measurement I have not made.
Verified at this base. Built and run on linuxarm64 at
ec578745with allten patches applied: ANSI 21679 tests, 0 failures.
tests/ccl.lsp243 tests, 0failures. Image
281a49e5, kernel67bb66b4.