diff --git a/README.rst b/README.rst index b52b6f2..79d1c6b 100644 --- a/README.rst +++ b/README.rst @@ -64,14 +64,14 @@ this would result in:: Output will follow:: - Line # Mem usage Increment Line Contents - ============================================== - 3 @profile - 4 5.97 MB 0.00 MB def my_func(): - 5 13.61 MB 7.64 MB a = [1] * (10 ** 6) - 6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7) - 7 13.61 MB -152.59 MB del b - 8 13.61 MB 0.00 MB return a + Line # Mem usage Increment Occurences Line Contents + ============================================================ + 3 38.816 MiB 38.816 MiB 1 @profile + 4 def my_func(): + 5 46.492 MiB 7.676 MiB 1 a = [1] * (10 ** 6) + 6 199.117 MiB 152.625 MiB 1 b = [2] * (2 * 10 ** 7) + 7 46.629 MiB -152.488 MiB 1 del b + 8 46.629 MiB 0.000 MiB 1 return a The first column represents the line number of the code that has been diff --git a/memory_profiler.py b/memory_profiler.py index cd4ba4f..632bee3 100644 --- a/memory_profiler.py +++ b/memory_profiler.py @@ -280,10 +280,10 @@ def memory_usage(proc=-1, interval=.1, timeout=None, timestamps=False, to this file instead of stored in memory and returned at the end of the subprocess. Useful for long-running processes. Implies timestamps=True. - + max_iterations : int Limits the number of iterations (calls to the process being monitored). Relevent - when the process is a python function. + when the process is a python function. Returns ------- @@ -357,7 +357,7 @@ def memory_usage(proc=-1, interval=.1, timeout=None, timestamps=False, raise p.join(5 * interval) - + if (n_measurements > 4) or (current_iter == max_iter) or (interval < 1e-6): break interval /= 10. @@ -643,7 +643,12 @@ def trace(self, code, lineno, prev_lineno): prev_line_value = self[code].get(prev_lineno, None) if prev_lineno else None prev_line_memory = prev_line_value[1] if prev_line_value else 0 - self[code][lineno] = (max(previous_inc, memory-prev_line_memory), max(memory, previous_memory)) + occ_count = self[code][lineno][2] + 1 if lineno in self[code] else 1 + self[code][lineno] = ( + previous_inc + (memory - prev_line_memory), + max(memory, previous_memory), + occ_count, + ) def items(self): """Iterate on the toplevel code blocks.""" @@ -800,10 +805,10 @@ def disable(self): def show_results(prof, stream=None, precision=1): if stream is None: stream = sys.stdout - template = '{0:>6} {1:>12} {2:>12} {3:<}' + template = '{0:>6} {1:>12} {2:>12} {3:>10} {4:<}' for (filename, lines) in prof.code_map.items(): - header = template.format('Line #', 'Mem usage', 'Increment', + header = template.format('Line #', 'Mem usage', 'Increment', 'Occurences', 'Line Contents') stream.write(u'Filename: ' + filename + '\n\n') @@ -817,13 +822,15 @@ def show_results(prof, stream=None, precision=1): for (lineno, mem) in lines: if mem: inc = mem[0] - mem = mem[1] - mem = template_mem.format(mem) + total_mem = mem[1] + total_mem = template_mem.format(total_mem) + occurences = mem[2] inc = template_mem.format(inc) else: - mem = u'' + total_mem = u'' inc = u'' - tmp = template.format(lineno, mem, inc, all_lines[lineno - 1]) + occurences = u'' + tmp = template.format(lineno, total_mem, inc, occurences, all_lines[lineno - 1]) stream.write(to_str(tmp)) stream.write(u'\n\n') diff --git a/test/test_increment_display.py b/test/test_increment_display.py new file mode 100644 index 0000000..b0dbe51 --- /dev/null +++ b/test/test_increment_display.py @@ -0,0 +1,81 @@ +import unittest + +from memory_profiler import LineProfiler, profile, show_results +from io import StringIO + + +class TestIncrementDisplay(unittest.TestCase): + """Tests memory incrementation / decrementation display""" + + def test_loop_count(self): + + def some_loop(): + for i in range(12): # line -2 + a = 1 # line -1 + + profiler = LineProfiler() + wrapped = profiler(some_loop) + wrapped() + show_results(profiler) + for_line = list(list(profiler.code_map.values())[0].values())[-2] + looped_instruction = list(list(profiler.code_map.values())[0].values())[-1] + + self.assertEqual(for_line[2], 13) + self.assertEqual(looped_instruction[2], 12) + + def test_normal_incr(self): + + def normal_incr(): + use_some_memory = [1] * (10 ** 6) + + profiler = LineProfiler() + wrapped = profiler(normal_incr) + wrapped() + + show_results(profiler) + results = list(list(profiler.code_map.values())[0].values())[-1] + + self.assertGreater(results[0], 0) + self.assertGreater(results[1], results[0]) + self.assertEqual(results[2], 1) + + def test_loop_incr(self): + + def loop_incr(): + a = [] + b = [2] * (2 * 10 ** 7) # line -4 + for i in range(3): + c = [2] * (2 * 10 ** 7) # line -2 + a.append(c) + + profiler = LineProfiler() + wrapped = profiler(loop_incr) + wrapped() + + show_results(profiler) + b_line = list(list(profiler.code_map.values())[0].values())[-4] + c_line = list(list(profiler.code_map.values())[0].values())[-2] + self.assertAlmostEqual(b_line[2] * 3, c_line[2], delta=1) + self.assertEqual(c_line[2], 3) + + def test_decr(self): + + def del_stuff(): + b = [2] * (2 * 10 ** 7) + del b + + profiler = LineProfiler() + wrapped = profiler(del_stuff) + wrapped() + + show_results(profiler) + b_line = list(list(profiler.code_map.values())[0].values())[-2] + del_line = list(list(profiler.code_map.values())[0].values())[-1] + + self.assertGreater(0, del_line[0]) + self.assertGreater(del_line[1], 0) + self.assertAlmostEqual(-del_line[0], b_line[0], delta=1) + + +if __name__ == '__main__': + unittest.main()