python / python/cpython

Frame pointer (and any per-function features) builds of tail calling interpreter slower than expected

未關閉
#154,124 1 則留言 4 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

interpreter-core performance type-bug
主要語言
Python
星號
77.2k
分支
35.9k
PR 合併指標
PR 指標待擷取

描述

Bug report

Bug description:

When building with the normal interpreter (computed goto), the impact of frame pointers should only be around 2% geomean max.

However, it seems the impact on the tail calling interpreter is higher. This is likely because each bytecode handler on the tail calling interpreter is now a function, which means they each have a frame pointer prologue and epilogue.

There is a fix on Clang 21 and higher (though I don't know if GCC 16 has it): omit the frame pointer, but reserve it so it doesn't get clobbered only for the bytecode handlers, leave the rest building with frame pointers. The flags are "-fomit-frame-pointer -momit-leaf-frame-pointer -mreserve-frame-pointer-reg". The key observation is that since we tail call, we only need the first entry into a tail calling handler to have the frame pointer prologue (ie LABEL(start_frame) needs the prologue), but all other tail calling function pointers do not. This practically eliminates the entire prologue/epilogue overhead for the interpreter, while keeping register allocation good and not clobbering frame pointers. Unfortunately, clang does seem not have custom per-function attributes for this option, so we have to move the non-starter bytecode handlers to their own compilation unit (ie, move them to their own C file).

This seems to affect anything with per-function overhead as well, such as CET/BTI changes. We can fix those incrementally, as the approacha re the same.

Here are some geometric mean results on Sam's fastmark (pyperformance subset) from my laptop (i7-12700H x86-64) with clang 22:

  • Baseline (FP on, tail calling interpreter): 0% slowdown
  • FP off, tail calling interpreter: 2.7% speedup
  • FP on, tail calling interpreter, reserve register patch (meowl + garbage + oiia): 2.5% speedup

TLDR: WIth this patch, the tail calling interpreter's overhead for frame pointer enabling drops from 2.7% to just 0.2%!!!! Frame pointers are practically free on the tail calling interpreter!!! Meanwhile, computed goto interpreter has a 1.5% hit (the i7-12700h machine is mine from PEP 831)

@pablogsal @markshannon @stratakis

CPython versions tested on:

CPython main branch

Operating systems tested on:

No response

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

先檢查 LABEL(start_frame) 進入點周圍的尾端呼叫直譯器,以及其位元組碼處理常式的建置設定。使用 Clang 執行已連結的 fastmark 基準測試,並比較回報的框架指標額外負擔;完成的條件是尾端呼叫直譯器保留框架指標,且沒有此處所述的處理常式前置/後置程序減速。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
c, python
領域
build-system, compilers, performance
Issue 類型
缺陷
難度
5/5
預估耗時
一週以上
活躍度
冷清
描述清晰度
基本清楚
新手友好度
38/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。