Skip to content

Commit e234c1f

Browse files
committed
pythongh-115362: Add documentation to pystats output
1 parent 91bf01d commit e234c1f

File tree

1 file changed

+87
-15
lines changed

1 file changed

+87
-15
lines changed

Tools/scripts/summarize_stats.py

+87-15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from pathlib import Path
2222
import re
2323
import sys
24+
import textwrap
2425
from typing import Any, Callable, TextIO, TypeAlias
2526

2627

@@ -389,17 +390,58 @@ def get_optimization_stats(self) -> dict[str, tuple[int, int | None]]:
389390
low_confidence = self._data["Optimization low confidence"]
390391

391392
return {
392-
"Optimization attempts": (attempts, None),
393-
"Traces created": (created, attempts),
394-
"Trace stack overflow": (trace_stack_overflow, attempts),
395-
"Trace stack underflow": (trace_stack_underflow, attempts),
396-
"Trace too long": (trace_too_long, attempts),
397-
"Trace too short": (trace_too_short, attempts),
398-
"Inner loop found": (inner_loop, attempts),
399-
"Recursive call": (recursive_call, attempts),
400-
"Low confidence": (low_confidence, attempts),
401-
"Traces executed": (executed, None),
402-
"Uops executed": (uops, executed),
393+
Doc(
394+
"Optimization attempts",
395+
"""
396+
The number of times a potential trace is identified. Specifically,
397+
this occurs in the JUMP BACKWARD instruction when the counter reaches
398+
a threshold.
399+
""",
400+
): (
401+
attempts,
402+
None,
403+
),
404+
Doc(
405+
"Traces created", "The number of traces that were successfully created."
406+
): (created, attempts),
407+
Doc(
408+
"Trace stack overflow",
409+
"A trace is truncated because it would require more than 5 stack frames.",
410+
): (trace_stack_overflow, attempts),
411+
Doc(
412+
"Trace stack underflow",
413+
"A potential trace is abandoned because it pops more frames than it pushes.",
414+
): (trace_stack_underflow, attempts),
415+
Doc(
416+
"Trace too long",
417+
"A trace is truncated because it is longer than the instruction buffer.",
418+
): (trace_too_long, attempts),
419+
Doc(
420+
"Trace too short",
421+
"A potential trace is abandoced because it it too short.",
422+
): (trace_too_short, attempts),
423+
Doc(
424+
"Inner loop found", "A trace is truncated because it has an inner loop"
425+
): (inner_loop, attempts),
426+
Doc(
427+
"Recursive call",
428+
"A trace is truncated because it has a recursive call.",
429+
): (recursive_call, attempts),
430+
Doc(
431+
"Low confidence",
432+
"""
433+
A trace is abandoned because the likelihood of the jump to top being
434+
taken is too low.
435+
""",
436+
): (low_confidence, attempts),
437+
Doc("Traces executed", "The number of traces that were executed"): (
438+
executed,
439+
None,
440+
),
441+
Doc("Uops executed", "The total number of uops that were executed"): (
442+
uops,
443+
executed,
444+
),
403445
}
404446

405447
def get_histogram(self, prefix: str) -> list[tuple[int, int]]:
@@ -415,12 +457,21 @@ def get_histogram(self, prefix: str) -> list[tuple[int, int]]:
415457
def get_rare_events(self) -> list[tuple[str, int]]:
416458
prefix = "Rare event "
417459
return [
418-
(key[len(prefix) + 1:-1].replace("_", " "), val)
460+
(key[len(prefix) + 1 : -1].replace("_", " "), val)
419461
for key, val in self._data.items()
420462
if key.startswith(prefix)
421463
]
422464

423465

466+
class Doc:
467+
def __init__(self, text: str, doc: str):
468+
self.text = text
469+
self.doc = textwrap.dedent(doc).strip()
470+
471+
def markdown(self) -> str:
472+
return f'{self.text} [ⓘ](## "{self.doc}")'
473+
474+
424475
class Count(int):
425476
def markdown(self) -> str:
426477
return format(self, ",d")
@@ -568,13 +619,16 @@ def __init__(
568619
title: str = "",
569620
summary: str = "",
570621
part_iter=None,
622+
*,
571623
comparative: bool = True,
624+
doc: str = "",
572625
):
573626
self.title = title
574627
if not summary:
575628
self.summary = title.lower()
576629
else:
577630
self.summary = summary
631+
self.doc = textwrap.dedent(doc)
578632
if part_iter is None:
579633
part_iter = []
580634
if isinstance(part_iter, list):
@@ -628,6 +682,10 @@ def execution_count_section() -> Section:
628682
join_mode=JoinMode.CHANGE_ONE_COLUMN,
629683
)
630684
],
685+
doc="""
686+
The "miss ratio" column shows the percentage of times the instruction
687+
executed that it deoptimized.
688+
""",
631689
)
632690

633691

@@ -655,7 +713,7 @@ def calc_pair_count_table(stats: Stats) -> Rows:
655713

656714
return Section(
657715
"Pair counts",
658-
"Pair counts for top 100 pairs",
716+
"Pair counts for top 100 Tier 1 instructions",
659717
[
660718
Table(
661719
("Pair", "Count:", "Self:", "Cumulative:"),
@@ -705,7 +763,7 @@ def iter_pre_succ_pairs_tables(base_stats: Stats, head_stats: Stats | None = Non
705763

706764
return Section(
707765
"Predecessor/Successor Pairs",
708-
"Top 5 predecessors and successors of each opcode",
766+
"Top 5 predecessors and successors of each Tier 1 opcode",
709767
iter_pre_succ_pairs_tables,
710768
comparative=False,
711769
)
@@ -1073,8 +1131,19 @@ def iter_optimization_tables(base_stats: Stats, head_stats: Stats | None = None)
10731131

10741132

10751133
def rare_event_section() -> Section:
1134+
RARE_DOCS = {
1135+
"set class": "Setting an object's class, `obj.__class__ = ...`",
1136+
"set bases": "Setting the bases of a class, `cls.__bases__ = ...`",
1137+
"set eval frame func": (
1138+
"Setting the PEP 523 frame eval function "
1139+
"`_PyInterpreterState_SetFrameEvalFunc()`"
1140+
),
1141+
"builtin dict": "Modifying the builtins, `__builtins__.__dict__[var] = ...`",
1142+
"func modification": "Modifying a function, e.g. `func.__defaults__ = ...`, etc.",
1143+
}
1144+
10761145
def calc_rare_event_table(stats: Stats) -> Table:
1077-
return [(x, Count(y)) for x, y in stats.get_rare_events()]
1146+
return [(Doc(x, RARE_DOCS[x]), Count(y)) for x, y in stats.get_rare_events()]
10781147

10791148
return Section(
10801149
"Rare events",
@@ -1134,6 +1203,9 @@ def to_markdown(x):
11341203
print("<details>", file=out)
11351204
print("<summary>", obj.summary, "</summary>", file=out)
11361205
print(file=out)
1206+
if obj.doc:
1207+
print(obj.doc, file=out)
1208+
11371209
if head_stats is not None and obj.comparative is False:
11381210
print("Not included in comparative output.\n")
11391211
else:

0 commit comments

Comments
 (0)