Skip to content

pass recursive lambda self param by reference#222

Merged
cameroncuster merged 4 commits into
devfrom
recursive-lambda-by-ref
Jul 6, 2026
Merged

pass recursive lambda self param by reference#222
cameroncuster merged 4 commits into
devfrom
recursive-lambda-by-ref

Conversation

@cameroncuster

Copy link
Copy Markdown
Member

Changes the self parameter of every self-passing recursive lambda from auto (by value) to auto&& (by reference) — 20 lambda sites across 18 files.

Why

auto dfs = [&](auto dfs, int u) { ... };   // before: closure copied into every frame
auto dfs = [&](auto&& dfs, int u) { ... }; // after: one closure, 8-byte ref per frame

A [&] closure is one word per captured entity. With the by-value form, every recursive call copies the whole closure into the new stack frame (and it must stay live across the recursive call, so it can't be elided). With auto&&, each frame holds only a reference. Call sites are unchanged.

Measured impact

Benchmark: recursive lambda capturing 4 locals (same shape as this library's DFS lambdas), measuring the address delta of a local between consecutive recursion depths. GCC 16, arm64 macOS:

auto dfs (self by value) auto&& dfs (self by ref)
frame size at -O2 136 B 40 B
frame size at -O0 112 B 64 B
stack at depth 10^7 ~1.3 GB ~380 MB

The closure copy is 32 B, but it forces additional spills/alignment on top, tripling the frame at -O2. Exact numbers are ABI/capture-count dependent (x86-64 judges will differ), but the direction and rough ratio are robust.

Practical consequences:

  • On a default 8 MB stack (running locally without ulimit -s), this is the difference between crashing at depth ~60k vs ~200k on a path graph.
  • On judges where stack = memory limit, a 10^6-node path graph costs ~130 MB of stack by value vs ~40 MB by ref.
  • Runtime speed is unchanged — the copies are trivial and inlined; this is purely a stack-footprint improvement.

Cost

+2 characters per lambda. No behavior change; all call sites (dfs(dfs, ...)) are identical.

If the library later moves to C++23 deducing this, the equivalent recommended form is this auto&& dfs.

Verification

  • clang-format --dry-run --Werror (dev.clang-format) passes on all 18 files
  • All affected Library Checker/Aizu tests compile clean with g++ -std=c++20 -O2 -Wall -Wextra (lca, edge_cd, centroid decomp, kth_path x4, shallowest, subtree isomorphism, scc x2, offline incremental scc, euler walk, two-edge cc, bcc x2, min-plus convolution)

@cameroncuster cameroncuster merged commit 29d8d92 into dev Jul 6, 2026
7 of 8 checks passed
@cameroncuster cameroncuster deleted the recursive-lambda-by-ref branch July 6, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants