def dump_times(): f.r.total = f.t.tmp_total - f.r.self_agg # (have already subtracted self_cut) f.r.stamps_sum = sum([v for v in itervalues(f.s.cum)]) f.r.self_agg += f.t.self_cut # (now add self_cut including self time of stop()) if f.s.itrs: for s, itr_list in iteritems(f.s.itrs): f.s.itr_max[s] = max(itr_list) nonzero_itrs = list(filter(lambda x: x > 0., itr_list)) f.s.itr_num[s] = len(nonzero_itrs) if f.s.itr_num[s] > 0: f.s.itr_min[s] = min(nonzero_itrs) else: f.s.itr_min[s] = 0. for s, val in iteritems(f.s.cum): # (for saving stamps_as_itr) if s not in f.s.itr_num: f.s.itr_num[s] = 1 f.s.itr_max[s] = val f.s.itr_min[s] = val merge_t = 0. if f.t.dump is not None: t = timer() merge.merge_times(f.t.dump, f.r) merge_t += timer() - t f.t.dump.self_agg += merge_t # Must aggregate up self time only in the case of named loop, because it # dumps directly to an already assigned subdivision, whereas normally self # time aggregates during subdivision assignment. if f.t.is_named_loop: f.r.parent.self_agg += f.r.self_agg + merge_t
def _assign_par_subdvsn(position): new_pos = position not in f.r.par_subdvsn and f.t.par_subdvsn_awaiting if new_pos: f.r.par_subdvsn[position] = dict() for par_name, sub_list in iteritems(f.t.par_subdvsn_awaiting): for sub_times in sub_list: sub_times.pos_in_parent = position f.r.par_subdvsn[position][par_name] = sub_list else: for par_name, sub_list in iteritems(f.t.par_subdvsn_awaiting): if par_name not in f.r.par_subdvsn[position]: for sub_times in sub_list: sub_times.pos_in_parent = position f.r.par_subdvsn[position][par_name] = sub_list else: for sub_times in sub_list: is_prev_sub = False for old_sub in f.r.par_subdvsn[position][par_name]: if old_sub.name == sub_times.name: is_prev_sub = True break if is_prev_sub: merge.merge_times(old_sub, sub_times) else: sub_times.pos_in_parent = position f.r.par_subdvsn[position][par_name].append(sub_times)
def _build_master_single(master, times, index, num_times): for stamp, val in iteritems(times.stamps.cum): if stamp not in master.stamps: master.stamps[stamp] = [''] * num_times master.stamps[stamp][index] = val for stamp, sub_list in iteritems(times.subdvsn): if stamp not in master.subdvsn: master.subdvsn[stamp] = list() for sub_times in sub_list: is_new = True for master_sub in master.subdvsn[stamp]: if master_sub.name == sub_times.name: is_new = False break if is_new: master_sub = CompareTimes(name=sub_times.name, parent=master) master.subdvsn[stamp].append(master_sub) _build_master_single(master_sub, sub_times, index, num_times) for stamp, par_dict in iteritems(times.par_subdvsn): if stamp not in master.par_subdvsn: master.par_subdvsn[stamp] = dict() for par_name, par_list in iteritems(par_dict): if par_name not in master.par_subdvsn[stamp]: master_sub = CompareTimes(name=par_name, parent=master) master.par_subdvsn[stamp][par_name] = master_sub master_sub = master.par_subdvsn[stamp][par_name] sub_with_max_tot = max(par_list, key=lambda x: x.total) _build_master_single(master_sub, sub_with_max_tot, index, num_times)
def _merge_par_subdivisions(rcvr, new): for sub_pos, par_dict in iteritems(new.par_subdvsn): if sub_pos in rcvr.par_subdvsn: for par_name, new_list in iteritems(par_dict): if par_name in rcvr.par_subdvsn[sub_pos]: rcvr_list = rcvr.par_subdvsn[sub_pos][par_name] add_list = [] # to avoid writing to loop iterate for new_sub in new_list: for rcvr_sub in rcvr_list: if rcvr_sub.name == new_sub.name: merge_times(rcvr_sub, new_sub) break else: new_sub.parent = rcvr new_sub.par_in_parent = True add_list.append(new_sub) rcvr.par_subdvsn[sub_pos][par_name] += add_list else: for new_sub in new_list: new_sub.parent = rcvr new_sub.par_in_parent = True rcvr.par_subdvsn[sub_pos][par_name] = new_list else: for par_name, par_list in iteritems(par_dict): for new_sub in par_list: new_sub.parent = rcvr new_sub.par_in_parent = True rcvr.par_subdvsn[sub_pos] = par_dict new.par_subdvsn.clear()
def _stamps_as_itr(rcvr, new): for k, v in iteritems(new.cum): if new.itr_num[k] == 1: # (then it won't be in new.itrs) if k in rcvr.itrs: rcvr.itrs[k].append(v) elif k in rcvr.cum: rcvr.itrs[k] = [rcvr.cum[k], v]
def _report_itr_stats(times, delim_mode): FMT = FMTS_RPT['Stats'] # FMT1 = FMTS_RPT['Stamps'] rep = '' rep += "\n" + FMT['STMP'].format('') headers = ['Total', 'Mean', 'Max', 'Min', 'Num'] for hdr in headers: rep += FMT['HDR'].format(hdr) if not delim_mode: rep += FMT['STMP'].format('') for _ in range(len(headers)): rep += FMT['HDR'].format('------') stamps = times.stamps for s, num in iteritems(stamps.itr_num): if num > 1: rep += FMT['STMP'].format(s) values = [ stamps.cum[s], stamps.cum[s] / stamps.itr_num[s], stamps.itr_max[s], stamps.itr_min[s] ] for val in values: rep += FMT['FLT'].format(val) rep += FMT['INT'].format(stamps.itr_num[s]) rep += "\n" return rep
def _merge_dict_itr(rcvr, new, attr, itr_func): rcvr_dict = getattr(rcvr, attr) new_dict = getattr(new, attr) for k, v in iteritems(new_dict): if k in rcvr_dict: rcvr_dict[k] = itr_func(v, rcvr_dict[k]) else: rcvr_dict[k] = v
def _merge_dict(rcvr, new, attr): rcvr_dict = getattr(rcvr, attr) new_dict = getattr(new, attr) for k, v in iteritems(new_dict): if k in rcvr_dict: rcvr_dict[k] += v else: rcvr_dict[k] = v
def _populate_compare_stats(master): for stamp, values in iteritems(master.stamps): master.stats[stamp] = _compute_stats(values) for sub_list in itervalues(master.subdvsn): for master_sub in sub_list: _populate_compare_stats(master_sub) for sub_dict in itervalues(master.par_subdvsn): for master_sub in itervalues(sub_dict): _populate_compare_stats(master_sub)
def _report_par_sub_times(par_subdvsn, indent): FMT = FMTS_RPT['Stamps'] rep = '' for par_name, par_list in iteritems(par_subdvsn): sub_with_max_tot = max(par_list, key=lambda x: x.total) rep += "\n{}{}".format(FMT['IDT_SYM'] * (indent + 1), par_name + FMT['PAR']) rep += _report_stamps(sub_with_max_tot, indent + 1, par=True) return rep
def loop_end(loop_end_stamp=None, end_stamp_unique=True, keep_subdivisions=True, quick_print=False): if f.t.stopped: raise StoppedError("Timer already stopped at end of loop iteration.") if f.t.paused: raise PausedError("Timer paused at end of loop iteration.") if loop_end_stamp is not None: timer_pub.stamp(loop_end_stamp, un=end_stamp_unique, ks=keep_subdivisions, qp=quick_print) t = timer() else: t = timer() f.t.last_t = t times_priv.assign_subdivisions(UNASGN, keep_subdivisions) # Prevserve the ordering of stamp names as much as possible, wait until # after first pass to initialize any unused registered stamps. if f.lp.first_itr: f.lp.first_itr = False for s in f.lp.rgstr_stamps: if s not in f.lp.stamps: timer_pub._init_loop_stamp(s) for s, used in iteritems(f.lp.itr_stamp_used): if used: val = f.lp.itr_stamps[s] f.s.cum[s] += val if f.lp.save_itrs: f.s.itrs[s].append(val) else: f.s.itr_num[s] += 1 if val > f.s.itr_max[s]: f.s.itr_max[s] = val if val < f.s.itr_min[s]: f.s.itr_min[s] = val elif f.lp.save_itrs and s in f.lp.rgstr_stamps: f.s.itrs[s].append(0.) if f.lp.name is not None: # Reach back and stamp in the parent timer. elapsed = t - f.tm1.last_t f.sm1.cum[f.lp.name] += elapsed if f.lp.save_itrs: f.sm1.itrs[f.lp.name].append(elapsed) else: f.sm1.itr_num[f.lp.name] += 1 if elapsed > f.sm1.itr_max[f.lp.name]: f.sm1.itr_max[f.lp.name] = elapsed if elapsed < f.sm1.itr_min[f.lp.name]: f.sm1.itr_min[f.lp.name] = elapsed f.tm1.last_t = t if quick_print: print("({}) {}: {:.4f}".format(f.tm1.name, f.lp.name, elapsed)) f.t.self_cut += timer() - t
def _merge_subdivisions(rcvr, new): for sub_pos, new_sub_list in iteritems(new.subdvsn): if sub_pos in rcvr.subdvsn: add_list = [] # to avoid writing to loop iterate for new_sub in new_sub_list: for rcvr_sub in rcvr.subdvsn[sub_pos]: if rcvr_sub.name == new_sub.name: merge_times(rcvr_sub, new_sub) break else: new_sub.parent = rcvr add_list.append(new_sub) rcvr.subdvsn[sub_pos] += add_list else: for sub in new_sub_list: sub.parent = rcvr rcvr.subdvsn[sub_pos] = new_sub_list # Clean up references to old data as we go (not sure if helpful?). new.subdvsn.clear()
def _compare_stamps(master, indent=0, stats_mode=False): if stats_mode: FMT = FMTS_CMP['Stats'] loop_dict = master.stats else: FMT = FMTS_CMP['List'] loop_dict = master.stamps rep = '' for stamp, values in iteritems(loop_dict): rep += FMT['NAME'].format(FMT['IDT_SYM'] * indent, stamp + FMT['APND']) if stats_mode: num = values.num values = [values.max, values.min, values.avg, values.std] for val in values: if val: rep += FMT['FLT'].format(val) else: rep += FMT['BLNK'] if stats_mode: rep += FMT['INT'].format(num) if stamp in master.subdvsn: for master_sub in master.subdvsn[stamp]: rep += _compare_stamps(master_sub, indent + 1, stats_mode) if stamp in master.par_subdvsn: for _, master_sub in master.par_subdvsn[stamp]: rep += _compare_stamps(master_sub, indent + 1, stats_mode) if UNASGN in master.subdvsn: rep += "\n{}{}".format(FMT['IDT_SYM'] * indent, UNASGN) for master_sub in master.subdvsn[UNASGN]: rep += _compare_stamps(master_sub, indent + 1, stats_mode) if UNASGN in master.par_subdvsn: if UNASGN not in master.subdvsn: rep += "\n{}{}".format(FMT['IDT_SYM'] * indent, UNASGN) for _, master_sub in master.par_subdvsn[UNASGN]: rep += _compare_stamps(master_sub, indent + 1, stats_mode) return rep
def compare(times_list=None, name=None, include_list=True, include_stats=True, delim_mode=False, format_options=None): """ Produce a formatted comparison of timing datas. Notes: If no times_list is provided, produces comparison reports on all parallel subdivisions present at the root level of the current timer. To compare parallel subdivisions at a lower level, get the times data, navigate within it to the parallel list of interest, and provide that as input here. As with report(), any further parallel subdivisions encountered have only their member with the greatest total time reported on (no branching). Args: times_list (Times, optional): list or tuple of Times objects. If not provided, uses current root timer. name (any, optional): Identifier, passed through str(). include_list (bool, optional): Display stamps hierarchy. include_stats (bool, optional): Display stamp comparison statistics. delim_mode (bool, optional): If True, format for spreadsheet. format_options (None, optional): Formatting options, see below. Formatting Keywords & Defaults: Human-readable Mode - 'stamp_name_width': 18 - 'list_column_width': 12 - 'list_tab_width': 2 - 'stat_column_width': 8 - 'stat_tab_width': 2 - 'indent_symbol: ' ' (one space) Delimited Mode - 'delimiter': '\t' (tab) - 'ident_symbol': '+' Returns: str: Times data comparison as formatted string. Raises: TypeError: If any element of provided collection is not a Times object. """ if times_list is None: rep = '' for par_dict in itervalues(f.root.times.par_subdvsn): for par_name, par_list in iteritems(par_dict): rep += report_loc.compare(par_list, par_name, include_list, include_stats, delim_mode, format_options) else: if not isinstance(times_list, (list, tuple)): raise TypeError( "Expected a list/tuple of times instances for param 'times_list'." ) if not all([isinstance(times, Times) for times in times_list]): raise TypeError( "At least one member of param 'times_list' is not a Times object." ) rep = report_loc.compare(times_list, name, include_list, include_stats, delim_mode, format_options) return rep