Package: charmonium.time_block
A decorator and a context-manager (with-statment) to time a block of
code.Quickstart$ pip install charmonium.time_block>>>importcharmonium.time_blockasch_time_block>>>ch_time_block._enable_doctest_logging()>>>importtime>>>>>>deffoo():...withch_time_block.ctx("bar"):...time.sleep(0.1)...>>>foo()>bar:running>bar:0.1sEquivalent context-manager:>>>importcharmonium.time_blockasch_time_block>>>ch_time_block._enable_doctest_logging()>>>>>>deffoo():...bar()...>>>>>>@ch_time_block.decor("bar")...defbar():...time.sleep(0.1)...>>>foo()>bar:running>bar:0.1sline_profis extremely detailed and complex, which makes it more
appropriate when you don’t know what to measure, whereas this package
is more appropriate when you already know the bottleneck, and just
want to see how slow a few functions/blocks are.Unlike external profiling, This does not need source-code access, so
it will work from.eggs.Unlike external profiling, this package reports in realtime tologger(destination customizable). This is intended to let the user
know what the code is doing right now.> download: running
> download: 0.1s
> processing: running
> processing > decompress: running
> processing > decompress: 0.2s
> processing: 0.4sSince this plugs into Python’sloggerinfrastructure, this can feed a pipeline that checks the
application health (e.g. ensuring a microservice is responsive).This records process’s increase in memory usage (relatively
cross-platform method usingpsutil) whendo_gc=True, which
gives a rough estimate of the memory leaked by the block.Like function profiling, but unlike other block-profilers, it is
recurrent, and it maintains a stack.>>>importcharmonium.time_blockasch_time_block>>>ch_time_block._enable_doctest_logging()>>>importtime>>>>>>@ch_time_block.decor()...deffoo():...time.sleep(0.1)...bar()...>>>>>>@ch_time_block.decor()...defbar():...time.sleep(0.2)...withch_time_block.ctx("baz"):...time.sleep(0.3)...>>>foo()>foo:running>foo>bar:running>foo>bar>baz:running>foo>bar>baz:0.3s>foo>bar:0.5s>foo:0.6sThis handles recursion.>>>importcharmonium.time_blockasch_time_block>>>ch_time_block._enable_doctest_logging()>>>importtime>>>>>>@ch_time_block.decor(print_args=True)...deffoo(n):...ifn!=0:...time.sleep(0.1)...returnfoo(n-1)...>>>foo(2)>foo(2):running>foo(2)>foo(1):running>foo(2)>foo(1)>foo(0):running>foo(2)>foo(1)>foo(0):0.0s>foo(2)>foo(1):0.1s>foo(2):0.2sThis even works for threads (or more usefullyThreadPoolExecutor).>>>importcharmonium.time_blockasch_time_block>>>ch_time_block._enable_doctest_logging()>>>importtime>>>fromconcurrent.futuresimportThreadPoolExecutor>>>>>>@ch_time_block.decor()...deffoo():...time.sleep(0.1)...baz()...>>>@ch_time_block.decor()...defbar():...time.sleep(0.2)...baz()...>>>@ch_time_block.decor()...defbaz():...returntime.sleep(0.3)...>>>fromthreadingimportThread>>>threads=[Thread(target=foo),Thread(target=bar)]>>>forthreadinthreads:# doctest:+SKIP...thread.start()...>foo:running>bar:running>foo>baz:running>bar>baz:running>foo>baz:0.3s>foo:0.4s>bar>baz:0.3s>bar:0.5s>>># TODO: get a better example, with named threadsThe results are programatically accessible at runtime. In the dict
returned byget_stats(), the stack frame (key) is represented as a
tuple of strings while the profile result (value) is a pair of time
and memory used.>>>importcharmonium.time_blockasch_time_block>>>ch_time_block._enable_doctest_logging()>>>ch_time_block.clear()>>>importtime>>>>>>@ch_time_block.decor()...deffoo():...time.sleep(0.1)...bar()...>>>>>>@ch_time_block.decor()...defbar():...time.sleep(0.2)...>>>[foo()for_inrange(2)]>foo:running>foo>bar:running>foo>bar:0.2s>foo:0.3s>foo:running>foo>bar:running>foo>bar:0.2s>foo:0.3s[None,None]>>>ch_time_block.get_stats()# doctest:+SKIP{('foo','bar'):[(0.2,0),(0.2,0)],('foo',):[(0.3,0),(0.3,0)]}>>>ch_time_block.print_stats()# doctest:+SKIPfoo=100%oftotal=100%ofparent=(0.3+/-0.0)sec=2*(0.2+/-0.0)secusing(0.0+/-0.0)Bfoo>bar=100%oftotal=66%ofparent=(0.2+/-0.0)sec=2*(0.1+/-0.0)secusing(0.0+/-0.0)B