plot_tasks_MPI.py 9.41 KB
Newer Older
1
#!/usr/bin/env python
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""
Usage:
    plot_tasks_MPI.py input.dat png-output-prefix [time-range-ms]
   
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.

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.

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)
All Rights Reserved.

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
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
36
37

import matplotlib
38
import matplotlib.collections as collections
39
matplotlib.use("Agg")
40
41
import pylab as pl
import numpy as np
42
import sys
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#  Basic plot configuration.
PLOT_PARAMS = {"axes.labelsize": 10,
               "axes.titlesize": 10,
               "font.size": 12,
               "legend.fontsize": 12,
               "xtick.labelsize": 10,
               "ytick.labelsize": 10,
               "figure.figsize" : (16., 4.),
               "figure.subplot.left" : 0.03,
               "figure.subplot.right" : 0.995,
               "figure.subplot.bottom" : 0.1,
               "figure.subplot.top" : 0.99,
               "figure.subplot.wspace" : 0.,
               "figure.subplot.hspace" : 0.,
               "lines.markersize" : 6,
               "lines.linewidth" : 3.
               }
pl.rcParams.update(PLOT_PARAMS)
62

63
#  Tasks and subtypes. Indexed as in tasks.h.
64
65
TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost",
             "drift", "kick", "kick_fixdt", "send", "recv", "grav_pp", "grav_mm",
66
             "grav_up", "grav_down", "grav_external", "part_sort", "gpart_sort",
67
             "split_cell", "rewait", "count"]
68

69
TASKCOLOURS = {"none": "black",
70
71
72
               "sort": "lightblue",
               "self": "greenyellow",
               "pair": "navy",
73
74
               "sub_self": "greenyellow",
               "sub_pair": "navy",
75
               "init": "indigo",
76
               "ghost": "cyan",
77
78
               "drift": "maroon",
               "kick": "green",
79
               "kick_fixdt": "green",
80
81
82
83
84
85
               "send": "yellow",
               "recv": "magenta",
               "grav_pp": "mediumorchid",
               "grav_mm": "mediumturquoise",
               "grav_up": "mediumvioletred",
               "grav_down": "mediumnightblue",
86
               "grav_external": "darkred",
87
               "part_sort": "steelblue",
88
               "gpart_sort": "teal" ,
89
90
91
92
               "split_cell": "seagreen",
               "rewait": "olive",
               "count": "powerblue"}

93
SUBTYPES = ["none", "density", "force", "grav", "count"]
94

95
SUBCOLOURS = {"none": "black",
96
97
98
99
              "density": "red",
              "force": "blue",
              "grav": "indigo",
              "count": "purple"}
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#  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:
117
118
    delta_t = int(sys.argv[3])
    
119
120
#  Read input.
data = pl.loadtxt( infile )
121

122
123
# Recover the start and end time
full_step = data[0,:]
124
125
tic_step = int(full_step[5])
toc_step = int(full_step[6])
126
127
128
129
CPU_CLOCK = float(full_step[-1])

print "CPU frequency:", CPU_CLOCK / 1.e9

130

131
132
133
134
135
nranks = int(max(data[:,0])) + 1
print "Number of ranks:", nranks
nthread = int(max(data[:,1])) + 1
print "Number of threads:", nthread

136
# Avoid start and end times of zero.
137
138
sdata = data[data[:,5] != 0]
sdata = sdata[sdata[:,6] != 0]
139
140
141
142

# 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.
143
delta_t = delta_t * CPU_CLOCK / 1000
144
145
146
147
148
149
150
151
152
153
154
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

# Once more doing the real gather and plots this time.
for rank in range(nranks):
    data = sdata[sdata[:,0] == rank]

155
156
157
158
159
160
    full_step = data[0,:]
    tic_step = int(full_step[5])
    toc_step = int(full_step[6])
    data = data[1:,:]

    start_t = tic_step
161
162
    data[:,5] -= start_t
    data[:,6] -= start_t
163
    end_t = (toc_step - start_t) / CPU_CLOCK * 1000
164
165
166
167
168
169

    tasks = {}
    tasks[-1] = []
    for i in range(nthread):
        tasks[i] = []

170
    num_lines = pl.shape(data)[0]
171
172
173
    for line in range(num_lines):
        thread = int(data[line,1])
        tasks[thread].append({})
174
175
        tasks[thread][-1]["type"] = TASKTYPES[int(data[line,2])]
        tasks[thread][-1]["subtype"] = SUBTYPES[int(data[line,3])]
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
        tic = int(data[line,5]) / CPU_CLOCK * 1000
        toc = int(data[line,6]) / CPU_CLOCK * 1000
        tasks[thread][-1]["tic"] = tic
        tasks[thread][-1]["toc"] = toc
        tasks[thread][-1]["t"] = (toc + tic)/ 2

    combtasks = {}
    combtasks[-1] = []
    for i in range(nthread):
        combtasks[i] = []

    for thread in range(nthread):
        tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"])
        lasttype = ""
        types = []
        for task in tasks[thread]:
            if task["type"] not in types:
                types.append(task["type"])
            if lasttype == "" or not lasttype == task["type"]:
                combtasks[thread].append({})
                combtasks[thread][-1]["type"] = task["type"]
                combtasks[thread][-1]["subtype"] = task["subtype"]
                combtasks[thread][-1]["tic"] = task["tic"]
                combtasks[thread][-1]["toc"] = task["toc"]
                if task["type"] == "self" or task["type"] == "pair" or task["type"] == "sub":
201
                    combtasks[thread][-1]["colour"] = SUBCOLOURS[task["subtype"]]
202
                else:
203
                    combtasks[thread][-1]["colour"] = TASKCOLOURS[task["type"]]
204
205
206
207
208
                lasttype = task["type"]
            else:
                combtasks[thread][-1]["toc"] = task["toc"]

    typesseen = []
209
210
    fig = pl.figure()
    ax = fig.add_subplot(1,1,1)
211
    ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK)
212
213
    ax.set_ylim(0, nthread)
    tictoc = np.zeros(2)
214
    for i in range(nthread):
215
216
217
218
219
220

        #  Collect ranges and colours into arrays.
        tictocs = np.zeros(len(combtasks[i])*2)
        colours = np.empty(len(combtasks[i])*2, dtype='object')
        coloursseen = []
        j = 0
221
        for task in combtasks[i]:
222
223
224
225
226
227
228
229
230
            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"])

            #  Legend support, collections don't add to this.
231
232
            if task["subtype"] != "none":
                qtask = task["type"] + "/" + task["subtype"]
233
            else:
234
235
236
237
238
                qtask = task["type"]
            if qtask not in typesseen:
                pl.plot([], [], color=task["colour"], label=qtask)
                typesseen.append(qtask)

239
240
241
242
243
244
245
246
247
        #  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)


248
249
250
251
    #  Legend and room for it.
    nrow = len(typesseen) / 5
    if len(typesseen) * 5 < nrow:
        nrow = nrow + 1
252
253
254
    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)
255

256
257
258
259
    # 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)

260
261
262
    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)
263
264
265
266
267

    pl.show()
    outpng = outbase + str(rank) + ".png"
    pl.savefig(outpng)
    print "Graphics done, output written to", outpng
268
269

sys.exit(0)