python / python/cpython

Optimize collection literals with leading null unpack idiom in bytecode, allows bare `BUILD_SET 0`

Abierto
#150,737 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

interpreter-core type-feature
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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 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 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:

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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza revisando empty_unpack_benchmark.py y los ejemplos de implementación enlazados en la propuesta; después, inspecciona el PR enlazado gh-150812 y la discusión relacionada en Discourse. Se considera terminado cuando los casos propuestos de literales de colecciones evitan actualizaciones vacías redundantes, al tiempo que preservan el comportamiento documentado y las mejoras de los benchmarks.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
compilers
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
25/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.