Source code for curry.backends.generic.eval.telemetry

'''
Code for reporting the status of Sprite over time.
'''
import collections, contextlib, logging, re
from ....utility import timer

logger = logging.getLogger(__name__)

[docs] class TelemetryData(object): def __init__(self, rts, full_report_interval=20): self.full_report_interval = full_report_interval self.report_count = 0 self.rts = rts self.prev = None self._forks = 0 self._eqconstr = 0 self._values = 0 self._constrlift = 0 self._copyspine = 0 self._pulltabs = 0 self._enterD = 0 self._enterN = 0 self._enterS = 0 self._enterhnf = 0 self._iterD = 0 self._iterhnf = 0 @property def iterations_in_D(self): 'The number of iterations in D.' return self._iterD @property def rewrite_steps(self): 'The number of rewrite steps taken.' return self.rts.stepcounter.global_count @property def forks(self): 'The number of times a configuration forked.' return self._forks @property def top_eqconstr(self): 'The number of times an equational constraint was handled by D.' return self._eqconstr @property def num_values(self): 'The number of values produced.' return self._values @property def num_values(self): 'The number of constrint-lifting steps.' return self._constrlift @property def copy_spine(self): 'The number of times a spine was copied.' return self._copyspine @property def pull_tab_steps(self): 'The number of pull-tab steps.' return self._pulltabs @property def entries_to_D(self): 'The number of entries to D.' return self._enterD @property def entries_to_N(self): 'The number of entries to N.' return self._enterN @property def entries_to_S(self): 'The number of entries to S.' return self._enterS @property def entries_to_hnf(self): 'The number of entries to hnf.' return self._enterhnf @property def iterations_in_hnf(self): 'The number of iterations in hnf.' return self._iterhnf P_GET_COUNT = re.compile(r'count\((\d+)\)') @property def next_cid(self): 'The next available choice or variable ID' m = re.match(self.P_GET_COUNT, str(self.rts.idfactory)) return int(str(m.group(1))) @property def next_sid(self): 'The next available set ID' m = re.match(self.P_GET_COUNT, str(self.rts.setfactory)) return int(str(m.group(1))) @property def number_of_queues(self): 'The number of work queues in existence' return len(self.rts.qtable) @property def queue_stack_depth(self): 'The set function nesting depth' return len(self.rts.qstack) @property def vtable_size(self): 'The number of free variables in existence' return len(self.rts.vtable) @property def sftable_size(self): 'The number of set functions currently being evaluated' return len(self.rts.sftable) ATTRS = sorted([k for k,v in locals().items() if isinstance(v, property)])
[docs] def capture(self): '''Capture the telemetry information.''' return {attr: getattr(self, attr) for attr in self.ATTRS}
[docs] def report(self, prev=None): '''Output a report to the log.''' is_full = self.report_count % self.full_report_interval == 0 self.report_count += 1 logger.info('------ Telemetry Report ------') data = self.capture() for line in self.show(data, full=is_full).split('\n'): logger.info(line) self.prev = data
@staticmethod def _formatdiff(a, b): if a is None or not (b - a): return '' else: return '(%+8d)' % (b - a)
[docs] def show(self, data, full=False): '''Show the telemetry information.''' fmt = '%-20s %-8s %-10s \'%s' def lines(): for attr in self.ATTRS: attr_value = data[attr] attr_doc = getattr(type(self), attr).__doc__ prev_value = None if self.prev is None else self.prev[attr] diff = self._formatdiff(prev_value, attr_value) if full or diff: yield fmt % (attr, attr_value, diff, attr_doc) return '\n'.join(lines())
[docs] def report_telemetry(rts, interval, generator): ''' Sets a repeating timer to log telemetry every ``interval`` seconds while evaluating the generator. ''' assert interval > 0 t = timer.RepeatingTimer(interval, rts.telemetry.report) try: t.start() for value in generator: yield value finally: t.cancel()