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
« 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# ------------------------------------------------------------------------------
8import numpy as np
9import time
10import os
12from pyrc.tools import round_valid
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.
27 This class was originally written by Joel Kimmich in his Fraqualex-project.
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.
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)
60 if print_init and print_results:
61 print(f"{self.clock}: {self.name}Started Timing")
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"}
67 @property
68 def clock(self):
69 return time.strftime("%H:%M:%S")
71 @property
72 def time(self):
73 diff = np.array(self.times)
74 diff = diff - self.times[0]
75 return diff.tolist()
77 @property
78 def log(self) -> dict:
79 return self.logging
81 @property
82 def seconds(self):
83 return [x / 1e9 for x in self.time]
85 @property
86 def log_hr(self):
87 return self.logging_hr
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.")
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))))
119 def catch_time(self, message=None):
120 self.times.append(time.perf_counter_ns())
121 if message is None:
122 message = "no message"
124 # log times with a message in dict
125 delta = self.times[-1] - self.times[0]
127 if not self.no_logging:
128 self.logging[delta] = message
129 self.logging_hr[ns_to_hr_time(delta)] = message
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}")
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()
151def ns_to_hr_time(nanoseconds: int):
152 """
153 Parses a value in nanoseconds to a human-readable time string.
155 Use this function only for relative values because the absolute value isn't defined.
157 Parameters
158 ----------
159 nanoseconds : int
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)}"