Hide keyboard shortcuts

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 

10 

11 

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 """ 

22 

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 

54 

55 def match(self, **kwargs): 

56 """ 

57 Tells if the profiler should be run on this 

58 set of parameters. 

59 

60 @param kwargs dictionary of parameters 

61 @return boolean 

62 """ 

63 return self.fct_match is None or self.fct_match(**kwargs) 

64 

65 def profile(self, fct, **kwargs): 

66 """ 

67 Profiles function *fct*, calls it 

68 *repeat* times. 

69 

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() 

89 

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) 

99 

100 def __len__(self): 

101 """ 

102 Returns the number of stored profiles. 

103 """ 

104 return len(self.profiled) 

105 

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 

113 

114 def to_txt(self, filename): 

115 """ 

116 Saves all profiles into one file. 

117 

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 

128 

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")