Optimize collection literals with leading null unpack idiom in bytecode, allows bare `BUILD_SET 0`
還沒有人認領這個 Issue。
- 主要語言
- Python
- 星號
- 77.2k
- 分支
- 36k
- PR 合併指標
- PR 指標待擷取
描述
Feature or enhancement
Proposal:
Performance was discussed at various times within PEP 802 topic, submitting this proposal as the PEP is unlikely to be accepted. This is intended as an advanced escape-hatch not overlapping with the main reasons for PEP 802.
Goal:
- Remove performance penalty of
ast.unparse(ast.Set(elts=[]))({*()}can be used in advanced cases and will not change recommended way to write empty setset()) - Remove performance penalty of incremental construction between 0-to-1/1-to-0 elements for
setandtupleliterals without special case considerations (e.g.x = *(),pre-seeded tuple literal without trailing comma pitfall) - Keep implementation simple/self-contained in bytecode generation, avoiding any AST changes to language
- Near-zero performance impact to bytecode generation (improves subsequent bytecode optimizations of target case)
Implementation examples:
- During first AST -> bytecode pass: https://github.com/python/cpython/commit/67742d92c411a93e7692f761ffeb3af75ba1ff87#diff-6d58b0ddc066ad12ebc378b62c4189335bd57c83aece11072c76fd86a78a1a4a
- During bytecode-only optimization step: https://github.com/python/cpython/commit/f857d88ef3aac90deadf438625211040e73df832#diff-3cbf15668c31488528b7ab0f903c674a0ecf550f4f53c1be6cf8cd965246c2a0
Micro-benchmark
Run on windows x64bit release build: empty_unpack_benchmark.py
Compile time
Targeted cases:
| Benchmark | Main | Flowgraph only | Codegen leading only |
|---|---|---|---|
[*()] |
1.450 ms / 1.000x | 1.393 ms / 0.961x | 1.378 ms / 0.950x |
{*()} |
1.530 ms / 1.000x | 1.488 ms / 0.973x | 1.467 ms / 0.959x |
(*(),) |
1.916 ms / 1.000x | 1.842 ms / 0.962x | 1.832 ms / 0.956x |
x = *(), |
1.139 ms / 1.000x | 1.088 ms / 0.955x | 1.055 ms / 0.927x |
xychart-beta
title "Compile targeted mean ratio vs main"
x-axis [Main, Flowgraph, Leading]
y-axis "Ratio" 0 --> 1.01
bar [1.000, 0.963, 0.948]
Control near-miss case comparisons moved less than +/-1% and are attributed to noise.
Runtime
The set() versus {*()} comparison is especially useful because it changes qualitatively across the two branches (~6.3% slower to ~29.5% faster):
| Branch | set() mean (ns) |
{*()} mean (ns) |
{*()} vs set() |
|---|---|---|---|
| Main | 54.14 | 57.53 | 1.063x |
| Flowgraph only | 53.49 | 38.01 | 0.711x |
| Codegen leading only | 55.44 | 39.00 | 0.704x |
Before this change, {*()} was paying for a redundant empty update and ended up slower than the constructor call. After the change, {*()} compiles down to a direct BUILD_SET 0; RETURN_VALUE path, while set() still has to load the global and perform a zero-argument call.
Has this already been discussed elsewhere?
I have already discussed this feature proposal on Discourse
Links to previous discussion of this feature:
https://discuss.python.org/t/pep-802-display-syntax-for-the-empty-set/101676/237
https://discuss.python.org/t/pep-802-display-syntax-for-the-empty-set/101676/216
https://discuss.python.org/t/pep-802-display-syntax-for-the-empty-set/101676/3
Linked PRs
- gh-150812
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
先查看 empty_unpack_benchmark.py 和提案中連結的實作範例,接著檢查連結的 PR gh-150812 以及相關的 Discourse 討論。當提出的集合字面值案例能避免多餘的空更新,同時保留文件記載的行為和基準測試改進時,即視為完成。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- compilers
- Issue 類型
- 功能
- 難度
- 5/5
- 預估耗時
- 一週以上
- 活躍度
- 停滯
- 描述清晰度
- 基本清楚
- 新手友好度
- 25/100