Suppress debug info on the AppendTypesSlow trampoline variable - #218
Conversation
With -g in the interpreter args, each __Cppyy_AppendTypesSlow<...> declare emits the full debug-info DIE tree of every template argument (all member declarations included). Since 'Resolve templated type names as a whole in GetScope' routes whole type expressions through the trampoline, that cost is paid per failed templated-name lookup, is uncacheable (fresh variable each call), and grows with the context's uniqued-DISubprogram set -- an 8.5x wall-clock regression on debug-info-enabled JIT workloads with large headers. The trampoline exists only to be reflected on, so mark the variable nodebug. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
|
@vgvassilev, @guitargeek, this is another one we started hitting after a rebase that included the above ref'd stuff, but note that it was an unexpected interaction with -g debug info. easy surgical fix. |
| // nodebug: with -g the variable's debug info would carry the full DIE | ||
| // tree of every template argument (all member declarations included) -- | ||
| // a large, uncacheable per-lookup cost on heavyweight types. | ||
| if (!Cpp::Declare(("__Cppyy_AppendTypesSlow<" + candidate + "> __attribute__((nodebug)) " + var + ";\n").c_str(), /*silent=*/true)) { |
There was a problem hiding this comment.
That causes the wrappers to be bigger and thus memory hoarding of already a heavy operation. Is that a huge problem for the debug setup?
There was a problem hiding this comment.
It's a large, measured problem: with -g, each probe variable's debug info pulls the complete DIE tree of its template argument (all member declarations, transitively) into the PTU — on our production replay (large-header trading workload, heavy AppendTypesSlow traffic) that was a 10x wall-clock regression, 1378s → 137s after suppressing, plus the DWARF itself hoarded per lookup. Against that, the attribute adds 26 bytes to a per-probe source buffer that already exists — paid on non-debug setups too, but it's noise next to the buffer itself. If even that bothers you I can gate the attribute text on whether the interpreter session has debug info enabled, at the cost of a slightly less uniform probe string.
There was a problem hiding this comment.
In that case, we can cheat on the JitCall side where just after we parse the prototype, we can check if we passed -g to the interpreter, and then call setAttr(createAttr(nodebug)) on wrapper declaration. That would save bytes I believe.
There was a problem hiding this comment.
No wait, on a second thought that might be suboptimal. Let's move forward with the current approach -- it would give us one more incentive to get rid of this string manipulations..
What
Marks the
__Cppyy_AppendTypesSlowtrampoline variable__attribute__((nodebug)).Why
Since #213 ("Resolve templated type names as a whole in GetScope"),
GetScope'sfallback for templated names routes the whole type expression through a
__Cppyy_AppendTypesSlow<expr> var;declaration. When the interpreter runs withdebug info enabled (
-ginCPPINTEROP_EXTRA_INTERPRETER_ARGS), each suchdeclaration emits the complete debug-info DIE tree of every template argument —
for a heavyweight class that is every member declaration, each becoming a uniqued
DISubprogramprobed against the LLVMContext's ever-growing uniquing set. Thevariable name is fresh on every call, so nothing is cached and the cost is paid
per failed templated-name lookup.
On a large real-world workload (market-data replay JIT-compiling large headers,
-ggdb3), this regressed wall clock ~8.5x (162s → 1378s); a profile showed >90%of CPU in
DenseMap<DISubprogram*, ...>probe/insert inside the JIT. The previoussplit-and-
InstantiateTemplatepath was Sema-only and never paid codegen or debuginfo. With the trampoline variable marked
nodebug, the workload returns to 137s —faster than before the GetScope rework — while user code keeps full debug info and
the whole-expression resolution semantics are unchanged.
The trampoline exists only to be reflected on via
GetVariableType, so its debuginfo carries no value.
test_templates.py/test_stltypes.py/test_datatypes.py/test_regression.pypass with the change (they exercise
AppendTypesSlowthrough templated lookups).🤖 Done with the help of Claude Code (Fable 5, human in the loop)