def run_trial(trial, tests_per_trial, exe_dir, out_dir, max_rsd, keep_best_test_only, convert_traces, skeleton_only): import online_math test_output_re = re.compile(test_output_pattern, flags=re.DOTALL) # abbrevs exe = trial.exe N = trial.N cores = trial.cores NB = trial.NB IB = trial.IB sched = trial.sched if NB < 100: max_rsd += 1 if NB < 80: max_rsd += 2 if NB < 60: max_rsd += 4 # counters and loop variables test_num = 0 stddev_fails = 0 trial_finished = False extra_tests = [] while not trial_finished: test_attempts = 0 while test_num < tests_per_trial + stddev_fails: if test_attempts > max_test_failures: test_num += 1 test_attempts = 0 print('Failed this test too many times. Moving on...') continue print( "%s for %dx%d matrix on %d cores, NB = %d, IB = %d; sched = %s Xargs = %s trial #%d" % (exe, N, N, cores, NB, IB, sched, str( trial.extra_args), test_num)) cmd, args = trial.generate_cmd() proc = subprocess.Popen([exe_dir + os.sep + cmd] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # RUN (stdout, stderr) = proc.communicate() # if len(stderr) != 0: # marker = randint(0, 99999) # print("AN ERROR OCCURRED (random id: %d)" % marker) # sys.stderr.write(str(marker) + ':\n' + stderr + '\n') match = test_output_re.match(stdout) trace_filenames = glob.glob('testing_' + exe + '*.prof-*') if match: # save successfully-parsed output time = float(match.group(1)) perf = float(match.group(2)) extra_output = match.group(3) print(" -----> gflops: %f time: %f NB:%d" % (perf, time, NB)) test = ParsecTest(trial.ident, exe, N, cores, NB, IB, sched, perf, time, test_num) test.extra_output = extra_output if not os.environ.get('SUPPRESS_EXTRA_OUTPUT', None): sys.stdout.write(extra_output) # rename trace, if it exists if len(trace_filenames) > 0: moved_trace_filenames = list() for filename in trace_filenames: trace_filename = filename.replace( 'testing_' + exe, out_dir + os.sep + test.unique_name()) # print('moving {} to {}'.format(filename, trace_filename)) print('moving', filename, 'to', trace_filename) shutil.move(filename, trace_filename) moved_trace_filenames.append(trace_filename) trace_filenames = moved_trace_filenames trial.append((test, trace_filenames)) test_num += 1 # no more attempts are needed - we got what we came for else: safe_unlink(trace_filenames) sys.stderr.write("results not properly parsed: %s\n" % stdout) print('\nWe\'ll just try this one again.\n') if tests_per_trial > 1: print('finished all {} tests of this trial'.format(len(trial))) # done with trial. now calculate trial statistics test_perfs = [] test_times = [] for test, trace_filenames in trial: test_times.append(test.time) test_perfs.append(test.perf) variance, avgPerf = online_math.online_variance_mean(test_perfs) perf_stddev = variance**0.5 rsd = trial.percent_sdv(perf_stddev, avgPerf) # now check whether our results are clean/good if rsd <= max_rsd or keep_best_test_only: # clean - save and print! trial.perf_sdv = perf_stddev trial.perf_avg = avgPerf variance, trial.time_avg = online_math.online_variance_mean( test_times) trial.time_sdv = variance**0.5 print(trial) # realtime progress report for test, trace_filenames in extra_tests: safe_unlink(trace_filenames) # these won't be needed anymore if keep_best_test_only: best_perf = 0 best_index = 0 for index, (test, trace_filenames) in enumerate(trial): if test.perf > best_perf: best_perf = test.perf best_index = index print( 'Only keeping the trace of the test with the best performance' + ' ({} gflops/s), at index {}.'.format( best_perf, best_index)) new_list = list() for index, (test, trace_filenames) in enumerate(trial): if index != best_index: safe_unlink(trace_filenames ) # remove traces of 'not best' runs new_list.append((test, list())) else: new_list.append((test, trace_filenames)) del trial[:] trial.extend(new_list) if convert_traces: # iterate through the list, convert the traces, and save the new names new_list = list() while len(trial) > 0: test, trace_filenames = trial.pop() print('converting', trace_filenames) if len(trace_filenames) > 0: try: import pbt2ptt add_info = add_info_to_trace(trial) trace_filenames = [ pbt2ptt.convert(trace_filenames, unlink=True, add_info=add_info, skeleton_only=skeleton_only) ] print('converted filename is', trace_filenames[0]) if skeleton_only: try: os.system( 'ptrepack --chunkshape=auto --propindexes ' + '--complevel=5 --complib=blosc ' + '{} {}'.format( trace_filenames[0], trace_filenames[0] + '.tmp')) shutil.move(trace_filenames[0] + '.tmp', trace_filenames[0]) except Exception as e: print(e) print('ptrepack utility not available.') new_list.append((test, trace_filenames)) except ImportError as ie: print(ie) print( 'Cannot convert. pbt2ptt module is unavailable.' ) new_list.append((test, trace_filenames)) pass # can't convert without the module... ahh well else: new_list.append((test, trace_filenames)) trial.extend(new_list) # put everything back in the trial while not trial_finished: # safe against Keyboard Interrupt try: pfilename = out_dir + os.sep + trial.unique_name( ) + '.trial' pfile = open(pfilename, 'w') trial.pickle(pfile) # move 'pending' to 'rerun' in case a later re-run of the entire group is necessary if 'pending.' in trial.filename: rerun = trial.filename.replace('pending.', 'rerun.') if os.path.exists(trial.filename): shutil.move(trial.filename, rerun) trial_finished = True # safe_unlink([out_dir + os.sep + # test.unique_name() + '.test' # for test in trial], # report_error=False) except KeyboardInterrupt: print('Currently writing files. Cannot interrupt.') elif stddev_fails < max_stddev_fails: # no good, try again from beginning stddev_fails += 1 test_num = 0 extra_tests.extend(trial[:]) del trial[:] # retry with a clean trial, in case there was interference print('WARNING: this trial has a large relative ' + 'standard deviation ({}%), and will be redone.'.format(rsd)) else: # no good.... but we've tried too many times :( # (so let's just use all of our many results, and label the trial with a warning) trial.extend(extra_tests) test_perfs = [] test_times = [] for test, trace_filenames in trial: test_times.append(test.time) test_perfs.append(test.perf) variance, avgPerf = online_math.online_variance_mean(test_perfs) perf_stddev = variance**0.5 trial.perf_sdv = perf_stddev trial.perf_avg = avgPerf variance, trial.time_avg = online_math.online_variance_mean( test_times) trial.time_sdv = variance**0.5 wfile = open(out_dir + os.sep + trial.unique_name() + '.warn', 'w') trial.pickle(wfile) # leave the pending pickle so it can be easily identified # as never having completed later on trial_finished = True # be done. return trial
import ptt_utils import pbt2ptt import argparse if __name__ == '__main__': parser = argparse.ArgumentParser(description="Convert a set of PaRSEC Binary Profile files into an HDF5 file") parser.add_argument('--output', dest='output', help='Output file name') parser.add_argument('--report-progress', dest='report_progress', action='store_const', const=True, default=False, help='Report progress of conversion to stderr') parser.add_argument('--single-process', dest='multiprocess', action='store_const', const=False, default=True, help='Deactivate multiprocess parallelism') parser.add_argument('inputs', metavar='INPUT', type=str, nargs='+', help='PaRSEC Binary Profile Input files') args = parser.parse_args() if args.output is None: groups = ptt_utils.group_trace_filenames(args.inputs) for f in groups: print("Processing {}".format(f)) name = pbt2ptt.convert(f, multiprocess=args.multiprocess, report_progress=args.report_progress) print("Generated: {}".format(name)) else: f = args.inputs print("Processing {}".format(f)) name = pbt2ptt.convert(f, multiprocess=args.multiprocess, report_progress=args.report_progress, out=args.output) print("Generated {}".format(name)) sys.exit(0)
def scatter_papi(filenames, units, unit_modify): with Timer() as main: # The import of matplotlib is timed because it takes a surprisingly long time. with Timer() as t: import matplotlib matplotlib.use('Agg') # For use with headless systems import matplotlib.pyplot as plt import matplotlib.cm as cm print('Importing matplotlib took {} seconds.\n'.format(t.interval)) trace = None # This is for loading and possibly converting trace files. with Timer() as t: if len(filenames) == 1 and (ptt.is_ptt(filenames[0])): print('Loading the HDFed trace...') else: print('Converting binary trace to the ParSEC Trace Tables format...') filenames[0] = pbt2ptt.convert(filenames, report_progress=True) print('Loading the HDFed trace...') trace = ptt.from_hdf(filenames[0]) print('The load took {} seconds.'.format(t.interval)) print('') # The column_data list will store subsets of the pandas dataframes for the # columns containing PAPI counter information. The column_names list stores # the names corresponding to those columns. column_data = [] column_names = [] print('Populating user-defined lists...') with Timer() as t: # We start from the beginning, which is where the PAPI event columns will be. for i in range(0,len(trace.events.columns.values)): column_name = trace.events.columns.values[i] # If we hit the 'begin' column, there aren't any more PAPI event columns. if column_name == 'begin': break if '_start' in column_name: continue column_names.append(column_name) # We only care about the data in this column for which there is data. # Note: column_data actually stores all of the rows for which the column # of interest is not NULL. column_data.append(trace.events[:][trace.events[column_name].notnull()]) print('Populating the lists took {} seconds.\n'.format(t.interval)) # Find the maximum y value max_count = 0 max_counts = [] for i in range(0, len(column_data)): temp = column_data[i][:][column_names[i]].values.tolist() max_counts.append(0) for val in temp: if(val > max_count): max_count = val if(val > max_counts[i]): max_counts[i] = val # Determine the maximum number of colors that we would need for one of the graphs. colors_needed = 0 for event_name in trace.event_names: if event_name.startswith('PINS_PAPI'): colors_needed += 1 print('Colors Needed: ' + str(colors_needed)) # Start the plot of the figure with a relatively large size. fig = plt.figure(num=None, figsize=(12, 9), dpi=80, facecolor='w', edgecolor='k') # Start the color iterator so we can plot each column in its own color. colors = iter(cm.prism(np.linspace(0, 1, colors_needed))) print('Plotting all PAPI counters together...') with Timer() as t: for i in range(0, len(column_data)): # This is done in 4 lines instead of two due to an issue with multiplying unit_modify temp = column_data[i]['begin'] * unit_modify tempX = temp.values.tolist() temp = column_data[i]['end'] * unit_modify # We should now have all of the 'x' values (time) tempX.extend(temp.values.tolist()) tempY = column_data[i][:][column_names[i] + '_start'].values.tolist() #end_index = len(tempY) # We should now have all of the 'y' values (count) tempY.extend(column_data[i][:][column_names[i]].values.tolist()) adjust_factor = 1000 adjust_power = 3 while max_count > adjust_factor: adjust_factor *= 1000 adjust_power += 3 adjust_factor /= 1000 adjust_power -= 3 if(adjust_factor > 1): for v in range(0, len(tempY)): tempY[v] /= adjust_factor #localize(tempY) # Note: The values in tempX and tempY are stored with the first half of the array being # the '_start' values and the second half being the 'end' values, so they match up # properly, however a line plot would look very odd because these values should # actually be interleaved. plt.scatter(tempX, tempY, color = next(colors), label = column_names[i]) #if(tempY[-1] > max_count): # max_count = tempY[-1] plt.title('All PAPI Counters') plt.ylim(ymin = 0, ymax = (max_count / adjust_factor) * 1.1) plt.xlim(xmin = 0) if(adjust_power > 0): plt.ylabel('Count (Times 10^' + str(adjust_power) + ')') else: plt.ylabel('Count') if units != 'c': plt.xlabel('Time (' + units + ')') else: plt.xlabel('Cycles') plt.legend(loc='upper left') plt.show() print('Saving plot as all_papi_counters.png...') fig.savefig('all_papi_counters.png') print('Plotting and saving took {} seconds.'.format(t.interval)) # Each iteration will plot a different individual counter as its own plot. for i in range(0, len(column_data)): with Timer() as t: print('Plotting data for: ' + column_names[i] + '...') fig = plt.figure(num=None, figsize=(12, 9), dpi=80, facecolor='w', edgecolor='k') # Restart the colors iterator colors = iter(cm.prism(np.linspace(0, 1, colors_needed))) # Plot each non-empty subset of this counter by 'type'. This typically means # the counters that occurred on each core are grouped together. for n in range(0, len(trace.event_names)-1): if trace.event_names[n].startswith('PINS_PAPI'): temp = column_data[i][:][column_data[i]['type'] == n]['begin'] * unit_modify if len(temp) > 0: tempX = temp.values.tolist() temp = column_data[i][:][column_data[i]['type'] == n]['end'] * unit_modify tempX.extend(temp.values.tolist()) tempY = column_data[i][:][column_data[i]['type'] == n][column_names[i] + '_start'].values.tolist() tempY.extend(column_data[i][:][column_data[i]['type'] == n][column_names[i]].values.tolist()) adjust_factor = 1000 adjust_power = 3 while max_counts[i] > adjust_factor: adjust_factor *= 1000 adjust_power += 3 adjust_factor /= 1000 adjust_power -= 3 if(adjust_factor > 1): for v in range(0, len(tempY)): tempY[v] /= adjust_factor #localize(tempY) temp_color = next(colors) plt.scatter(tempX, tempY, color = temp_color,\ label = trace.event_names[n].replace('PINS_PAPI_', '')) # The following will do line plots #plt.scatter(tempX, tempY, color = temp_color) #plt.plot(tempX, tempY, color = temp_color,\ # linestyle = '-', label = trace.event_names[n].replace('PINS_PAPI_', '')) plt.title(column_names[i]) plt.ylim(ymin = 0, ymax = (max_counts[i] / adjust_factor) * 1.1) plt.xlim(xmin = 0) if(adjust_power > 0): plt.ylabel('Count (Times 10^' + str(adjust_power) + ')') else: plt.ylabel('Count') if units != 'c': plt.xlabel('Time (' + units + ')') else: plt.xlabel('Cycles') plt.legend(loc='upper left') plt.show() fig.savefig(column_names[i] + '.png') print('Saving plot as ' + column_names[i] + '.png...') print('Plotting and saving took {} seconds.'.format(t.interval)) print('Total Time: {} seconds\n'.format(main.interval))
#!/usr/bin/env python from __future__ import print_function import sys import ptt_utils import pbt2ptt if __name__ == '__main__': if len(sys.argv[1:]) == 0: print("Usage: %s profile-file1 profile-file2 ..." % (sys.argv[0]), file=sys.stderr) sys.exit(1) groups = ptt_utils.group_trace_filenames(sys.argv[1:]) for f in groups: print("Processing %s" % f) name = pbt2ptt.convert(f) print("Generated: %s" % (name)) sys.exit(0)
def do_demo(filenames, translate=False): with Timer() as main: trace = None with Timer() as t: if len(filenames) == 1 and (ptt.is_ptt(filenames[0])): print('First, we load the HDFed trace...') else: print( 'First, we read the binary trace and convert it to the ParSEC Trace Tables format.' ) filenames[0] = pbt2ptt.convert(filenames, report_progress=True) print('Then, we read the HDFed trace...') trace = ptt.from_hdf(filenames[0]) print('The load took {} seconds.'.format(t.interval)) print('') print('First, let\'s print some basic information about the run.\n') print('Most PaRSEC traces are traces of testing executables, and') print( 'these runs tend to have some basic linear algebra attributes, such as matrix size.' ) print( 'If the trace contains these sorts of attributes, they will print below:\n' ) try: print( 'N: {} M: {} NB: {} MB: {} gflops: {} time elapsed: {} scheduler: {}\n' .format(trace.N, trace.M, trace.NB, trace.MB, trace.gflops, trace.time_elapsed, trace.sched)) except AttributeError as e: print(e) print( 'It appears that one or more of the basic attributes was not present,' ) print('so we\'ll just move on.\n') print( 'The bulk of the trace information is stored in a data structure called a DataFrame.' ) print('A DataFrame is a large matrix/table with labeled columns.\n') print('One of our trace\'s DataFrames contains all of the "events".') print('Each event in our trace is one row in the events DataFrame,') print( 'and some events have different pieces of information than others.\n' ) print( 'The columns of the DataFrame (or data labels) and their datatypes are:' ) print(trace.events.dtypes) print('') print( 'Now, we can print some statistics about the *shared* columns of the events.' ) print('###################################################\n') # Note trace.events.loc[:,'begin':] returns the information from columns 'begin' to the last column with Timer() as t: print(trace.events.loc[:, 'begin':]) print('There are ' + str(len(trace.events)) + ' events in this trace', end=' ') print('and they took {} seconds to describe.'.format(t.interval)) print('###################################################\n\n') print('') print('') user_columns = [] for column_name in trace.events.columns.values: if column_name not in trace.events.loc[:, 'begin':]: user_columns.append(column_name) print('Here are some statistics on the unique, non-shared columns:') print('###################################################\n') with Timer() as t: print(trace.events[user_columns].describe()) print('There are ' + str(len(trace.events)) + ' events in this trace', end=' ') print('and they took {} seconds to describe.'.format(t.interval)) print('###################################################\n\n') print('') # Set this to a number to specify the compression level (e.x. 1) clevel = None if clevel: print('Compression Test:') print('###################################################\n') print('Testing re-store of events as a compressed HDF5') with Timer() as t: trace.events.to_hdf('test_compressed_events.hdf5', 'events', complevel=clevel, complib='blosc') print( 'took {} to write only the events to HDF5, compression level {}\n' .format(t.interval, clevel)) print('') print('Testing re-store as a Table HDF5') with Timer() as t: trace.to_hdf('test_table.hdf5', table=True, append=False) print('took {} to write the HDF5 table\n'.format(t.interval)) print('') print('Testing re-store as Storer HDF5') with Timer() as t: trace.to_hdf('test_storer.hdf5', table=False, append=False) print('took {} to write the HDF5 storer\n'.format(t.interval)) print('') print('Testing re-store as HDF5_Store') with Timer() as t: new_store = pandas.HDFStore('test_events.hdf5_store', 'w', complevel=clevel, complib='blosc') new_store.put('events', trace.events, table=False) print( 'took {} to PUT only the events to HDF5, compression level {}\n' .format(t.interval, clevel)) print('') print('Testing read from compressed HDF5') with Timer() as t: trace.events = pandas.read_hdf('test_compressed_events.hdf5', 'events') print(trace.events[trace.events.loc[:, 'begin':]].describe()) print( 'There are ' + str(len(trace.events)) + ' events in this trace', 'and they took {} seconds to read & describe.'.format( t.interval)) print('') print('Testing read from Table HDF5') with Timer() as t: trace = ptt.from_hdf('test_table.hdf5') print( 'There are ' + str(len(trace.events)) + ' events in this trace', 'and they took {} seconds to read'.format(t.interval)) print('') print('Testing write to CSV (with compression)...') with Timer() as t: trace.events.to_csv('test.csv', complevel=clevel, complib='blosc') print('took {} to write to csv, clevel {}'.format( t.interval, clevel)) print('###################################################\n\n') print( 'Now, we will select only the PINS_PAPI* events via a simple operation (only the first will be printed).' ) print('###################################################\n') for event_name in trace.event_types.keys(): if event_name.startswith('PINS_PAPI'): print('\nFound: ' + event_name) print('---------------------------------------------------\n') onlyexec = trace.events[:][trace.events['type'] == trace.event_types[event_name]] print(onlyexec.describe()) break # Removing this break will print all of the PINS_PAPI* events' descriptions print('###################################################\n\n') onlyexec = trace.events[:][trace.events['type'] == trace.event_types[event_name]] print('') print('Now, we will select only the {} events from thread 0.'.format( event_name)) print( 'We will also pick only certain pieces of the statistics to show, using the same' ) print( 'syntax that is used to pick rows out of any regular DataFrame.\n') onlyexec = onlyexec[:][onlyexec.stream_id == 0] if len(onlyexec) == 0: print( 'Unfortunately the {} event doesn\'t have any events for thread 0,' .format(event_name)) print('so the following outputs will be rather dull...\n') print('Again, our view of the dataframe has changed:') print('###################################################\n') print(onlyexec.describe()[:]['count':'std']) print('###################################################\n\n') print('') print( 'It is also possible to perform both operations in one query, like so:' ) onlyexec = trace.events[:][ (trace.events['type'] == trace.event_types[event_name]) & (trace.events.stream_id == 0)] print( 'Note that the description is the same as for the previous subset.' ) print('###################################################\n') print(onlyexec.describe()[:]['count':'std']) print('###################################################\n\n') print('') print( 'Now, a simple sort of {} events from thread 0 by duration, in ascending order.' .format(event_name)) with Timer() as t: onlyexec['duration'] = pandas.Series(onlyexec['end'] - onlyexec['begin']) srted = onlyexec.sort_index(by=['duration'], ascending=[True]) print('That sort only took ' + str(t.interval) + ' seconds.') print('Here is the sorted list:') print('###################################################\n') print(srted) print('###################################################\n\n') print('Overall demo took {:.2f} seconds'.format(main.interval))
def autoload_traces(filenames, convert=True, unlink=False, enhance_filenames=False, skeleton_only=False, report_progress=True, force_reconvert=False, multiprocess=True): """ Preferred interface for most attempts to load PTTs from the filesystem. Whether from PTT, PBT, or a combination of the two, you should be able to throw a huge, messy list of filenames at this function and receive a bunch of coherent PTT traces back. Give it a whirl, and be sure to report any bugs! """ pbt_groups = group_trace_filenames(filenames) ptts = list() # convert or separate into PTTs and PBTs if convert: # then turn everything into a PTT for fn_group in pbt_groups[:]: if len(fn_group) == 1 and ptt.is_ptt(fn_group[0]): ptts.append(fn_group[0]) pbt_groups.remove(fn_group) else: import pbt2ptt # do convert on all -- already-converted filenames will simply be returned converted_filename = pbt2ptt.convert( fn_group, unlink=unlink, report_progress=report_progress, force_reconvert=force_reconvert, multiprocess=multiprocess) ptts.append(converted_filename) pbt_groups.remove(fn_group) else: # separate into already-PTTs and PBTs for fn_group in pbt_groups[:]: ptt_name = ptt.ptt_name(fn_group[0]) h5_conflicts = find_h5_conflicts(fn_group) if ptt.is_ptt(fn_group[0]): ptts.append(fn_group) pbt_groups.remove(fn_group) elif os.path.exists( ptt_name ): # passed a PBT name, but previous conversion exists ptts.append([ptt_name]) pbt_groups.remove(fn_group) elif len(h5_conflicts) > 0: ptts.append([h5_conflicts[0]]) pbt_groups.remove(fn_group) # LOAD PTTs if len(ptts) > 0: # prepare to multithread the loads if multiprocess: pool = multiprocessing.Pool(multiprocessing.cpu_count()) else: pool = multiprocessing.Pool(1) if report_progress: print('loading PTTs...') partial_from_hdf = functools.partial(ptt.from_hdf, skeleton_only=skeleton_only) # do load traces = pool.map(partial_from_hdf, ptts) if report_progress: print('loaded all PTTs.') else: traces = list() # LOAD PBTs for group in pbt_groups: import pbt2ptt # don't do this if not necessary if report_progress: print('loading PBT group {}'.format(group)) trace = pbt2ptt.read(group, skeleton_only=skeleton_only, multiprocess=multiprocess, report_progress=report_progress) traces.append(trace) return traces
convert = False args.remove('--no-convert') if '--unlink' in args: unlink = True args.remove('--unlink') if len(args) > 0: name_infos = args else: name_infos = default_name_infos processed_filename_groups = group_trace_filenames(filenames) # this is commented out because I decided not to enhance the filenames of the # binary trace files by default anymore. It tended to confuse matters, # and it inadvertently encourages continued use of those binary traces. # processed_filename_groups = preprocess_traces(filenames, dry_run=dry_run, # enhance_filenames=False, # force_enhance=force_enhance, # name_infos=name_infos) if convert: for fn_group in processed_filename_groups: import pbt2ptt fn_group = pbt2ptt.convert(fn_group, unlink=unlink, report_progress=True, force_reconvert=False, multiprocess=True)
def scatter_papi(filenames, units, unit_modify, args): with Timer() as main: # The import of matplotlib is timed because it takes a surprisingly long time. with Timer() as t: import matplotlib matplotlib.use('Agg') # For use with headless systems import matplotlib.pyplot as plt import matplotlib.cm as cm #print('Importing matplotlib took {} seconds.\n'.format(t.interval)) trace = None # This is for loading and possibly converting trace files. with Timer() as t: if len(filenames) == 1 and (ptt.is_ptt(filenames[0])): print('Loading the HDFed trace...') else: print( 'Converting binary trace to the ParSEC Trace Tables format...' ) filenames[0] = pbt2ptt.convert(filenames, report_progress=True) print('Loading the HDFed trace...') trace = ptt.from_hdf(filenames[0]) print('The load took {} seconds.\n'.format(t.interval)) i = 0 if args.list: print('Available Counters:') for col_name in trace.events.columns.values: if col_name == 'begin': break if '_start' in col_name: continue print(str(i) + '\t' + col_name) i += 1 print('\nAvailable Events:') for i in range(0, len(trace.event_names) - 1): if not trace.event_names[i].startswith('PINS_PAPI'): print(str(i) + '\t' + trace.event_names[i]) exit() colors_needed = 0 num_counters = 0 event_names = [] event_types = [] column_names = [] i = 0 available = [] # We only need to print these if the user hasn't already specified a list of counters if args.counters == None: print( '/nPlease select the counter(s) you want to plot as a comma-separated list. Enter \'-1\' for all.' ) print( 'You may also specify a range of events within the list. For instance, 4-6 translates to 4,5,6.' ) print('Example: 0,1,4-6,8\n') for col_name in trace.events.columns.values: if col_name == 'begin': break if '_start' in col_name: continue if args.counters == None: print(str(i) + '\t' + col_name) available.append(col_name) i += 1 if args.counters == None: selection = raw_input('Counter(s) to measure: ') selection = selection.replace(' ', '') else: selection = args.counters print('\nYour selected counter(s):') if selection != '-1': selection = selection.split(',') i = 0 # Iterate through the selections and print the corresponding counters while True: # If this is a list of counters, break it up and add each to the list if '-' in selection[i]: min_max = selection[i].split('-') selection[i] = available[int(min_max[0])] print(min_max[0] + '\t' + selection[i]) inc = 0 for n in range(int(min_max[0]) + 1, int(min_max[1]) + 1): inc += 1 print(str(n) + '\t' + available[n]) selection.insert(i + inc, available[n]) i += inc + 1 else: print(selection[i] + '\t' + available[int(selection[i])]) selection[i] = available[int(selection[i])] i += 1 if i == len(selection): break # Add all of the counters to the selection else: selection = [] for i in range(0, len(available)): print(str(i) + '\t' + available[i]) selection.append(available[i]) column_names.extend(selection) num_counters = len(selection) # We only need to do this if the user hasn't specified a list of events already if args.events == None: print( '\n\nPlease select the event(s) you want to plot as a comma-separated list. Enter \'-1\' for all.' ) print( 'You may also specify a range of events within the list. For instance, 4-6 translates to 4,5,6.' ) print('Example: 0,1,4-6,8\n') for i in range(0, len(trace.event_names) - 1): if not trace.event_names[i].startswith('PINS_PAPI'): print(str(i) + '\t' + trace.event_names[i]) selection = raw_input('Event(s) to Measure: ') selection = selection.replace(' ', '') else: selection = args.events print('\nYour selected event(s):') if selection != '-1': selection = selection.split(',') i = 0 # Iterate through the selections and print the corresponding events while True: # If this is a list of events, break it up and add each to the list if '-' in selection[i]: min_max = selection[i].split('-') selection[i] = trace.event_names[int(min_max[0])] event_types.append(trace.event_types[selection[i]]) print(min_max[0] + '\t' + trace.event_names[int(min_max[0])]) inc = 0 for n in range(int(min_max[0]) + 1, int(min_max[1]) + 1): inc += 1 print(str(n) + '\t' + trace.event_names[n]) selection.insert(i + inc, trace.event_names[n]) event_types.append(trace.event_types[selection[i + inc]]) i += inc + 1 else: print(selection[i] + '\t' + trace.event_names[int(selection[i])]) selection[i] = trace.event_names[int(selection[i])] event_types.append(trace.event_types[selection[i]]) i += 1 if i == len(selection): break else: selection = [] for i in range(0, len(trace.event_names) - 1): if not trace.event_names[i].startswith('PINS_PAPI'): print(str(i) + '\t' + trace.event_names[i]) selection.append(trace.event_names[i]) event_types.append(trace.event_types[selection[-1]]) print('') event_names = selection colors_needed = num_counters * len(event_names) print('Colors Needed: ' + str(colors_needed)) # The counter_data list will store subsets of the pandas dataframes for the # columns containing PAPI counter information. The column_names list stores # the names corresponding to those columns. counter_data = [] counter_dict = [] event_data = [] print('Populating user-defined lists...') with Timer() as t: # We start from the beginning, which is where the PAPI event columns will be. for i in range(0, len(trace.events.columns.values)): column_name = trace.events.columns.values[i] # If we hit the 'begin' column, there aren't any more PAPI event columns. if column_name == 'begin': break if '_start' in column_name: continue if column_name not in column_names: continue # We only care about the data in this column for which there is data. # Note: counter_data actually stores all of the rows for which the column # of interest is not NULL. counter_data.append( trace.events[:][trace.events[column_name].notnull()]) prev = 0 counter_dict.append(dict()) end_count = counter_data[-1][column_name].values.tolist() start_count = counter_data[-1][column_name + '_start'].values.tolist() for j in range(0, len(end_count)): counter_dict[-1][ end_count[j]] = end_count[j] - start_count[j] for i in range(0, len(event_types)): event_data.append( trace.events[:][trace.events.type == event_types[i]]) print('Populating the lists took {} seconds.\n'.format(t.interval)) max_count = 0 for i in range(0, len(counter_data)): print('i = ' + str(i)) # For each counter, iterate through all of the selected event types for j in range(0, len(event_types)): # The begins, ends, and streams lists are used for selecting correct records from pandas begins = event_data[j].begin.tolist() ends = event_data[j].end.tolist() streams = event_data[j].stream_id.tolist() counts = [] for k in range(0, len(event_data[j])): temp_data = counter_data[i][:][ (counter_data[i].begin < ends[k]) & (counter_data[i].end > begins[k]) & (counter_data[i].stream_id == streams[k])].loc[:, [column_names[i], 'begin', 'end' ]].values.tolist() for l in range(0, len(temp_data)): temp_begin = 0 temp_end = 0 if temp_data[l][1] < begins[k]: # The measurement started before the event temp_begin = begins[k] else: # The measurement started after the event temp_begin = temp_data[l][1] if temp_data[l][2] > ends[k]: # The measurement ended after the event temp_end = ends[k] else: # The measurement ended before the event temp_end = temp_data[l][2] # This is the proportion of overlap between the counter and event data overlap = (temp_end - temp_begin) / (temp_data[l][2] - temp_data[l][1]) # Only the proportion of the counter data that corresponds to this event is recorded. # Note: This is an approximation, because events will inevitably accumulate counts # at different rates. counts.append( int(overlap * counter_dict[i][temp_data[l][0]])) for val in counts: if val > max_count: max_count = val # Start the plot of the figure with a relatively large size. fig = plt.figure(num=None, figsize=(16, 9), dpi=80, facecolor='w', edgecolor='k') # Start the color iterator so we can plot each column in its own color. colors = iter(cm.rainbow(np.linspace(0, 1, colors_needed))) print('Plotting all selected counters and events together...') with Timer() as t: # Iterate through all of the selected counters for i in range(0, len(counter_data)): # For each counter, iterate through all of the selected event types for j in range(0, len(event_types)): # The begins, ends, and streams lists are used for selecting correct records from pandas begins = event_data[j].begin.tolist() ends = event_data[j].end.tolist() streams = event_data[j].stream_id.tolist() counts = [] times = [] print('Processing and plotting \'' + event_names[j] + '_' + column_names[i] + '\'...') # Iterate through all of the events within this event type for k in range(0, len(event_data[j])): # This command grabs all of the rows for which the counter data and the event data overlap in time on the same execution stream temp_data = counter_data[i][:][ (counter_data[i].begin < ends[k]) & (counter_data[i].end > begins[k]) & (counter_data[i].stream_id == streams[k] )].loc[:, [column_names[i], 'begin', 'end' ]].values.tolist() # Iterate through all of the counter data rows for this event. # Note: There will typically be only 1 overlapping counter row, but it is possible there could be more for l in range(0, len(temp_data)): temp_begin = 0 temp_end = 0 if temp_data[l][1] < begins[k]: # The measurement started before the event temp_begin = begins[k] else: # The measurement started after the event temp_begin = temp_data[l][1] if temp_data[l][2] > ends[k]: # The measurement ended after the event temp_end = ends[k] else: # The measurement ended before the event temp_end = temp_data[l][2] # This is the proportion of overlap between the counter and event data overlap = (temp_end - temp_begin) / ( temp_data[l][2] - temp_data[l][1]) # Only the proportion of the counter data that corresponds to this event is recorded. # Note: This is an approximation, because events will inevitably accumulate counts # at different rates. counts.append( int(overlap * counter_dict[i][temp_data[l][0]])) times.append(temp_end * unit_modify) adjust_factor = 1000 adjust_power = 3 while max_count > adjust_factor: adjust_factor *= 1000 adjust_power += 3 adjust_factor /= 1000 adjust_power -= 3 if (adjust_factor > 1): for v in range(0, len(counts)): counts[v] /= adjust_factor # Plot the data for this event type plt.scatter(times, counts, color=next(colors), label=event_names[j] + '_' + column_names[i]) plt.title('Counts by Event Name') plt.ylim(ymin=0) plt.xlim(xmin=0) if (adjust_power > 0): plt.ylabel('Count (Times 10^' + str(adjust_power) + ')') else: plt.ylabel('Count') if units != 'c': plt.xlabel('Time (' + units + ')') else: plt.xlabel('Cycles') ax = plt.subplot(111) box = ax.get_position() ax.set_position([box.x0, box.y0, box.width * 0.8, box.height]) ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) plt.show() figure_name = 'counts_by_eventname.png' if args.output != None: figure_name = args.output print('Saving plot as ' + figure_name + '...') fig.savefig(figure_name) print('Plotting and saving took {} seconds.'.format(t.interval)) print('Total Time: {} seconds\n'.format(main.interval))