[docs]classNullProfiler:"""This is a null profiler that does nothing"""def__init__(self,name:str=None,parent=None):passdefis_null(self)->bool:returnTruedef__str__(self):return"No profiling data collected"defstart(self,name:str=None):returnselfdefstop(self):returnself
[docs]classProfiler:"""This is a simple profiling class that supports manual instrumenting of the code. It is used for sub-function profiling. """def__init__(self,name:str=None,parent=None):self._name=nameself._parent=parentself._children=[]self._start=Noneself._end=None
[docs]defis_null(self)->bool:"""Return whether this is a null profiler"""returnFalse
def_to_string(self):"""Used to write the results of profiling as a report"""lines=[]t=self.total()ifself._nameisNone:ifself._parentisNone:self._name="Total time"ift:iflen(self._children)>0:ct=self.child_total()ifct:lines.append("%s: %.3f ms (%.3f ms)"%(self._name,t,ct))else:lines.append("%s: %.3f ms (???) ms"%(self._name,t))else:lines.append("%s: %.3f ms"%(self._name,t))elifself._startisNone:lines.append(f"{self._name}")else:lines.append(f"{self._name}: still timing...")forchildinself._children:clines=child._to_string()lines.append(f" \\-{clines[0]}")iflen(clines)>1:forlineinclines[1:]:lines.append(f" {line}")returnlinesdef__str__(self):"""Return the results of profiling as a report"""return"\n".join(self._to_string())
[docs]defname(self)->str:"""Return the name of this profiler"""returnself._name
[docs]defchild_total(self)->float:"""Return the sum of time spent in the child profilers"""sum=Noneforchildinself._children:t=child.total()iftisnotNone:ifsumisNone:sum=telse:sum+=treturnsum
[docs]deftotal(self)->float:"""Return the amount of time that was recorded for this profiler in milliseconds (accurate to ~nanoseconds) """ifself._parentisNone:# this is the top-level profiler - just return the child totalreturnself.child_total()elifself._end:return(self._end-self._start)*0.000001# ns to mselse:returnNone
[docs]defstart(self,name:str):"""Start profiling the section called 'name'. This returns the updated profiler, e.g. p = Profiler() p = p.start("long_loop") # run some code p = p.end() print(p) """p=Profiler(name=name,parent=self)self._children.append(p)p._start=time.time_ns()returnp
[docs]defstop(self):"""Stop profiling. This records the end time and returns the parent profiler (if we have one) """end=time.time_ns()ifself._startisNone:from._consoleimportConsoleConsole.warning(f"You cannot stop profiler {self._name} as "f"it has not been started!")elifself._endisnotNone:from._consoleimportConsoleConsole.warning(f"WARNING: You cannot stop profiler {self._name} as "f"it has already been stopped!")else:self._end=endifself._parent:returnself._parentelse:# no parent - just return this profilerreturnself