python / python/cpython

Free-threading: add stop-the-world duration and contention counts to pystats

未关闭
#154,969 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

interpreter-core topic-free-threading type-feature
主要语言
Python
星标
77.2k
派生
35.9k
PR 合并指标
PR 指标待抓取

描述

Feature or enhancement

Proposal:

gh-131253 added the first free-threading counters to pystats (mutex_sleeps, world_stops, qsbr_polls) and was recently closed as complete. After spending some time measuring the free-threaded build with them, I think two small additions would make them considerably more useful. The numbers below are from current main on a 24-core Linux machine, using Tools/ftscalingbench plus uprobe tracing on _PyMutex_LockTimed to cross-check what the counters report.

Stop-the-world duration. world_stops counts pauses but says nothing about their cost. Two ftscalingbench runs I traced both did exactly 277 stops, but total pause time was 0.45 ms in one (pymethod) and 1.53 ms in the other (staticmethod_call) - a 3.4x difference the counter cannot distinguish. The original issue already floated "possibly total duration"; it just never got implemented. Two clock reads in stop_the_world() would cover it. A max would be nice as well, since the distribution has a long tail - most pauses I saw were 0.5-2 us, with outliers around 16 us.

Contention events, not just sleeps. mutex_sleeps counts a thread actually parking, but _PyMutex_LockTimed spins first, so any contention that resolves by spinning is invisible. On a deliberately contended workload (8 threads appending to one shared list) I measured 196 entries into _PyMutex_LockTimed against a mutex_sleeps of 14, so the one contention counter we have misses roughly 93% of contention. An entry counter in _PyMutex_LockTimed would close that gap, and since it sits on the slow path already the cost is one relaxed increment.

Two more observations from the same experiments, recorded here because they shaped what I'm not proposing:

The scaling losses in ftscalingbench are not lock waits. Three workloads losing 35-50% of ideal scaling at 8 threads each showed only about 0.5 ms of genuine lock waiting, nearly all of it the import machinery RLock during thread startup plus interpreter teardown - none of it in the benchmark loops themselves. So I don't think more lock counters beyond the above are worth adding; whatever those benchmarks are losing, it happens below the mutex layer (my guess is refcounting cache traffic), and hardware-level tools are the right instrument there.

Benchmarking the free-threaded build also has a warmup trap worth knowing about: bytecode is thread-local, so specialization warmup on one thread does nothing for workers spawned later. I initially measured cold-bytecode churn without realizing it - FOR_ITER re-specializing hundreds of times per run, disappearing entirely when I reversed the run order. Anything that benchmarks with freshly spawned threads is exposed to this. Happy to write it up for the free-threading HOWTO if there's interest.

I can prepare PRs for both counters. This deliberately doesn't touch ring-buffer stats support for the FT build, which came up at the end of gh-131253.

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs
  • gh-155069

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从 stop_the_world() 和 _PyMutex_LockTimed 附近的 pystats free-threading 实现开始,然后使用 Tools/ftscalingbench 重现所述测量结果。完成的标准是 pystats 报告 stop-the-world 的总持续时间和 mutex 竞争进入次数;请先查看关联的 PR gh-155069,因为相关工作已经在进行中。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
performance
Issue 类型
功能
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
基本清楚
新手友好度
30/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。