ATLAS Offline Software
Loading...
Searching...
No Matches
perfmonmt-plotter.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
4
5# @author: Hasan Ozturk <haozturk@cern.ch>
6
7__author__ = "Hasan Ozturk <haozturk@cern.ch"
8__doc__ = "A python module which parses the PerfMonMTSvc results and makes plots"
9
10import json
11
12import matplotlib
13matplotlib.use('PDF') # use PDF backend
14import matplotlib.pyplot as plt
15import numpy as np
16
17import operator
18import argparse
19import tarfile
20
21colors = { "dCPU" : "tab:blue", "dWall" : "tab:orange",
22 "dVmem" : "tab:blue", "dPss" : "tab:green",
23 "dRss" : "tab:orange", "dSwap" : "tab:red",
24 "cpuTime" : "tab:blue", "wallTime" : "tab:orange",
25 "malloc" : "tab:orange", "vmem" : "tab:blue",
26 "pss" : "tab:green", "rss" : "tab:orange", "swap" : "tab:red" }
27
28def plotBarChart(params):
29
30 ax = params["ax"]
31
32 offset = 1 - len(params["vals"])
33 for metric in params["vals"]:
34 vals = params["vals"][metric]
35 ax.barh(params["index"] + (offset*0.5)*params["width"], vals, params["width"],
36 color = colors[metric], alpha=0.8, label = metric)
37 offset += 2
38
39 ax.set_xlabel(params["xlabel"], fontsize=params['xlabelFontSize'])
40 ax.set_ylabel(params["ylabel"], fontsize=params['ylabelFontSize'])
41 ax.set_title(params["title"], fontsize=params['titleFontSize'], fontweight='bold')
42 ax.set_yticks(params["index"])
43 ax.set_yticklabels(params["yTickLabels"])
44 handles, labels = ax.get_legend_handles_labels()
45 ax.legend(reversed(handles), reversed(labels), prop={'size': params['legendFontSize']})
46 ax.tick_params(axis='both', which='major', labelsize=30)
47 ax.grid(linestyle=':',linewidth=0.1)
48
49def plotLineChart(params):
50
51 ax = params['ax']
52
53 for label, metric in params['yVals'].items():
54 ax.plot(params['xVals'], metric, color = colors[label], label = label)
55
56 ax.set_xlabel(params['xlabel'])
57 ax.set_ylabel(params['ylabel'])
58
59 ax.set_xticks(params['xVals'])
60 ax.set_xticklabels(params['xVals'], rotation='vertical')
61 ax.tick_params(axis='both', which='major', labelsize=10)
62 ax.set_title(params['title'])
63 ax.legend()
64 ax.grid(linestyle=':',linewidth=0.1)
65
66def sortComponents(compNames, measDict, compCountPerPlot):
67
68 metricToCompMeasDict = {} # This is a lookup map, we use it to map components to measurements after sorting
69 measList = []
70 for metric, meas in measDict.items():
71
72 metricToCompMeasDict[metric] = dict(zip(compNames, meas))
73 measList.append(meas)
74
75 # Sort by the sum of each metric: Ex: Sort by (vmem + malloc) for memMon
76 sortByList = [sum(x) for x in zip(*measList)]
77 sortedCompMeasTuple = sorted(dict(zip(compNames, sortByList)).items(), key = operator.itemgetter(1))
78 sortedCompNames = [ compMeas[0] for compMeas in sortedCompMeasTuple]
79
80 sortedMeasurements = {}
81 # Populate sortedMeasurements according to the sortedCompNames
82 for comp in sortedCompNames[-compCountPerPlot:]:
83 for metric, compMeas in metricToCompMeasDict.items():
84
85 if metric not in sortedMeasurements:
86 sortedMeasurements[metric] = []
87
88 sortedMeasurements[metric].append(compMeas[comp])
89
90
91
92 return sortedCompNames[-compCountPerPlot:], sortedMeasurements
93
94def plotSnapshotLevel(snapshotData, plotname):
95
96 # Collect data
97 stepNames, dCPUVals, dWallVals, dVmemVals, dRssVals, dPssVals, dSwapVals = [],[],[],[],[],[],[]
98 for step in ['Finalize', 'FirstEvent', 'Execute', 'Initialize', 'Configure']:
99 meas = snapshotData[step]
100
101 # Show in seconds
102 dCPU = meas["dCPU"] * 0.001
103 dWall = meas["dWall"] * 0.001
104
105 # Show in megabytes
106 dVmem = meas["dVmem"] * 0.001
107 dRss = meas["dRss"] * 0.001
108 dPss = meas["dPss"] * 0.001
109 dSwap = meas["dSwap"] * 0.001
110
111 stepNames.append(step)
112 dCPUVals.append(dCPU)
113 dWallVals.append(dWall)
114
115 dVmemVals.append(dVmem)
116 dRssVals.append(dRss)
117 dPssVals.append(dPss)
118 dSwapVals.append(dSwap)
119
120 timeMonVals = {
121 "dCPU": dCPUVals,
122 "dWall": dWallVals
123 }
124
125 memMonVals = {
126 "dVmem": dVmemVals,
127 "dRss": dRssVals,
128 "dPss": dPssVals,
129 "dSwap": dSwapVals,
130 }
131
132
133 timeMonFig, timeMonAx = plt.subplots(figsize=(20,15))
134 memMonFig, memMonAx = plt.subplots(figsize=(20,15))
135
136 timeMonParams = {
137 "ax": timeMonAx,
138 "index": np.arange(len(stepNames)),
139 "width": 0.5/len(timeMonVals),
140 "vals": timeMonVals,
141 "yTickLabels": stepNames,
142 "xlabel": "Time [sec]",
143 "ylabel": "Steps",
144 "title": "Snapshot Level Monitoring: Time Metrics",
145 "titleFontSize": 40,
146 "xlabelFontSize": 40,
147 "ylabelFontSize": 40,
148 "legendFontSize": 30
149
150 }
151
152 memMonParams = {
153 "ax": memMonAx,
154 "index": np.arange(len(stepNames)),
155 "width": 0.5/len(memMonVals), # Think about this
156 "vals": memMonVals,
157 "yTickLabels": stepNames,
158 "xlabel": "Memory [MB]",
159 "ylabel": "Steps",
160 "title": "Snapshot Level Monitoring: Memory Metrics",
161 "titleFontSize": 40,
162 "xlabelFontSize": 40,
163 "ylabelFontSize": 40,
164 "legendFontSize": 30
165 }
166
167
168 plotBarChart(timeMonParams)
169 plotBarChart(memMonParams)
170
171
172 timeMonFig.set_tight_layout( True )
173 timeMonFig.savefig("Snaphot_Level_Time")
174
175 memMonFig.set_tight_layout(True)
176 memMonFig.savefig("Snapshot_Level_Memory")
177
178
179def plotComponentLevel(componentLevelData, compCountPerPlot):
180
181 timeMonFig = plt.figure(figsize=(35,105))
182 memMonFig = plt.figure(figsize=(35,105))
183
184 for idx, step in enumerate(componentLevelData):
185
186 compNames, vmemVals, cpuTimeVals, wallTimeVals, mallocVals, countVals = [],[],[],[],[],[]
187 for comp, meas in componentLevelData[step].items():
188
189 count = meas["count"]
190 cpuTime = meas["cpuTime"] * 0.001 # seconds
191 wallTime = meas["wallTime"] * 0.001 # seconds
192 malloc = meas["malloc"] * 0.001 # MB
193 vmem = meas["vmem"] * 0.001 # MB
194
195 # Discard negative measurements
196 if vmem < 0 or malloc < 0:
197 continue
198
199 # Truncate unwieldy component names
200 if len(comp) > 50:
201 comp = f"{comp[:20]}[...]{comp[-20:]}"
202
203 compNames.append(comp + " [" + str(count) + "]")
204 vmemVals.append(vmem)
205 cpuTimeVals.append(cpuTime)
206 wallTimeVals.append(wallTime)
207 mallocVals.append(malloc)
208 countVals.append(count)
209
210 timeMonVals = {
211 "cpuTime": cpuTimeVals,
212 "wallTime": wallTimeVals
213 }
214
215 memMonVals = {
216 "vmem": vmemVals,
217 "malloc": mallocVals,
218 }
219
220 # Sort the components
221 sortedTimeMonCompNames, sortedTimeMonVals = sortComponents(compNames, timeMonVals, compCountPerPlot)
222 sortedCompNamesMem, sortedMemMonVals = sortComponents(compNames, memMonVals, compCountPerPlot)
223
224 timeMonAx = timeMonFig.add_subplot(len(componentLevelData),1,idx+1)
225 memMonAx = memMonFig.add_subplot(len(componentLevelData),1,idx+1)
226
227 timeMonParams = {
228 "ax": timeMonAx,
229 "index": np.arange(len(sortedTimeMonCompNames)),
230 "width": 0.5/len(sortedTimeMonVals), # Think about this
231 "vals": sortedTimeMonVals,
232 "yTickLabels": sortedTimeMonCompNames,
233 "xlabel": "Time [sec]",
234 "ylabel": "Components",
235 "title": step,
236 "titleFontSize": 70,
237 "xlabelFontSize": 50,
238 "ylabelFontSize": 50,
239 "legendFontSize": 30
240 }
241
242 memMonParams = {
243 "ax": memMonAx,
244 "index": np.arange(len(sortedCompNamesMem)),
245 "width": 0.5/len(sortedMemMonVals), # Think about this
246 "vals": sortedMemMonVals,
247 "yTickLabels": sortedCompNamesMem,
248 "xlabel": "Memory [MB]",
249 "ylabel": "Components",
250 "title": step,
251 "titleFontSize": 70,
252 "xlabelFontSize": 50,
253 "ylabelFontSize": 50,
254 "legendFontSize": 30
255 }
256
257 plotBarChart(timeMonParams)
258 plotBarChart(memMonParams)
259
260 timeMonFig.set_tight_layout( True )
261 timeMonFig.savefig("Component_Level_Time")
262
263 memMonFig.set_tight_layout( True )
264 memMonFig.savefig("Component_Level_Memory")
265
266
267def plotEventLevel(eventLevelData):
268
269 sortedEventLevelData = sorted(eventLevelData.items(), key=lambda i: int(i[0]))
270
271 eventVals, cpuTimeVals, wallTimeVals, vmemVals, rssVals, pssVals, swapVals = [], [], [], [], [], [], []
272
273 timeMonFig, timeMonAx = plt.subplots()
274 memMonFig, memMonAx = plt.subplots()
275
276 for entry in sortedEventLevelData:
277
278 event = entry[0]
279 meas = entry[1]
280
281 # Time metrics in seconds, Memory metrics in megabytes
282 eventVals.append(event)
283 cpuTimeVals.append(meas['cpuTime'] * 0.001)
284 wallTimeVals.append(meas['wallTime'] * 0.001)
285 vmemVals.append(meas['vmem'] * 0.001)
286 rssVals.append(meas['rss'] * 0.001)
287 pssVals.append(meas['pss'] * 0.001)
288 swapVals.append(meas['swap'] * 0.001)
289
290
291 timeMonVals = {
292 "cpuTime": cpuTimeVals,
293 "wallTime": wallTimeVals
294 }
295
296 memMonVals = {
297 "vmem": vmemVals,
298 "rss": rssVals,
299 "pss": pssVals,
300 "swap": swapVals
301 }
302
303 timeMonParams = {
304 "ax": timeMonAx,
305 "yVals": timeMonVals,
306 "xVals": eventVals, # Maybe x ticks?
307 "xlabel": "Events",
308 "ylabel": "Time [sec]",
309 "title": "Event Level Time Measurements"
310 }
311
312 memMonParams = {
313 "ax": memMonAx,
314 "yVals": memMonVals,
315 "xVals": eventVals, # Maybe x ticks?
316 "xlabel": "Events",
317 "ylabel": "Memory [MB]",
318 "title": "Event Level Memory Measurements"
319 }
320
321
322 plotLineChart(timeMonParams)
323 plotLineChart(memMonParams)
324
325 timeMonFig.set_tight_layout(True)
326 timeMonFig.savefig("Event_Level_Time")
327
328 memMonFig.set_tight_layout(True)
329 memMonFig.savefig("Event_Level_Memory")
330
331def process(data, ncomps):
332 if "snapshotLevel" in data["summary"]:
333 snapshotData = data["summary"]["snapshotLevel"]
334 plotSnapshotLevel(snapshotData, 'snapshotLevel.pdf')
335
336 if "componentLevel" in data:
337 componentLevelData = data["componentLevel"]
338 plotComponentLevel(componentLevelData, ncomps)
339
340 if "eventLevel" in data:
341 eventLevelData = data["eventLevel"]
342 plotEventLevel(eventLevelData)
343
344def main():
345 ''' Main function for producing plots from PerfMonMT JSON file.'''
346
347 parser = argparse.ArgumentParser()
348
349 parser.add_argument("-i", "--input", dest = "input",
350 default = 'PerfMonMTSvc_result.json',
351 help = 'The input JSON file')
352 parser.add_argument("-n", "--numberOfCompsPerPlot",
353 dest = "numberOfCompsPerPlot", default = 20,
354 help = "The number of components to be plotted")
355
356 args = parser.parse_args()
357
358 if tarfile.is_tarfile(args.input):
359 tar = tarfile.open(args.input)
360 for member in tar.getmembers():
361 f = tar.extractfile(member)
362 data = json.load(f)
363 process(data, int(args.numberOfCompsPerPlot))
364 tar.close()
365 else:
366 with open(args.input) as jsonFile:
367 data = json.load(jsonFile)
368 process(data, int(args.numberOfCompsPerPlot))
369
370
371if __name__ == "__main__":
372 main()
const std::string process
plotEventLevel(eventLevelData)
plotSnapshotLevel(snapshotData, plotname)
sortComponents(compNames, measDict, compCountPerPlot)
plotComponentLevel(componentLevelData, compCountPerPlot)