Skip to content

Commit 3886eea

Browse files
authored
Merge pull request #229 from Afoucaul/master
Py-Spy style flame graph for timestamp visualisation
2 parents 36eade4 + 85976ed commit 3886eea

4 files changed

Lines changed: 265 additions & 14 deletions

File tree

README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ afterward will plot the result, making plots (using matplotlib) similar to these
150150
:target: https://github.com/scikit-learn/scikit-learn/pull/2248
151151
:height: 350px
152152

153+
or, with ``mprof plot --flame`` (the function and timestamp names will appear on hover):
154+
155+
.. image:: ./images/flamegraph.png
156+
:height: 350px
157+
153158
A discussion of these capabilities can be found `here <http://fa.bianp.net/blog/2014/plot-memory-usage-as-a-function-of-time/>`_.
154159

155160
.. warning:: If your Python file imports the memory profiler `from memory_profiler import profile` these timestamps will not be recorded. Comment out the import, leave your functions decorated, and re-run.

images/flamegraph.png

60.6 KB
Loading

memory_profiler.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import time
1919
import traceback
2020
import warnings
21+
import contextlib
2122

2223
if sys.platform == "win32":
2324
# any value except signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT
@@ -460,16 +461,25 @@ def _find_script(script_name):
460461
class _TimeStamperCM(object):
461462
"""Time-stamping context manager."""
462463

463-
def __init__(self, timestamps, filename, backend):
464+
def __init__(self, timestamps, filename, backend, timestamper=None, func=None):
464465
self.timestamps = timestamps
465466
self.filename = filename
466467
self.backend = backend
468+
self.ts = timestamper
469+
self.func = func
467470

468471
def __enter__(self):
472+
if self.ts is not None:
473+
self.ts.current_stack_level += 1
474+
self.ts.stack[self.func].append(self.ts.current_stack_level)
475+
469476
self.timestamps.append(
470477
_get_memory(os.getpid(), self.backend, timestamps=True, filename=self.filename))
471478

472479
def __exit__(self, *args):
480+
if self.ts is not None:
481+
self.ts.current_stack_level -= 1
482+
473483
self.timestamps.append(
474484
_get_memory(os.getpid(), self.backend, timestamps=True, filename=self.filename))
475485

@@ -482,6 +492,8 @@ class TimeStamper:
482492
def __init__(self, backend):
483493
self.functions = {}
484494
self.backend = backend
495+
self.current_stack_level = -1
496+
self.stack = {}
485497

486498
def __call__(self, func=None, precision=None):
487499
if func is not None:
@@ -516,11 +528,18 @@ def timestamp(self, name="<block>"):
516528
filename = inspect.getsourcefile(func)
517529
except TypeError:
518530
filename = '<unknown>'
519-
return _TimeStamperCM(timestamps, filename, self.backend)
531+
return _TimeStamperCM(
532+
timestamps,
533+
filename,
534+
self.backend,
535+
timestamper=self,
536+
func=func
537+
)
520538

521539
def add_function(self, func):
522540
if func not in self.functions:
523541
self.functions[func] = []
542+
self.stack[func] = []
524543

525544
def wrap_function(self, func):
526545
""" Wrap a function to timestamp it.
@@ -536,23 +555,33 @@ def f(*args, **kwds):
536555
_get_memory(os.getpid(), self.backend, timestamps=True, filename=filename)]
537556
self.functions[func].append(timestamps)
538557
try:
539-
return func(*args, **kwds)
558+
with self.call_on_stack(func, *args, **kwds) as result:
559+
return result
540560
finally:
541561
# end time
542562
timestamps.append(_get_memory(os.getpid(), self.backend, timestamps=True,
543563
filename=filename))
544564

545565
return f
546566

567+
@contextlib.contextmanager
568+
def call_on_stack(self, func, *args, **kwds):
569+
self.current_stack_level += 1
570+
self.stack[func].append(self.current_stack_level)
571+
572+
yield func(*args, **kwds)
573+
574+
self.current_stack_level -= 1
575+
547576
def show_results(self, stream=None):
548577
if stream is None:
549578
stream = sys.stdout
550579

551580
for func, timestamps in self.functions.items():
552581
function_name = "%s.%s" % (func.__module__, func.__name__)
553-
for ts in timestamps:
554-
stream.write("FUNC %s %.4f %.4f %.4f %.4f\n" % (
555-
(function_name,) + ts[0] + ts[1]))
582+
for ts, level in zip(timestamps, self.stack[func]):
583+
stream.write("FUNC %s %.4f %.4f %.4f %.4f %d\n" % (
584+
(function_name,) + ts[0] + ts[1] + (level,)))
556585

557586

558587
class CodeMap(dict):

0 commit comments

Comments
 (0)