gesslerpd
(Peter Gessler)
June 2, 2026, 1:07am
241
Alright, submitted issue for this, there are 2 self-contained implementations linked (one in initial codegen and one in optimizer).
opened 10:00PM - 01 Jun 26 UTC
type-feature
# Feature or enhancement
### Proposal:
Performance was discussed at various ti… mes within [PEP 802](https://discuss.python.org/t/pep-802-display-syntax-for-the-empty-set/101676) topic, submitting this proposal as the [PEP is unlikely to be accepted](https://discuss.python.org/t/pep-802-display-syntax-for-the-empty-set/101676/237).
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 set `set()`)
- Remove performance penalty of incremental construction between 0-to-1/1-to-0 elements for `set` and `tuple` literals without special case considerations (e.g. `x = *(),` pre-seeded tuple literal without trailing comma pitfall)
- Keep implementation in 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 optimization step: https://github.com/python/cpython/commit/f857d88ef3aac90deadf438625211040e73df832#diff-3cbf15668c31488528b7ab0f903c674a0ecf550f4f53c1be6cf8cd965246c2a0
## Micro-benchmark
Run on windows x64bit release build: [empty_unpack_benchmark.py](https://github.com/user-attachments/files/28482608/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 |
```mermaid
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
1 Like
edvilme
(Eduardo Villalpando Mello)
June 11, 2026, 11:58pm
242
{.}?
It is kind of similar to the empty set symbol (if you squint your eyes), and it is similar to the comma notation for tuples without suggesting there are values already.
Don’t know if this was suggested/rejected but wanted to weigh in in case it helps