diff --git a/examples/plot_tasks.py b/examples/plot_tasks.py index 1be59d1c8449970321b8ef9053ddf24b4559dabd..75ac2db65cac748606c143b3c691a167b314a926 100755 --- a/examples/plot_tasks.py +++ b/examples/plot_tasks.py @@ -1,18 +1,20 @@ #!/usr/bin/env python """ Usage: - plot_tasks.py input.dat output.png [time-range-ms] + plot_tasks.py [options] input.dat output.png -where input.dat is a thread info file for a step. Use the '-y interval' -flag of the swift MPI commands to create these. The output plot will be -called 'output.png'. Use the time-range-ms in millisecs to produce -plots with the same time span. +where input.dat is a thread info file for a step. Use the '-y interval' flag +of the swift command to create these. The output plot will be called +'output.png'. The --limit option can be used to produce plots with the same +time span and the --expand option to expand each thread line into '*expand' +lines, so that adjacent tasks of the same type can be distinguished. Other +options can be seen using the --help flag. This file is part of SWIFT. Copyright (c) 2015 Pedro Gonnet (pedro.gonnet@durham.ac.uk), Bert Vandenbroucke (bert.vandenbroucke@ugent.be) Matthieu Schaller (matthieu.schaller@durham.ac.uk) - (c) 2016 Peter W. Draper (p.w.draper@durham.ac.uk) + (c) 2017 Peter W. Draper (p.w.draper@durham.ac.uk) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -29,11 +31,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. """ import matplotlib +matplotlib.use("Agg") import matplotlib.collections as collections -matplotlib.use('Agg') +import matplotlib.ticker as plticker import pylab as pl -import numpy as np import sys +import argparse + +# Handle the command line. +parser = argparse.ArgumentParser(description="Plot task graphs") + +parser.add_argument("input", help="Thread data file (-y output)") +parser.add_argument("outpng", help="Name for output graphic file (PNG)") +parser.add_argument("-l", "--limit", dest="limit", + help="Upper time limit in millisecs (def: depends on data)", + default=0, type=int) +parser.add_argument("-e", "--expand", dest="expand", + help="Thread expansion factor (def: 1)", + default=1, type=int) +parser.add_argument("--height", dest="height", + help="Height of plot in inches (def: 4)", + default=4., type=float) +parser.add_argument("--width", dest="width", + help="Width of plot in inches (def: 16)", + default=16., type=float) +parser.add_argument("--nolegend", dest="nolegend", + help="Whether to show the legend (def: False)", + default=False, action="store_true") +parser.add_argument("-v", "--verbose", dest="verbose", + help="Show colour assignments and other details (def: False)", + default=False, action="store_true") + +args = parser.parse_args() +infile = args.input +outpng = args.outpng +delta_t = args.limit +expand = args.expand # Basic plot configuration. PLOT_PARAMS = {"axes.labelsize": 10, @@ -42,7 +75,7 @@ PLOT_PARAMS = {"axes.labelsize": 10, "legend.fontsize": 12, "xtick.labelsize": 10, "ytick.labelsize": 10, - "figure.figsize" : (16., 4.), + "figure.figsize" : (args.width, args.height), "figure.subplot.left" : 0.03, "figure.subplot.right" : 0.995, "figure.subplot.bottom" : 0.1, @@ -56,9 +89,10 @@ pl.rcParams.update(PLOT_PARAMS) # Tasks and subtypes. Indexed as in tasks.h. TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", - "init", "ghost", "extra_ghost", "drift", "kick1", "kick2", + "init_grav", "ghost", "extra_ghost", "drift", "kick1", "kick2", "timestep", "send", "recv", "grav_top_level", "grav_long_range", "grav_mm", "grav_down", "cooling", "sourceterms", "count"] + SUBTYPES = ["none", "density", "gradient", "force", "grav", "external_grav", "tend", "xv", "rho", "gpart", "multipole", "spart", "count"] @@ -69,14 +103,16 @@ FULLTYPES = ["self/force", "self/density", "self/grav", "sub_self/force", "sub_pair/density", "recv/xv", "send/xv", "recv/rho", "send/rho", "recv/tend", "send/tend"] -# Get a number of colours for the various types. -colours = ["black", "gray", "rosybrown", "firebrick", "red", "darksalmon", - "sienna", "sandybrown", "bisque", "tan", "moccasin", "gold", "darkkhaki", - "lightgoldenrodyellow", "olivedrab", "chartreuse", "darksage", "lightgreen", - "green", "mediumseagreen", "mediumaquamarine", "mediumturquoise", "darkslategrey", - "cyan", "cadetblue", "skyblue", "dodgerblue", "slategray", "darkblue", - "slateblue", "blueviolet", "mediumorchid", "purple", "magenta", "hotpink", - "pink"] +# A number of colours for the various types. Recycled when there are +# more task types than colours... + +colours = ["cyan", "lightgray", "darkblue", "yellow", "tan", "dodgerblue", + "sienna", "aquamarine", "bisque", "blue", "green", "brown", + "purple", "mocassin", "olivedrab", "chartreuse", "darksage", + "darkgreen", "green", "mediumseagreen", "mediumaquamarine", + "darkslategrey", "mediumturquoise", "black", "cadetblue", "skyblue", + "red", "slategray", "gold", "slateblue", "blueviolet", + "mediumorchid", "firebrick", "magenta", "hotpink", "pink"] maxcolours = len(colours) # Set colours of task/subtype. @@ -87,30 +123,21 @@ for task in TASKTYPES: ncolours = (ncolours + 1) % maxcolours SUBCOLOURS = {} -for task in SUBTYPES: +for task in FULLTYPES: SUBCOLOURS[task] = colours[ncolours] ncolours = (ncolours + 1) % maxcolours -for task in FULLTYPES: +for task in SUBTYPES: SUBCOLOURS[task] = colours[ncolours] ncolours = (ncolours + 1) % maxcolours -# Show docs if help is requested. -if len( sys.argv ) == 2 and ( sys.argv[1][0:2] == "-h" or sys.argv[1][0:3] == "--h" ): - from pydoc import help - help( "__main__" ) - sys.exit( 0 ) - -# Handle command-line. -if len( sys.argv ) != 3 and len( sys.argv ) != 4: - print "Usage: ", sys.argv[0], "input.dat output.png [time-range-ms]" - sys.exit(1) - -infile = sys.argv[1] -outpng = sys.argv[2] -delta_t = 0 -if len( sys.argv ) == 4: - delta_t = int(sys.argv[3]) +# For fiddling with colours... +if args.verbose: + print "#Selected colours:" + for task in TASKCOLOURS.keys(): + print "# " + task + ": " + TASKCOLOURS[task] + for task in SUBCOLOURS.keys(): + print "# " + task + ": " + SUBCOLOURS[task] # Read input. data = pl.loadtxt( infile ) @@ -118,51 +145,61 @@ data = pl.loadtxt( infile ) nthread = int(max(data[:,0])) + 1 print "Number of threads:", nthread -# Recover the start and end time +# Recover the start and end time full_step = data[0,:] tic_step = int(full_step[4]) toc_step = int(full_step[5]) -CPU_CLOCK = float(full_step[-1]) +CPU_CLOCK = float(full_step[-1]) / 1000.0 data = data[1:,:] +if args.verbose: + print "CPU frequency:", CPU_CLOCK * 1000.0 -print "CPU frequency:", CPU_CLOCK - -# Avoid start and end times of zero. +# Avoid start and end times of zero. data = data[data[:,4] != 0] data = data[data[:,5] != 0] -# Calculate the time range, if not given. -delta_t = delta_t * CPU_CLOCK / 1000 +# Calculate the time range, if not given. +delta_t = delta_t * CPU_CLOCK if delta_t == 0: dt = max(data[:,5]) - min(data[:,4]) if dt > delta_t: delta_t = dt - print "Data range: ", delta_t / CPU_CLOCK * 1000, "ms" + print "Data range: ", delta_t / CPU_CLOCK, "ms" -# Once more doing the real gather and plots this time. -start_t = tic_step +# Once more doing the real gather and plots this time. +start_t = float(tic_step) data[:,4] -= start_t data[:,5] -= start_t -end_t = (toc_step - start_t) / CPU_CLOCK * 1000 +end_t = (toc_step - start_t) / CPU_CLOCK tasks = {} tasks[-1] = [] -for i in range(nthread): +for i in range(nthread*expand): tasks[i] = [] +# Counters for each thread when expanding. +ecounter = [] +for i in range(nthread): + ecounter.append(0) + num_lines = pl.size(data) / 10 for line in range(num_lines): thread = int(data[line,0]) + + # Expand to cover extra lines if expanding. + ethread = thread * expand + (ecounter[thread] % expand) + ecounter[thread] = ecounter[thread] + 1 + thread = ethread + tasks[thread].append({}) tasktype = TASKTYPES[int(data[line,1])] subtype = SUBTYPES[int(data[line,2])] tasks[thread][-1]["type"] = tasktype tasks[thread][-1]["subtype"] = subtype - tic = int(data[line,4]) / CPU_CLOCK * 1000 - toc = int(data[line,5]) / CPU_CLOCK * 1000 + tic = int(data[line,4]) / CPU_CLOCK + toc = int(data[line,5]) / CPU_CLOCK tasks[thread][-1]["tic"] = tic tasks[thread][-1]["toc"] = toc - tasks[thread][-1]["t"] = (toc + tic)/ 2 if "self" in tasktype or "pair" in tasktype: fulltype = tasktype + "/" + subtype if fulltype in SUBCOLOURS: @@ -171,31 +208,24 @@ for line in range(num_lines): tasks[thread][-1]["colour"] = SUBCOLOURS[subtype] else: tasks[thread][-1]["colour"] = TASKCOLOURS[tasktype] - -for thread in range(nthread): - tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"]) - + +# Use expanded threads from now on. +nthread = nthread * expand + typesseen = [] fig = pl.figure() ax = fig.add_subplot(1,1,1) -ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) +ax.set_xlim(-delta_t * 0.01 / CPU_CLOCK, delta_t * 1.01 / CPU_CLOCK) ax.set_ylim(0, nthread) -tictoc = np.zeros(2) for i in range(nthread): # Collect ranges and colours into arrays. - tictocs = np.zeros(len(tasks[i])*2) - colours = np.empty(len(tasks[i])*2, dtype='object') - coloursseen = [] + tictocs = [] + colours = [] j = 0 for task in tasks[i]: - tictocs[j] = task["tic"] - tictocs[j+1] = task["toc"] - colours[j] = task["colour"] - colours[j+1] = task["colour"] - j = j + 2 - if task["colour"] not in coloursseen: - coloursseen.append(task["colour"]) + tictocs.append((task["tic"], task["toc"] - task["tic"])) + colours.append(task["colour"]) # Legend support, collections don't add to this. if task["subtype"] != "none": @@ -206,31 +236,33 @@ for i in range(nthread): pl.plot([], [], color=task["colour"], label=qtask) typesseen.append(qtask) - # Now plot each colour, faster to use a mask to select colour ranges. - for colour in coloursseen: - collection = collections.BrokenBarHCollection.span_where(tictocs, ymin=i+0.05, ymax=i+0.95, - where=colours == colour, - facecolor=colour, - linewidths=0) - ax.add_collection(collection) - + # Now plot. + ax.broken_barh(tictocs, [i+0.05,0.90], facecolors = colours, linewidth=0) # Legend and room for it. nrow = len(typesseen) / 5 -if len(typesseen) * 5 < nrow: - nrow = nrow + 1 -ax.fill_between([0, 0], nthread+0.5, nthread + nrow + 0.5, facecolor="white") -ax.set_ylim(0, nthread + nrow + 1) -ax.legend(loc=1, shadow=True, mode="expand", ncol=5) +if not args.nolegend: + if len(typesseen) * 5 < nrow: + nrow = nrow + 1 + ax.fill_between([0, 0], nthread+0.5, nthread + nrow + 0.5, facecolor="white") + ax.set_ylim(0, nthread + nrow + 1) + ax.legend(loc=1, shadow=True, mode="expand", ncol=5) # Start and end of time-step ax.plot([0, 0], [0, nthread + nrow + 1], 'k--', linewidth=1) ax.plot([end_t, end_t], [0, nthread + nrow + 1], 'k--', linewidth=1) ax.set_xlabel("Wall clock time [ms]") -ax.set_ylabel("Thread ID" ) +if expand == 1: + ax.set_ylabel("Thread ID" ) +else: + ax.set_ylabel("Thread ID * " + str(expand) ) ax.set_yticks(pl.array(range(nthread)), True) +loc = plticker.MultipleLocator(base=expand) +ax.yaxis.set_major_locator(loc) +ax.grid(True, which='major', axis="y", linestyle="-") + pl.show() pl.savefig(outpng) print "Graphics done, output written to", outpng diff --git a/examples/plot_tasks_MPI.py b/examples/plot_tasks_MPI.py index c95bfa1fd2d087cc907b57201c1a1397cbeb1460..a1f81bb1789c73ebe21391509c7d7674f6e43100 100755 --- a/examples/plot_tasks_MPI.py +++ b/examples/plot_tasks_MPI.py @@ -1,13 +1,15 @@ #!/usr/bin/env python """ Usage: - plot_tasks_MPI.py input.dat png-output-prefix [time-range-ms] + plot_tasks_MPI.py [options] input.dat png-output-prefix -where input.dat is a thread info file for a step of an MPI run. Use the '-y -interval' flag of the swift MPI commands to create these. The output plots -will be called 'png-output-prefix<mpi-rank>.png', i.e. one each for all the -threads in each MPI rank. Use the time-range-ms in millisecs to produce -plots with the same time span. +where input.dat is a thread info file for a step. Use the '-y interval' flag +of the swift MPI command to create these. The output plot will be called +'png-output-prefix<mpi-rank>.png', i.e. one each for all the threads in each +MPI rank. The --limit option can be used to produce plots with the same time +span and the --expand option to expand each thread line into '*expand' lines, +so that adjacent tasks of the same type can be distinguished. Other options +can be seen using the --help flag. See the command 'process_plot_tasks_MPI' to efficiently wrap this command to process a number of thread info files and create an HTML file to view them. @@ -17,7 +19,7 @@ This file is part of SWIFT. Copyright (C) 2015 Pedro Gonnet (pedro.gonnet@durham.ac.uk), Bert Vandenbroucke (bert.vandenbroucke@ugent.be) Matthieu Schaller (matthieu.schaller@durham.ac.uk) - Peter W. Draper (p.w.draper@durham.ac.uk) + (C) 2017 Peter W. Draper (p.w.draper@durham.ac.uk) All Rights Reserved. This program is free software: you can redistribute it and/or modify @@ -35,13 +37,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. """ import matplotlib -import matplotlib.collections as collections matplotlib.use("Agg") +import matplotlib.collections as collections +import matplotlib.ticker as plticker import pylab as pl -import numpy as np import sys -#import warnings -#warnings.simplefilter("error") +import argparse + +# Handle the command line. +parser = argparse.ArgumentParser(description="Plot task graphs") + +parser.add_argument("input", help="Thread data file (-y output)") +parser.add_argument("outbase", help="Base name for output graphic files (PNG)") +parser.add_argument("-l", "--limit", dest="limit", + help="Upper time limit in millisecs (def: depends on data)", + default=0, type=int) +parser.add_argument("-e", "--expand", dest="expand", + help="Thread expansion factor (def: 1)", + default=1, type=int) +parser.add_argument("--height", dest="height", + help="Height of plot in inches (def: 4)", + default=4., type=float) +parser.add_argument("--width", dest="width", + help="Width of plot in inches (def: 16)", + default=16., type=float) +parser.add_argument("--nolegend", dest="nolegend", + help="Whether to show the legend (def: False)", + default=False, action="store_true") +parser.add_argument("-v", "--verbose", dest="verbose", + help="Show colour assignments and other details (def: False)", + default=False, action="store_true") + +args = parser.parse_args() +infile = args.input +outbase = args.outbase +delta_t = args.limit +expand = args.expand # Basic plot configuration. PLOT_PARAMS = {"axes.labelsize": 10, @@ -50,7 +81,7 @@ PLOT_PARAMS = {"axes.labelsize": 10, "legend.fontsize": 12, "xtick.labelsize": 10, "ytick.labelsize": 10, - "figure.figsize" : (16., 4.), + "figure.figsize" : (args.width, args.height), "figure.subplot.left" : 0.03, "figure.subplot.right" : 0.995, "figure.subplot.bottom" : 0.1, @@ -64,26 +95,31 @@ pl.rcParams.update(PLOT_PARAMS) # Tasks and subtypes. Indexed as in tasks.h. TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", - "init", "ghost", "extra_ghost", "drift", "kick1", "kick2", - "timestep", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "cooling", "sourceterms", "count"] + "init_grav", "ghost", "extra_ghost", "drift", "kick1", "kick2", + "timestep", "send", "recv", "grav_top_level", "grav_long_range", + "grav_mm", "grav_down", "cooling", "sourceterms", "count"] + SUBTYPES = ["none", "density", "gradient", "force", "grav", "external_grav", - "tend", "xv", "rho", "gpart", "count"] + "tend", "xv", "rho", "gpart", "multipole", "spart", "count"] # Task/subtypes of interest. -FULLTYPES = ["self/force", "self/density", "sub_self/force", - "sub_self/density", "pair/force", "pair/density", "sub_pair/force", +FULLTYPES = ["self/force", "self/density", "self/grav", "sub_self/force", + "sub_self/density", "pair/force", "pair/density", "pair/grav", + "sub_pair/force", "sub_pair/density", "recv/xv", "send/xv", "recv/rho", "send/rho", "recv/tend", "send/tend"] -# Get a number of colours for the various types. -colours = ["black", "gray", "rosybrown", "firebrick", "red", "darksalmon", - "sienna", "sandybrown", "bisque", "tan", "moccasin", "gold", "darkkhaki", - "lightgoldenrodyellow", "olivedrab", "chartreuse", "darksage", "lightgreen", - "green", "mediumseagreen", "mediumaquamarine", "mediumturquoise", "darkslategrey", - "cyan", "cadetblue", "skyblue", "dodgerblue", "slategray", "darkblue", - "slateblue", "blueviolet", "mediumorchid", "purple", "magenta", "hotpink", - "pink"] +# A number of colours for the various types. Recycled when there are +# more task types than colours... + +colours = ["cyan", "lightgray", "darkblue", "yellow", "tan", "dodgerblue", + "sienna", "aquamarine", "bisque", "blue", "green", "brown", + "purple", "mocassin", "olivedrab", "chartreuse", "darksage", + "darkgreen", "green", "mediumseagreen", "mediumaquamarine", + "darkslategrey", "mediumturquoise", "black", "cadetblue", "skyblue", + "red", "slategray", "gold", "slateblue", "blueviolet", + "mediumorchid", "firebrick", "magenta", "hotpink", "pink", + "orange", "lightgreen"] maxcolours = len(colours) # Set colours of task/subtype. @@ -94,43 +130,30 @@ for task in TASKTYPES: ncolours = (ncolours + 1) % maxcolours SUBCOLOURS = {} -for task in SUBTYPES: +for task in FULLTYPES: SUBCOLOURS[task] = colours[ncolours] ncolours = (ncolours + 1) % maxcolours -for task in FULLTYPES: +for task in SUBTYPES: SUBCOLOURS[task] = colours[ncolours] ncolours = (ncolours + 1) % maxcolours -# Show docs if help is requested. -if len( sys.argv ) == 2 and ( sys.argv[1][0:2] == "-h" or sys.argv[1][0:3] == "--h" ): - from pydoc import help - help( "__main__" ) - sys.exit( 0 ) - -# Handle command-line. -if len( sys.argv ) != 3 and len( sys.argv ) != 4: - print "Usage: ", sys.argv[0], "input.dat png-output-prefix [time-range-ms]" - sys.exit(1) - - -infile = sys.argv[1] -outbase = sys.argv[2] -delta_t = 0 -if len( sys.argv ) == 4: - delta_t = int(sys.argv[3]) +# For fiddling with colours... +if args.verbose: + print "#Selected colours:" + for task in TASKCOLOURS.keys(): + print "# " + task + ": " + TASKCOLOURS[task] + for task in SUBCOLOURS.keys(): + print "# " + task + ": " + SUBCOLOURS[task] # Read input. data = pl.loadtxt( infile ) -# Recover the start and end time +# Get CPU_CLOCK to convert ticks into milliseconds. full_step = data[0,:] -tic_step = int(full_step[5]) -toc_step = int(full_step[6]) -CPU_CLOCK = float(full_step[-1]) - -print "CPU frequency:", CPU_CLOCK - +CPU_CLOCK = float(full_step[-1]) / 1000.0 +if args.verbose: + print "CPU frequency:", CPU_CLOCK * 1000.0 nranks = int(max(data[:,0])) + 1 print "Number of ranks:", nranks @@ -144,60 +167,70 @@ sdata = sdata[sdata[:,6] != 0] # Each rank can have different clock (compute node), but we want to use the # same delta times range for comparisons, so we suck it up and take the hit of # precalculating this, unless the user knows better. -delta_t = delta_t * CPU_CLOCK / 1000 +delta_t = delta_t * CPU_CLOCK if delta_t == 0: for rank in range(nranks): data = sdata[sdata[:,0] == rank] dt = max(data[:,6]) - min(data[:,5]) if dt > delta_t: delta_t = dt - print "Data range: ", delta_t / CPU_CLOCK * 1000, "ms" + print "Data range: ", delta_t / CPU_CLOCK, "ms" # Once more doing the real gather and plots this time. for rank in range(nranks): data = sdata[sdata[:,0] == rank] + # Start and end times for this rank. full_step = data[0,:] tic_step = int(full_step[5]) toc_step = int(full_step[6]) data = data[1:,:] - typesseen = [] # Dummy image for ranks that have no tasks. if data.size == 0: print "rank ", rank, " has no tasks" fig = pl.figure() ax = fig.add_subplot(1,1,1) - ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) - ax.set_ylim(0, nthread) + ax.set_xlim(-delta_t * 0.01 / CPU_CLOCK, delta_t * 1.01 / CPU_CLOCK) + ax.set_ylim(0, nthread*expand) start_t = tic_step - end_t = (toc_step - start_t) / CPU_CLOCK * 1000 + end_t = (toc_step - start_t) / CPU_CLOCK else: - start_t = tic_step + start_t = float(tic_step) data[:,5] -= start_t data[:,6] -= start_t - end_t = (toc_step - start_t) / CPU_CLOCK * 1000 + end_t = (toc_step - start_t) / CPU_CLOCK tasks = {} tasks[-1] = [] - for i in range(nthread): + for i in range(nthread*expand): tasks[i] = [] + # Counters for each thread when expanding. + ecounter = [] + for i in range(nthread): + ecounter.append(0) + num_lines = pl.shape(data)[0] for line in range(num_lines): thread = int(data[line,1]) + + # Expand to cover extra lines if expanding. + ethread = thread * expand + (ecounter[thread] % expand) + ecounter[thread] = ecounter[thread] + 1 + thread = ethread + tasks[thread].append({}) tasktype = TASKTYPES[int(data[line,2])] subtype = SUBTYPES[int(data[line,3])] tasks[thread][-1]["type"] = tasktype tasks[thread][-1]["subtype"] = subtype - tic = int(data[line,5]) / CPU_CLOCK * 1000 - toc = int(data[line,6]) / CPU_CLOCK * 1000 + tic = int(data[line,5]) / CPU_CLOCK + toc = int(data[line,6]) / CPU_CLOCK tasks[thread][-1]["tic"] = tic tasks[thread][-1]["toc"] = toc - tasks[thread][-1]["t"] = (toc + tic)/ 2 if "self" in tasktype or "pair" in tasktype or "recv" in tasktype or "send" in tasktype: fulltype = tasktype + "/" + subtype if fulltype in SUBCOLOURS: @@ -207,29 +240,23 @@ for rank in range(nranks): else: tasks[thread][-1]["colour"] = TASKCOLOURS[tasktype] - for thread in range(nthread): - tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"]) + # Use expanded threads from now on. + nethread = nthread * expand + typesseen = [] fig = pl.figure() ax = fig.add_subplot(1,1,1) - ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) - ax.set_ylim(0, nthread) - tictoc = np.zeros(2) - for i in range(nthread): + ax.set_xlim(-delta_t * 0.01 / CPU_CLOCK, delta_t * 1.01 / CPU_CLOCK) + ax.set_ylim(0, nethread) + for i in range(nethread): # Collect ranges and colours into arrays. - tictocs = np.zeros(len(tasks[i])*2) - colours = np.empty(len(tasks[i])*2, dtype='object') - coloursseen = [] + tictocs = [] + colours = [] j = 0 for task in tasks[i]: - tictocs[j] = task["tic"] - tictocs[j+1] = task["toc"] - colours[j] = task["colour"] - colours[j+1] = task["colour"] - j = j + 2 - if task["colour"] not in coloursseen: - coloursseen.append(task["colour"]) + tictocs.append((task["tic"], task["toc"] - task["tic"])) + colours.append(task["colour"]) # Legend support, collections don't add to this. if task["subtype"] != "none": @@ -241,33 +268,34 @@ for rank in range(nranks): pl.plot([], [], color=task["colour"], label=qtask) typesseen.append(qtask) - # Now plot each colour, faster to use a mask to select colour ranges. - for colour in coloursseen: - collection = collections.BrokenBarHCollection.span_where(tictocs, - ymin=i+0.05, - ymax=i+0.95, - where=colours == colour, - facecolor=colour, - linewidths=0) - ax.add_collection(collection) + # Now plot. + ax.broken_barh(tictocs, [i+0.05,0.90], facecolors = colours, linewidth=0) # Legend and room for it. nrow = len(typesseen) / 5 if len(typesseen) * 5 < nrow: nrow = nrow + 1 - ax.fill_between([0, 0], nthread+0.5, nthread + nrow + 0.5, facecolor="white") - ax.set_ylim(0, nthread + nrow + 1) + ax.fill_between([0, 0], nethread+0.5, nethread + nrow + 0.5, facecolor="white") + ax.set_ylim(0, nethread + nrow + 1) if data.size > 0: ax.legend(loc=1, shadow=True, mode="expand", ncol=5) # Start and end of time-step - ax.plot([0, 0], [0, nthread + nrow + 1], 'k--', linewidth=1) - ax.plot([end_t, end_t], [0, nthread + nrow + 1], 'k--', linewidth=1) + ax.plot([0, 0], [0, nethread + nrow + 1], 'k--', linewidth=1) + ax.plot([end_t, end_t], [0, nethread + nrow + 1], 'k--', linewidth=1) ax.set_xlabel("Wall clock time [ms]") - ax.set_ylabel("Thread ID for MPI Rank " + str(rank) ) - ax.set_yticks(pl.array(range(nthread)), True) + + if expand == 1: + ax.set_ylabel("Thread ID" ) + else: + ax.set_ylabel("Thread ID * " + str(expand) ) + ax.set_yticks(pl.array(range(nethread)), True) + + loc = plticker.MultipleLocator(base=expand) + ax.yaxis.set_major_locator(loc) + ax.grid(True, which='major', axis="y", linestyle="-") pl.show() outpng = outbase + str(rank) + ".png" diff --git a/examples/process_plot_tasks b/examples/process_plot_tasks index cf19401b582c29f7e35073be93569ea8039f958d..c33531269617e81d4e8f18f1528b43492b08cf26 100755 --- a/examples/process_plot_tasks +++ b/examples/process_plot_tasks @@ -56,7 +56,7 @@ done # And process them, echo "Processing thread info files..." -echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "./plot_tasks.py \$0 \$2 $TIMERANGE" +echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "./plot_tasks.py --expand 1 --limit $TIMERANGE --width 16 --height 4 \$0 \$2 " echo "Writing output index.html file" # Construct document - serial. diff --git a/examples/process_plot_tasks_MPI b/examples/process_plot_tasks_MPI index d3eb5d4a5fc5918b287cd5d98efcc5881b6f910c..268a5393dc94f8b05cb4ff0a71c45b3b8e554c84 100755 --- a/examples/process_plot_tasks_MPI +++ b/examples/process_plot_tasks_MPI @@ -61,7 +61,7 @@ nrank=$(($nrank-1)) # And process them, echo "Processing thread info files..." -echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "./plot_tasks_MPI.py \$0 \$2 $TIMERANGE" +echo $list | xargs -P $NPROCS -n 3 /bin/bash -c "./plot_tasks_MPI.py --expand 1 --limit $TIMERANGE \$0 \$2 " echo "Writing output index.html file" # Construct document - serial. diff --git a/src/scheduler.c b/src/scheduler.c index ccc8e78902efc27360a5bd8b0b7589a9bb8096a5..25d03918ecc53fd54478c8d6e88d8149936a4ac1 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1123,6 +1123,15 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements, */ void scheduler_start(struct scheduler *s) { +/* Reset all task debugging timers */ +#ifdef SWIFT_DEBUG_TASKS + for (int i = 0; i < s->nr_tasks; ++i) { + s->tasks[i].tic = 0; + s->tasks[i].toc = 0; + s->tasks[i].rid = -1; + } +#endif + /* Re-wait the tasks. */ if (s->active_count > 1000) { threadpool_map(s->threadpool, scheduler_rewait_mapper, s->tid_active, diff --git a/tests/tolerance_27_perturbed.dat b/tests/tolerance_27_perturbed.dat index a238acd234772b6e146a1d83a5ebab6c7fb69921..b6ed8c2c18808156056f9c5816212866c2dfa998 100644 --- a/tests/tolerance_27_perturbed.dat +++ b/tests/tolerance_27_perturbed.dat @@ -1,3 +1,3 @@ # ID pos_x pos_y pos_z v_x v_y v_z rho rho_dh wcount wcount_dh div_v curl_vx curl_vy curl_vz 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.2e-6 1e-4 5e-5 2e-3 3.1e-6 3e-6 3e-6 3e-6 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-3 1e-5 1e-4 2e-5 2e-3 2e-3 2e-3 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-3 1e-5 1e-4 2e-5 2e-3 2e-3 2e-3