Coverage for pyrc \ tools \ timing.py: 30%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-13 16:59 +0200

1# ------------------------------------------------------------------------------- 

2# Copyright (C) 2026 Joel Kimmich, Tim Jourdan 

3# ------------------------------------------------------------------------------ 

4# License 

5# This file is part of PyRC, distributed under GPL-3.0-or-later. 

6# ------------------------------------------------------------------------------ 

7 

8import numpy as np 

9import time 

10import os 

11 

12from pyrc.tools import round_valid 

13 

14 

15class Timing: 

16 def __init__( 

17 self, 

18 name: str = "", 

19 print_init: bool = True, 

20 print_results: bool = True, 

21 no_logging: bool = False, 

22 result_path: str = None, 

23 ): 

24 """ 

25 Class for simple timing. 

26 

27 This class was originally written by Joel Kimmich in his Fraqualex-project. 

28 

29 Parameters 

30 ---------- 

31 name : str, optional 

32 Possible prefix 

33 print_init : bool, optional 

34 If True the dialog 'Started Timing' will be printed out of init 

35 print_results : bool, optional 

36 If False no prints will be done 

37 no_logging : bool, optional 

38 If True no logging will be done, which speeds up the timing a bit. 

39 result_path : str, optional 

40 The path to save a result file with all events. 

41 If None the result will not be saved. 

42 

43 Examples 

44 -------- 

45 >>> time = Timing() 

46 >>> time.catch_time() # prints out the current time and the difference to the last catch_time call 

47 >>> time.end_timing() # ends the timing with a last print out (if print_results is True) 

48 >>> log: dict = time.log # in the log you can find all times deltas in a dict (if no_logging is False) 

49 """ 

50 self.times = [] 

51 self.no_logging = no_logging 

52 start_time_ns = time.perf_counter_ns() 

53 self.times.append(start_time_ns) 

54 self.name = name + ": " 

55 self.print_results = print_results 

56 self.result_path = result_path 

57 if self.result_path: 

58 os.makedirs(os.path.dirname(self.result_path), exist_ok=True) 

59 

60 if print_init and print_results: 

61 print(f"{self.clock}: {self.name}Started Timing") 

62 

63 # internal logging of all catch_time events 

64 self.logging = {0: self.name + "start"} 

65 self.logging_hr = {ns_to_hr_time(0): self.name + "start"} 

66 

67 @property 

68 def clock(self): 

69 return time.strftime("%H:%M:%S") 

70 

71 @property 

72 def time(self): 

73 diff = np.array(self.times) 

74 diff = diff - self.times[0] 

75 return diff.tolist() 

76 

77 @property 

78 def log(self) -> dict: 

79 return self.logging 

80 

81 @property 

82 def seconds(self): 

83 return [x / 1e9 for x in self.time] 

84 

85 @property 

86 def log_hr(self): 

87 return self.logging_hr 

88 

89 # def write_log_file(self, result_path: str = None): 

90 # """ 

91 # Write the current log into a yaml file. 

92 # 

93 # Parameters 

94 # ---------- 

95 # result_path : str, optional 

96 # The path to save a result file with all events. 

97 # If None, self.result_path will be used. If this is also None, no log file is written. 

98 # 

99 # """ 

100 # if result_path is None: 

101 # if self.result_path: 

102 # result_path = self.result_path 

103 # 

104 # if result_path: 

105 # write_dict_to_yaml(data=self.log, path=result_path) 

106 # else: 

107 # logging.warning("No logging file created because no file path is given.") 

108 

109 # def plot_time_catches(self): 

110 # """ 

111 # Plots the times over the calls with matplotlib. 

112 # 

113 # Returns 

114 # ------- 

115 # """ 

116 # seconds = self.seconds 

117 # plot_over_time(seconds, list(range(0, len(seconds)))) 

118 

119 def catch_time(self, message=None): 

120 self.times.append(time.perf_counter_ns()) 

121 if message is None: 

122 message = "no message" 

123 

124 # log times with a message in dict 

125 delta = self.times[-1] - self.times[0] 

126 

127 if not self.no_logging: 

128 self.logging[delta] = message 

129 self.logging_hr[ns_to_hr_time(delta)] = message 

130 

131 if self.print_results: 

132 time_diff = self.times[-1] - self.times[-2] 

133 time_str = ns_to_hr_time(time_diff) 

134 if message is not None: 

135 print(f"{self.clock}: {self.name}Time-diff: {time_str} | {message}") 

136 else: 

137 print(f"{self.clock}: {self.name}Time-diff: {time_str}") 

138 

139 def end_timing(self, return_times: bool = False): 

140 self.catch_time("Ending timing.") 

141 if self.print_results: 

142 valid = round_valid((self.times[-1] - self.times[0]) / 1e9, 4) 

143 print(f"{self.clock}: {self.name}Duration: {valid} seconds") 

144 if return_times: 

145 times_seconds = [x / 1e9 for x in self.times] 

146 return times_seconds 

147 # if self.result_path: 

148 # self.write_log_file() 

149 

150 

151def ns_to_hr_time(nanoseconds: int): 

152 """ 

153 Parses a value in nanoseconds to a human-readable time string. 

154 

155 Use this function only for relative values because the absolute value isn't defined. 

156 

157 Parameters 

158 ---------- 

159 nanoseconds : int 

160 

161 Returns 

162 ------- 

163 str : 

164 The human-readable time string with hours:minutes:seconds. Seconds has four decimal digits. 

165 """ 

166 seconds = nanoseconds / 1e9 

167 hours = seconds // 3600 

168 minutes = (seconds % 3600) // 60 

169 seconds = (seconds % 3600) % 60 

170 return f"{'{:5.0f}'.format(hours)}:{'{:02.0f}'.format(minutes)}:{'{:07.4f}'.format(seconds)}"