Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Implements a benchmark about performance.
4"""
5from io import StringIO
6from pstats import SortKey, Stats
7from cProfile import Profile as c_Profile
8from profile import Profile as py_Profile
9from pyinstrument import Profiler
12class ProfilerCall:
13 """
14 Runs a profiler on a specific call.
15 It can use either :epkg:`pyinstrument`,
16 either :epkg:`cProfile`. The first module
17 takes a snapshot every *interval* and stores
18 the call stack. That explains why not all
19 called functions appear in the summary
20 but the overhead is smaller.
21 """
23 def __init__(self, fct_match=None, name="ProfilerCall",
24 repeat=1000, interval=0.0001, module="pyinstrument"):
25 """
26 @param fct_match function which tells if the profiler
27 should be run on a set of parameters,
28 signature is ``match(**kwargs) -> boolean``
29 @param repeat number of times to repeat the function
30 to profile
31 @param name name of the profile
32 @param interval see `interval <https://github.com/joerick/
33 pyinstrument/blob/master/pyinstrument/profiler.py#L22>`_
34 @param module ``'pyinstrument'`` by default, ``'cProfile'`` or
35 ``'profile'`` works too
36 """
37 self.interval = interval
38 if callable(fct_match):
39 self.fct_match = fct_match
40 elif isinstance(fct_match, dict):
41 def match(**kwargs):
42 for k, v in kwargs.items():
43 if v != fct_match.get(k, v):
44 return False
45 return True
46 self.fct_match = match
47 else:
48 self.fct_match = fct_match
49 self.profiled = []
50 self.kwargs = []
51 self.name = name
52 self.repeat = repeat
53 self.module = module
55 def match(self, **kwargs):
56 """
57 Tells if the profiler should be run on this
58 set of parameters.
60 @param kwargs dictionary of parameters
61 @return boolean
62 """
63 return self.fct_match is None or self.fct_match(**kwargs)
65 def profile(self, fct, **kwargs):
66 """
67 Profiles function *fct*, calls it
68 *repeat* times.
70 @param fct function to profile (no argument)
71 @param kwargs stores additional information
72 about the profiling
73 """
74 if self.module in ('pyinstrument', 'cProfile'):
75 if self.module == 'pyinstrument':
76 profiler = Profiler(interval=self.interval)
77 start = profiler.start
78 stop = profiler.stop
79 else:
80 profiler = c_Profile()
81 start = profiler.enable
82 stop = profiler.disable
83 start()
84 for _ in range(self.repeat):
85 fct()
86 stop()
87 elif self.module == "profile":
88 profiler = py_Profile()
90 def lf():
91 for _ in range(self.repeat):
92 fct()
93 profiler.runcall(lf)
94 else:
95 raise ValueError( # pragma: no cover
96 "Unknown profiler '{}'.".format(self.module))
97 self.profiled.append(profiler)
98 self.kwargs.append(kwargs)
100 def __len__(self):
101 """
102 Returns the number of stored profiles.
103 """
104 return len(self.profiled)
106 def __iter__(self):
107 """
108 Iterates on stored profiled.
109 Returns a couple ``(profile, configuration)``.
110 """
111 for a, b in zip(self.profiled, self.kwargs):
112 yield a, b
114 def to_txt(self, filename):
115 """
116 Saves all profiles into one file.
118 @param filename filename where to save the profiles,
119 can be a stream
120 """
121 if len(self) == 0:
122 raise ValueError( # pragma: no cover
123 "No profile was done.")
124 if isinstance(filename, str):
125 with open(filename, "w") as f: # pylint: disable=W1514
126 self.to_txt(f)
127 return
129 f = filename
130 f.write(self.name + "\n")
131 for i, (prof, kw) in enumerate(self):
132 f.write("------------------------------------------------------\n")
133 f.write("profile %d\n" % i)
134 if kw:
135 for a, b in sorted(kw.items()):
136 f.write("%s=%s\n" % (a, str(b).replace('\n', '\\n')))
137 f.write("--\n")
138 if hasattr(prof, 'output_text'):
139 f.write(prof.output_text(unicode=False, color=False))
140 else:
141 s = StringIO()
142 sortby = SortKey.CUMULATIVE
143 ps = Stats(prof, stream=s).sort_stats(sortby)
144 ps.print_stats()
145 f.write(s.getvalue())
146 f.write("\n")