The return of the frame pointers

Performance tradeoffs of frame pointers

  • Main costs discussed: one fewer general-purpose register (serious on 32-bit x86, minor on x86_64 and other ISAs) and extra prologue/epilogue instructions that can bloat small, frequently called functions and pressure I‑cache.
  • Reported slowdowns vary widely: some see ~1–3% in real apps; a Phoronix test reported ~14% geometric mean penalty; kernel measurements cited 5–10% for some workloads; others claim “under 1%” distro‑wide with modern toolchains.
  • Disagreement over benchmark quality: some call DB benchmarks “microbenchmarks,” others argue they are realistic end‑to‑end tests. Consensus that impact is workload‑dependent and not universally characterized.
  • 32‑bit x86 is still considered particularly register‑starved; for many other architectures the lost register is seen as negligible.

Profiling, debugging, and why FPs matter

  • Many consider reliable, low‑overhead stack traces essential for CPU, memory, and especially off‑CPU profiling and execution tracing.
  • Without frame pointers, kernel perf must copy large stack chunks and unwind later, causing CPU, memory, and disk overhead and requiring debug packages.
  • Developers argue that “just recompile with FPs” is unrealistic when whole systems and third‑party libraries are involved, and makes asking end‑users for traces impractical.
  • Some note that improved profiling has yielded >10–20% speedups in real software, easily outweighing a small global FP overhead.

DWARF and alternative unwinding approaches

  • DWARF/.eh_frame unwinding is described as flexible but complex and slow for high‑rate sampling or off‑CPU profiling. perf’s current DWARF path is especially costly due to stack copying.
  • Others claim DWARF can be made fast by preloading and caching unwind info, and point to eBPF‑based profilers that convert tables to efficient in‑kernel formats.
  • JVM/JIT and other high‑level runtimes remain hard: custom unwinding, mixed native/managed stacks, and heavy JIT activity make DWARF‑only solutions fragile or too expensive at scale.

Defaults, distro policies, and who pays the cost

  • Debate centers on what should be the default for kernels, libc, and distro builds.
  • One side: enable FPs globally so system‑wide profiling “just works,” allowing continuous optimization and post‑hoc analysis of production releases.
  • Other side: most users never profile; a few percent loss across millions of devices and servers is non‑trivial in energy and cost; performance‑sensitive code can enable FPs only in dev builds.
  • Suggestions include per‑function attributes (with caveats), separate “dev” vs “end‑user” libc builds, and using source‑based or Nix/Guix systems to toggle policy system‑wide.

Security and architecture ideas

  • Separate or shadow stacks for return addresses are discussed as a cleaner, more secure design, but current hardware and ABI compatibility limit practical deployment.
  • Some propose banning alloca() rather than paying FP cost; others push back that FPs are at least deployable now, while more radical changes are not.