def percentiles_by_stencil(results, percentiles): """Computes given percentiles of measured run times of multiple results and sorts them by stencil. Args: results: List of `Result` objects. percentils: List of percentiles, each percentile must be in the range [0, 100] (inclusive). Returns: A tuple of lists (stencils, percentiles 1, percentiles 2, ...). The number of returned values depends on the number of input percentiles. """ stencils = results[0].stencils if any(stencils != r.stencils for r in results): raise ArgumentError('All results must include the same stencils') qtimes = [] for q in percentiles: def compute_q(times): return np.percentile(times, q) qtimes.append(by_stencils(r.mapped_times(compute_q) for r in results)) return (stencils, *qtimes)
def get_datetime(result): if key == 'runtime': datetime = result.runtime.datetime elif key == 'job': datetime = result.datetime else: raise ArgumentError('"key" argument must be "runtime" or "job"') return time.local_time(datetime)
def times_by_stencil(results): """Collects times of multiple results by stencils. Args: results: List of `Result` objects. Returns: A tuple of lists (stencils, times). """ stencils = results[0].stencils if any(stencils != r.stencils for r in results): raise ArgumentError('All results must include the same stencils') times = by_stencils(r.times_by_stencil() for r in results) return stencils, times
def from_timestr(timestr): """Converts a time string back to a `datetime.datetime` object. The given string must be in the format as generated by the `timestr` function. Args: timestr: A string representing a time in the supported format. Returns: A `datetime.datetime` object, representing the same time as `timestr`. """ try: return datetime.strptime(timestr, '%Y-%m-%dT%H:%M:%S.%f%z') except ValueError: raise ArgumentError(f'"{timestr}" is an invalid time string') from None
def statistics_by_stencil(results): """Computes mean and stdev times of multiple results by stencil. Args: results: List of `Result` objects. Returns: A tuple of lists (stencils, meantimes, stdevtimes). """ stencils = results[0].stencils if any(stencils != r.stencils for r in results): raise ArgumentError('All results must include the same stencils') meantimes = by_stencils(r.mapped_times(np.mean) for r in results) stdevtimes = by_stencils(r.mapped_times(np.std) for r in results) return stencils, meantimes, stdevtimes
def timestr(time): """Returns the given time as a string. Args: time: The time that should be converted to a string. Returns: A string representing `time`, compatible to ISO 8601 time format. """ try: dt_format = ('%Y-%m-%dT%H:%M:%S.%f%z', '%a %b %d %H:%M:&S %Z %Y', '%Y-%m-%d %H;%M') for current_format in dt_format: try: return time.strftime(timestr, current_format) except ValueError: pass except ValueError: raise ArgumentError(f'"{timestr}" is an invalid time string') from None
def history(results, key='job', limit=None): """Plots run time history of all results. Depending on the argument `job`, The results are either ordered by runtime (i.e. commit/build time) or job time (i.e. when the job was run). Args: results: List of `result.Result` objects. key: Either 'job' or 'runtime'. limit: Optionally limits the number of plotted results to the given number, i.e. only displays the most recent results. If `None`, all given results are plotted. """ # get date/time either from the runtime (commit/build) or job (when job # was run) def get_datetime(result): if key == 'runtime': datetime = result.runtime.datetime elif key == 'job': datetime = result.datetime else: raise ArgumentError('"key" argument must be "runtime" or "job"') return time.local_time(datetime) # sort results by desired reference time results = sorted(results, key=get_datetime) if limit is not None: if not isinstance(limit, int) or limit <= 0: raise ArgumentError('"limit" must be a positive integer') results = results[-limit:] data = result.percentiles_by_stencil(results, [0, 25, 50, 75, 100]) dates = [matplotlib.dates.date2num(get_datetime(r)) for r in results] if len(dates) > len(set(dates)): logger.warning('Non-unique datetimes in history plot') locator = matplotlib.dates.AutoDateLocator() formatter = matplotlib.dates.AutoDateFormatter(locator) formatter.scaled[1 / 24] = '%y-%m-%d %H:%M' formatter.scaled[1 / (24 * 60)] = '%y-%m-%d %H:%M' formatter.scaled[1 / (24 * 60 * 60)] = '%y-%m-%d %H:%M:%S' if len(data[0]) < 8: q2, stencil, mint, q1, q3, maxt = zip(*sorted(zip(data[3], data[0], data[1], data[2], data[4], data[5]))) colors = discrete_colors(len(stencil[0])) fig, ax = plt.subplots(1, 1, figsize=figsize(4, 2)) for color, stencil, mint, q1, q2, q3, maxt in zip(colors, stencil, mint, q1, q2, q3, maxt): ax.fill_between(dates, mint, maxt, alpha=0.2, color=color) ax.fill_between(dates, q1, q3, alpha=0.5, color=color) ax.plot(dates, q2, '|-', label=stencil.title(), color=color) ax.legend(loc='upper left') ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) fig.autofmt_xdate() fig.tight_layout() elif len(data[0]) < 20: # Sort by mean time three, zero, one, two, four, five = zip(*sorted(zip(data[3], data[0], data[1], data[2], data[4], data[5]))) stencils = numpy.array_split(numpy.array(zero), 4) mints = numpy.array_split(numpy.array(one), 4) q1s = numpy.array_split(numpy.array(two), 4) q2s = numpy.array_split(numpy.array(three), 4) q3s = numpy.array_split(numpy.array(four), 4) maxts = numpy.array_split(numpy.array(five), 4) colors = discrete_colors(len(stencils[0])) fig, ax = plt.subplots(2, 2, figsize=figsize(4, 2)) axes = itertools.chain(*ax) for cstencil, cmint, cq1, cq2, cq3, cmaxt, ax in zip(stencils, mints, q1s, q2s, q3s, maxts, axes): for color, stencil, mint, q1, q2, q3, maxt in zip(colors, cstencil, cmint, cq1, cq2, cq3, cmaxt): ax.fill_between(dates, mint, maxt, alpha=0.2, color=color) ax.fill_between(dates, q1, q3, alpha=0.5, color=color) ax.plot(dates, q2, '|-', label=stencil.title(), color=color) ax.legend(loc='upper left') ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) fig.autofmt_xdate() fig.tight_layout() else: # Sort by mean time three, zero, one, two, four, five = zip(*sorted(zip(data[3], data[0], data[1], data[2], data[4], data[5]))) stencils = numpy.array_split(numpy.array(zero), 6) mints = numpy.array_split(numpy.array(one), 6) q1s = numpy.array_split(numpy.array(two), 6) q2s = numpy.array_split(numpy.array(three), 6) q3s = numpy.array_split(numpy.array(four), 6) maxts = numpy.array_split(numpy.array(five), 6) colors = discrete_colors(len(stencils[0])) fig, ax = plt.subplots(3, 2, figsize=figsize(4, 2)) axes = itertools.chain(*ax) for cstencil, cmint, cq1, cq2, cq3, cmaxt, ax in zip(stencils, mints, q1s, q2s, q3s, maxts, axes): for color, stencil, mint, q1, q2, q3, maxt in zip(colors, cstencil, cmint, cq1, cq2, cq3, cmaxt): ax.fill_between(dates, mint, maxt, alpha=0.2, color=color) ax.fill_between(dates, q1, q3, alpha=0.5, color=color) ax.plot(dates, q2, '|-', label=stencil.title(), color=color) ax.legend(loc='upper left') ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) fig.autofmt_xdate() fig.tight_layout() return fig
def history(results, key='job', limit=None): """Plots run time history of all results. Depending on the argument `job`, The results are either ordered by runtime (i.e. commit/build time) or job time (i.e. when the job was run). Args: results: List of `result.Result` objects. key: Either 'job' or 'runtime'. limit: Optionally limits the number of plotted results to the given number, i.e. only displays the most recent results. If `None`, all given results are plotted. """ # get date/time either from the runtime (commit/build) or job (when job # was run) def get_datetime(result): if key == 'runtime': datetime = result.runtime.datetime elif key == 'job': datetime = result.datetime else: raise ArgumentError('"key" argument must be "runtime" or "job"') return time.local_time(datetime) # sort results by desired reference time results = sorted(results, key=get_datetime) # if limit is not None: if not isinstance(limit, int) or limit <= 0: raise ArgumentError('"limit" must be a positive integer') results = results[-limit:] data = result.percentiles_by_stencil(results, [0, 25, 50, 75, 100]) dates = [matplotlib.dates.date2num(get_datetime(r)) for r in results] if len(dates) > len(set(dates)): logger.warning('Non-unique datetimes in history plot') fig, ax = plt.subplots(figsize=figsize(2, 1)) locator = matplotlib.dates.AutoDateLocator() formatter = matplotlib.dates.AutoDateFormatter(locator) formatter.scaled[1 / 24] = '%y-%m-%d %H:%M' formatter.scaled[1 / (24 * 60)] = '%y-%m-%d %H:%M' formatter.scaled[1 / (24 * 60 * 60)] = '%y-%m-%d %H:%M:%S' ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) colors = discrete_colors(len(data[0])) for color, stencil, mint, q1, q2, q3, maxt in zip(colors, *data): ax.fill_between(dates, mint, maxt, alpha=0.2, color=color) ax.fill_between(dates, q1, q3, alpha=0.5, color=color) ax.plot(dates, q2, '|-', label=stencil.title(), color=color) ax.legend(loc='upper left') fig.autofmt_xdate() fig.tight_layout() return fig