comparison mercurial/util.py @ 38797:8751d1e2a7ff

util: create a context manager to handle timing The context manager is pulled out of the timed decorator function, and refactored to provide a stats instance, with added tests.
author Martijn Pieters <mj@zopatista.com>
date Wed, 01 Aug 2018 16:05:41 +0200
parents 27391d74aaa2
children 9d49bb117dde
comparison
equal deleted inserted replaced
38796:f956dc7217fc 38797:8751d1e2a7ff
34 import time 34 import time
35 import traceback 35 import traceback
36 import warnings 36 import warnings
37 import zlib 37 import zlib
38 38
39 from .thirdparty import (
40 attr,
41 )
39 from . import ( 42 from . import (
40 encoding, 43 encoding,
41 error, 44 error,
42 i18n, 45 i18n,
43 node as nodemod, 46 node as nodemod,
2872 (100, 0.000000001, _('%.1f ns')), 2875 (100, 0.000000001, _('%.1f ns')),
2873 (10, 0.000000001, _('%.2f ns')), 2876 (10, 0.000000001, _('%.2f ns')),
2874 (1, 0.000000001, _('%.3f ns')), 2877 (1, 0.000000001, _('%.3f ns')),
2875 ) 2878 )
2876 2879
2877 _timenesting = [0] 2880 @attr.s
2881 class timedcmstats(object):
2882 """Stats information produced by the timedcm context manager on entering."""
2883
2884 # the starting value of the timer as a float (meaning and resulution is
2885 # platform dependent, see util.timer)
2886 start = attr.ib(default=attr.Factory(lambda: timer()))
2887 # the number of seconds as a floating point value; starts at 0, updated when
2888 # the context is exited.
2889 elapsed = attr.ib(default=0)
2890 # the number of nested timedcm context managers.
2891 level = attr.ib(default=1)
2892
2893 def __str__(self):
2894 return timecount(self.elapsed) if self.elapsed else '<unknown>'
2895
2896 @contextlib.contextmanager
2897 def timedcm():
2898 """A context manager that produces timing information for a given context.
2899
2900 On entering a timedcmstats instance is produced.
2901
2902 This context manager is reentrant.
2903
2904 """
2905 # track nested context managers
2906 timedcm._nested += 1
2907 timing_stats = timedcmstats(level=timedcm._nested)
2908 try:
2909 yield timing_stats
2910 finally:
2911 timing_stats.elapsed = timer() - timing_stats.start
2912 timedcm._nested -= 1
2913
2914 timedcm._nested = 0
2878 2915
2879 def timed(func): 2916 def timed(func):
2880 '''Report the execution time of a function call to stderr. 2917 '''Report the execution time of a function call to stderr.
2881 2918
2882 During development, use as a decorator when you need to measure 2919 During development, use as a decorator when you need to measure
2886 def foo(a, b, c): 2923 def foo(a, b, c):
2887 pass 2924 pass
2888 ''' 2925 '''
2889 2926
2890 def wrapper(*args, **kwargs): 2927 def wrapper(*args, **kwargs):
2891 start = timer() 2928 with timedcm() as time_stats:
2892 indent = 2 2929 result = func(*args, **kwargs)
2893 _timenesting[0] += indent 2930 stderr = procutil.stderr
2894 try: 2931 stderr.write('%s%s: %s\n' % (
2895 return func(*args, **kwargs) 2932 ' ' * time_stats.level * 2, func.__name__, time_stats))
2896 finally: 2933 return result
2897 elapsed = timer() - start
2898 _timenesting[0] -= indent
2899 stderr = procutil.stderr
2900 stderr.write('%s%s: %s\n' %
2901 (' ' * _timenesting[0], func.__name__,
2902 timecount(elapsed)))
2903 return wrapper 2934 return wrapper
2904 2935
2905 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30), 2936 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2906 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1)) 2937 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2907 2938