Package: ttimer
ttimerttimer is a simple timer that keeps track of the call hierarchy.
ttimer is intended to fulfill a use case somewhere between a timer and a profiler:
like a timer, it measures only where you explicitly write it, while like a profiler, it handles the call hierarchy and measures own time.Install>pipinstallttimeror simply copy whole ttimer.py into your project.
(the license text is also included in ttimer.py)Usage>>>fromtimeimportsleep>>>fromttimerimportget_timer>>>timer=get_timer(timer_name="my timer")>>>withtimer("a"):>>>sleep(0.1)>>>for_inrange(10):>>>withtimer("b"):>>>sleep(0.01)>>>withtimer("b"):>>>sleep(0.1)>>>print(timer.render())pathcounttimeowntimecputimeowncputime-------------------------------------------------------a10.2038310.1005310.0013140.000784└──b100.1032990.1032990.000530.00053b10.1036030.1036038.2e-058.2e-05>>>print(timer.render(flat=True))pathcounttimeowntimecputimeowncputime-------------------------------------------------------a10.2038310.1005310.0013140.000784b110.2069030.2069030.0006120.000612ttimer records the following metrics in the with-statement:count: Call count.time: Elapsed time measured byperf_counter. It includes time elapsed during sleep and is system-wide.own time: Time, excluding the total time of its children.cpu time: CPU time measured byprocess_time.own cpu time: CPU time, excluding the total time of its children.If the name is not passed in the with-statement,
the name will be automatically resolved from the file and function names.>>>fromttimerimportget_timer>>>t=get_timer(timer_name="foo")>>>witht:>>>pass>>>print(t.render())pathcounttimeowntimecputimeowncputime---------------------------------------------------------------------------------------test_get_timers(test_timer.py:144)10.0003479450.0003479450.0002280.000228You can also use decorators instead of with-statement:fromttimerimporttimer@timer(timer_name="my timer")deffoo(a:int):passIn bothget_timerand@timer, timers with the sametimer_nameshare the same elapsed time.
This is useful when you want to measure times across modules.All named timers are stored as a thread-local variable,
and you can useget_timersto enumerate the stored timers.>>>fromttimerimportget_timer,get_timers>>>withget_timer("foo"):>>>pass>>>withget_timer("bar"):>>>pass>>>all_timers=get_timers(){'foo':<ttimer.timer.Timerobjectat0x7fc9a334fc50>,'bar':<ttimer.timer.Timerobjectat0x7fc9a334df98>}Local timersIf you do not prefer global (thread-local) variables, you can simply create a localTimerinstance.
In this style, if you use a decorator, you should pass the timer you created as an additionaltimerkeyword argument.fromttimerimportTimer,timert=Timer()# local timer@timer()deffoo(a:int):passwitht("a"):foo(a=1,timer=t)# additional "timer" keyword argument are used to specify the contextPropertiesBy accessingtimer[key], you can get the accumulated result as an instance ofRecorddataclass.
You can of course also get a list of records by.records.fromdataclassesimportasdictfromttimerimportget_timertimer=get_timer("my timer")withtimer("a"):passprint("result of{}:".format(timer["a"].name))print("time:{}".format(timer["a"].time))print("cpu time:{}".format(timer["a"].cpu_time))print("own time:{}".format(timer["a"].own_time))print("own cpu time:{}".format(timer["a"].own_cpu_time))print("count:{}".format(timer["a"].count))print(asdict(timer["a"]))# result is dataclasstimer.records# list of recordsThe results you can get with above are equivalent toflat=True: i.e., the measurements with the same name are accumulated.
If you want to get the measurements for each call stack separately, you can use.nodes..nodesreturns all the nodes, but if you want to get only the root node, use.trees.
Both return an instance ofNode, and you can access the child nodes with.children, or access the node's records with.record.fromttimerimportget_timer,Recordtimer=get_timer("my timer")withtimer("a"):withtimer("b"):passwithtimer("b"):passassertlen(timer.records)==2assertlen(timer.nodes)==3asserttimer.nodes[1].stack==("a","b")assertisinstance(timer.nodes[1].record,Record)assertlen(timer.trees)==2