`asyncio.print_call_graph()` output is exponential in the number of tasks
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 77.2k
- Fork
- 35.9k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
Bug report
Bug description:
The size of the asyncio.print_call_graph() output is proportional to the number of paths through the "awaited by" graph, not to the number of tasks:
import asyncio
import time
async def waits_for(*deps):
await asyncio.gather(*deps)
async def main(levels):
fut = asyncio.Future()
layer = [fut]
for _ in range(levels):
layer = [asyncio.create_task(waits_for(*layer)) for _ in range(2)]
await asyncio.sleep(0)
t0 = time.perf_counter()
graph = asyncio.format_call_graph(fut)
dt = time.perf_counter() - t0
print(f"{2 * levels:3d} tasks -> {graph.count('* Task'):9d} nodes,"
f"{len(graph) / 1e6:9.1f} MB,{dt:8.2f} s")
for levels in (2, 4, 6, 8, 10, 12, 14, 16, 18, 20):
asyncio.run(main(levels))
Actual output:
4 tasks -> 6 nodes, 0.0 MB, 0.00 s
8 tasks -> 30 nodes, 0.0 MB, 0.00 s
12 tasks -> 126 nodes, 0.0 MB, 0.00 s
16 tasks -> 510 nodes, 0.1 MB, 0.00 s
20 tasks -> 2046 nodes, 0.6 MB, 0.00 s
24 tasks -> 8190 nodes, 2.4 MB, 0.02 s
28 tasks -> 32766 nodes, 10.7 MB, 0.07 s
32 tasks -> 131070 nodes, 46.4 MB, 0.33 s
36 tasks -> 524286 nodes, 200.3 MB, 1.96 s
40 tasks -> 2097150 nodes, 859.8 MB, 9.90 s
Expected:
4 tasks -> 6 nodes, 0.0 MB, 0.00 s
8 tasks -> 14 nodes, 0.0 MB, 0.00 s
12 tasks -> 22 nodes, 0.0 MB, 0.00 s
16 tasks -> 30 nodes, 0.0 MB, 0.00 s
20 tasks -> 38 nodes, 0.0 MB, 0.00 s
24 tasks -> 46 nodes, 0.0 MB, 0.00 s
28 tasks -> 54 nodes, 0.0 MB, 0.00 s
32 tasks -> 62 nodes, 0.0 MB, 0.00 s
36 tasks -> 70 nodes, 0.0 MB, 0.00 s
40 tasks -> 78 nodes, 0.0 MB, 0.00 s
Besides that, the graph is impossible to read. The cause is that capture_call_graph() renders relations as a tree, but that relation is a DAG.
Proposed fix: expand each future's "awaited by" only once
Have a fix ready for that
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs
- gh-156861
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia dall’implementazione del grafo delle chiamate di asyncio, in particolare capture_call_graph(), ed esegui il riproduttore nell’issue per osservare l’output esponenziale. Confronta il numero effettivo di nodi con quello previsto, quindi verifica che il grafo rimanga limitato dal numero di task e che la fix già collegata copra il caso DAG.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- python
- Ambito
- backend
- Tipo di issue
- Bug
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Stato di attività
- Ferma
- Chiarezza
- Specificata chiaramente
- Idoneità per principianti
- 25/100