Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relative percentage display for Console output #174

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion pyinstrument/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ def dash_m_callback(option: str, opt: str, value: str, parser: optparse.OptionPa
help="show everything",
default=False,
)
parser.add_option(
"",
"--show-percentage",
dest="show_percentage",
action="store_true",
help="(text renderer only) show relative percentages alongside timings",
default=False,
)

parser.add_option(
"",
Expand Down Expand Up @@ -299,7 +307,9 @@ def dash_m_callback(option: str, opt: str, value: str, parser: optparse.OptionPa
unicode: Any = options.unicode if unicode_override else file_supports_unicode(f)
color: Any = options.color if color_override else file_supports_color(f)

renderer_kwargs.update({"unicode": unicode, "color": color})
renderer_kwargs.update(
{"unicode": unicode, "color": color, "show_percentage": options.show_percentage}
)

renderer_class = get_renderer_class(options.renderer)
renderer = renderer_class(**renderer_kwargs)
Expand Down
12 changes: 11 additions & 1 deletion pyinstrument/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ def self_time(self, self_time: float):
self._self_time = self_time
self._invalidate_time_caches()

@property
def perc(self) -> float | None:
return self._perc

@perc.setter
def perc(self, val: float):
self._perc = val

# invalidates the cache for the time() function.
# called whenever self_time or _children is modified.
def _invalidate_time_caches(self):
Expand Down Expand Up @@ -139,6 +147,7 @@ def __init__(
self._children = []

self._time = None
self._perc = None
self._await_time = None

if children:
Expand Down Expand Up @@ -291,11 +300,12 @@ def _invalidate_time_caches(self):
frame._await_time = None

def __repr__(self):
return "Frame(identifier=%s, time=%f, len(children)=%d), group=%r" % (
return "Frame(identifier=%s, time=%f, len(children)=%d), group=%r, perc=%r" % (
self.identifier,
self.time(),
len(self.children),
self.group,
self.perc,
)


Expand Down
29 changes: 29 additions & 0 deletions pyinstrument/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,32 @@ def remove_irrelevant_nodes(
remove_irrelevant_nodes(child, options=options, total_time=total_time)

return frame


def compute_relative_percentage(
frame: BaseFrame | None, options: ProcessorOptions
) -> BaseFrame | None:
"""
Compute the relative percentage amongst siblings
"""

def _compute_layer_percentage(frame: BaseFrame):
total_time = frame.time()

for child in frame.children:
child.perc = child.time() / total_time
child = _compute_layer_percentage(child)

if len(frame.children) == 0:
frame.perc = 1.0

return frame

if frame is None:
return None

frame = _compute_layer_percentage(frame)

frame.perc = 1.0

return frame
12 changes: 10 additions & 2 deletions pyinstrument/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,9 @@ def print(
color: bool | None = None,
show_all: bool = False,
timeline: bool = False,
show_percentage: bool = False,
):
"""print(file=sys.stdout, *, unicode=None, color=None, show_all=False, timeline=False)
"""print(file=sys.stdout, *, unicode=None, color=None, show_all=False, timeline=False, show_percentage=False)

Print the captured profile to the console.

Expand All @@ -266,6 +267,7 @@ def print(
:param color: Override ANSI color support detection.
:param show_all: Sets the ``show_all`` parameter on the renderer.
:param timeline: Sets the ``timeline`` parameter on the renderer.
:param show_percentage: Sets the ``show_percentage`` parameter on the renderer.
"""
if unicode is None:
unicode = file_supports_unicode(file)
Expand All @@ -278,6 +280,7 @@ def print(
color=color,
show_all=show_all,
timeline=timeline,
show_percentage=show_percentage,
),
file=file,
)
Expand All @@ -288,13 +291,18 @@ def output_text(
color: bool = False,
show_all: bool = False,
timeline: bool = False,
show_percentage: bool = False,
) -> str:
"""
Return the profile output as text, as rendered by :class:`ConsoleRenderer`
"""
return self.output(
renderer=renderers.ConsoleRenderer(
unicode=unicode, color=color, show_all=show_all, timeline=timeline
unicode=unicode,
color=color,
show_all=show_all,
timeline=timeline,
show_percentage=show_percentage,
)
)

Expand Down
38 changes: 29 additions & 9 deletions pyinstrument/renderers/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ class ConsoleRenderer(Renderer):
consoles.
"""

def __init__(self, unicode: bool = False, color: bool = False, **kwargs: Any):
def __init__(
self,
unicode: bool = False,
color: bool = False,
show_percentage: bool = False,
**kwargs: Any,
):
"""
:param unicode: Use unicode, like box-drawing characters in the output.
:param color: Enable color support, using ANSI color sequences.
Expand All @@ -27,6 +33,7 @@ def __init__(self, unicode: bool = False, color: bool = False, **kwargs: Any):
self.unicode = unicode
self.color = color
self.colors = self.colors_enabled if color else self.colors_disabled
self.show_percentage = show_percentage

def render(self, session: Session):
result = self.render_preamble(session)
Expand Down Expand Up @@ -76,14 +83,26 @@ def render_frame(self, frame: BaseFrame, indent: str = "", child_indent: str = "
):
time_str = self._ansi_color_for_time(frame) + f"{frame.time():.3f}" + self.colors.end
function_color = self._ansi_color_for_function(frame)
result = "{indent}{time_str} {function_color}{function}{c.end} {c.faint}{code_position}{c.end}\n".format(
indent=indent,
time_str=time_str,
function_color=function_color,
function=frame.function,
code_position=frame.code_position_short,
c=self.colors,
)
if not self.show_percentage:
result = "{indent}{time_str} {function_color}{function}{c.end} {c.faint}{code_position}{c.end}\n".format(
indent=indent,
time_str=time_str,
function_color=function_color,
function=frame.function,
code_position=frame.code_position_short,
c=self.colors,
)
else:
perc_str = self._ansi_color_for_time(frame) + f"{frame.perc:.2f}" + self.colors.end
result = "{indent}{time_str} ({perc_str}) {function_color}{function}{c.end} {c.faint}{code_position}{c.end}\n".format(
indent=indent,
time_str=time_str,
perc_str=perc_str,
function_color=function_color,
function=frame.function,
code_position=frame.code_position_short,
c=self.colors,
)
if self.unicode:
indents = {"├": "├─ ", "│": "│ ", "└": "└─ ", " ": " "}
else:
Expand Down Expand Up @@ -142,6 +161,7 @@ def default_processors(self) -> ProcessorList:
processors.group_library_frames_processor,
processors.remove_unnecessary_self_time_nodes,
processors.remove_irrelevant_nodes,
processors.compute_relative_percentage,
]

class colors_enabled:
Expand Down