Skip to content

Commit 0dac991

Browse files
authored
Merge pull request #48961 from gartung/gartung-PerfTools-AllocMonitor-2
Make Circles plots for resources reported by Module Alloc Monitor service.
2 parents f934747 + 9a13634 commit 0dac991

File tree

6 files changed

+159
-3
lines changed

6 files changed

+159
-3
lines changed

Configuration/Applications/python/ConfigBuilder.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Options:
7676
defaultOptions.profile = None
7777
defaultOptions.heap_profile = None
7878
defaultOptions.maxmem_profile = None
79+
defaultOptions.alloc_monitor = None
7980
defaultOptions.isRepacked = False
8081
defaultOptions.restoreRNDSeeds = False
8182
defaultOptions.donotDropOnInput = ''

Configuration/Applications/python/Options.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,12 @@
440440
action="store_true",
441441
dest="maxmem_profile")
442442

443+
expertSettings.add_argument("--alloc_monitor",
444+
help="Add necessary LD_PRELOAD for PerfTools/AllocMonitor",
445+
default=False,
446+
action="store_true",
447+
dest="alloc_monitor")
448+
443449
expertSettings.add_argument("--io",
444450
help="Create a json file with io informations",
445451
default=None,

Configuration/Applications/python/cmsDriverOptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ def OptionsFromItems(items):
258258
raise Exception("--maxmem_profile and --prefix are incompatible")
259259
options.prefix = "env LD_PRELOAD=libPerfToolsAllocMonitorPreload.so:libPerfToolsMaxMemoryPreload.so "
260260

261+
if options.alloc_monitor:
262+
if options.prefix:
263+
raise Exception("--alloc_monitor and --prefix are incompatible")
264+
options.prefix = "env LD_PRELOAD=libPerfToolsAllocMonitorPreload.so"
265+
261266
# If an "era" argument was supplied make sure it is one of the valid possibilities
262267
if options.era :
263268
from Configuration.StandardSequences.Eras import eras
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import FWCore.ParameterSet.Config as cms
2+
def customise(process):
3+
process.ModuleAllocMonitor = cms.Service("ModuleAllocMonitor",
4+
fileName=cms.untracked.string("moduleAllocMonitor.log")
5+
)
6+
return(process)
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/env python3
2+
import json
3+
transitionTypes = [
4+
"construction",
5+
"begin job",
6+
"begin stream",
7+
"global begin run",
8+
"stream begin run",
9+
"global begin luminosity block",
10+
"stream begin luminosity block",
11+
"event",
12+
]
13+
allocTypes = ["added", "nAlloc", "nDealloc", "maxTemp", "max1Alloc"]
14+
15+
def processModuleTransition(moduleLabel, moduleType, moduleInfo, transitionType, moduleTransition):
16+
moduleTransition[moduleLabel] = {"cpptype": moduleType, "allocs": []}
17+
for entry in moduleInfo:
18+
if entry["transition"] == transitionType:
19+
moduleTransition[moduleLabel]["allocs"].append(entry.get("alloc",{}))
20+
moduleTransition[moduleLabel]["nTransitions"] = len(moduleTransition[moduleLabel]["allocs"])
21+
22+
def formatToCircles(moduleTransitions):
23+
modules_dict = {}
24+
doc = {
25+
"modules": [],
26+
"resources": [],
27+
"total": {}
28+
}
29+
for transitionType in transitionTypes:
30+
doc["resources"] += [
31+
{
32+
"name": f"added {transitionType}",
33+
"description": f"{transitionType}: added memory (average)",
34+
"title": f"{transitionType}: Amount of memory added to the process at the end of the transition",
35+
"unit": "kB"
36+
},
37+
{
38+
39+
"name": f"nAlloc {transitionType}",
40+
"description": f"{transitionType}: num allocs (average)",
41+
"title": f"{transitionType}: Number of allocations during the transition",
42+
"unit": ""
43+
},
44+
{
45+
"name": f"nDealloc {transitionType}",
46+
"description": f"{transitionType}: num deallocs (average)",
47+
"title": f"{transitionType}: Number of deallocations during the transition",
48+
"unit": ""
49+
},
50+
{
51+
"name": f"maxTemp {transitionType}",
52+
"description": f"{transitionType}: maximum temporary memory (average)",
53+
"title": f"{transitionType}: Maximum temporary memory during the transition",
54+
"unit": "kB"
55+
},
56+
{
57+
"name": f"max1Alloc {transitionType}",
58+
"description": f"{transitionType}: largest single allocation (average)",
59+
"title": f"{transitionType}: Largest single allocation during the transition",
60+
"unit": "kB"
61+
},
62+
]
63+
# The circles code uses the "events" field to normalize the values between files with different number of events
64+
# Here we set it to 1 for the total events because the total is already normalized per transition
65+
doc["total"]["events"] = 1
66+
doc["total"]["label"] = "Job"
67+
doc["total"]["type"] = "Job"
68+
for allocType in allocTypes:
69+
doc["total"][f"{allocType} {transitionType}"] = 0
70+
71+
for transitionType, moduleTransition in moduleTransitions.items():
72+
for label, info in moduleTransition.items():
73+
allocs = info.get("allocs", [])
74+
if not label in modules_dict:
75+
modules_dict[label] = {
76+
"label": info.get("label", label),
77+
"type": info.get("cpptype", "unknown")
78+
}
79+
added = 0
80+
nAlloc = 0
81+
nDealloc = 0
82+
maxTemp = 0
83+
max1Alloc = 0
84+
for alloc in allocs:
85+
added += alloc.get("added", 0)
86+
nAlloc += alloc.get("nAlloc", 0)
87+
nDealloc += alloc.get("nDealloc", 0)
88+
maxTemp += alloc.get("maxTemp", 0)
89+
max1Alloc += alloc.get("max1Alloc", 0)
90+
ntransitions = moduleTransitions[transitionType][label]["nTransitions"]
91+
if ntransitions > 0:
92+
modules_dict[label][f"nAlloc {transitionType}"] = nAlloc/ntransitions
93+
modules_dict[label][f"added {transitionType}"] = (added/ntransitions)/1024
94+
modules_dict[label][f"maxTemp {transitionType}"] = (maxTemp/ntransitions)/1024
95+
modules_dict[label][f"nDealloc {transitionType}"] = nDealloc/ntransitions
96+
modules_dict[label][f"max1Alloc {transitionType}"] = (max1Alloc/ntransitions)/1024
97+
else:
98+
modules_dict[label][f"nAlloc {transitionType}"] = nAlloc
99+
modules_dict[label][f"added {transitionType}"] = (added)/1024
100+
modules_dict[label][f"maxTemp {transitionType}"] = (maxTemp)/1024
101+
modules_dict[label][f"nDealloc {transitionType}"] = nDealloc
102+
modules_dict[label][f"max1Alloc {transitionType}"] = max1Alloc/1024
103+
doc["total"][f"nAlloc {transitionType}"] += modules_dict[label][f"nAlloc {transitionType}"]
104+
doc["total"][f"nDealloc {transitionType}"] += modules_dict[label][f"nDealloc {transitionType}"]
105+
doc["total"][f"maxTemp {transitionType}"] += modules_dict[label][f"maxTemp {transitionType}"]
106+
doc["total"][f"added {transitionType}"] += modules_dict[label][f"added {transitionType}"]
107+
doc["total"][f"max1Alloc {transitionType}"] += modules_dict[label][f"max1Alloc {transitionType}"]
108+
109+
for key in sorted(modules_dict.keys()):
110+
module = modules_dict[key]
111+
module["events"] = moduleTransitions['event'][key].get("nTransitions")
112+
doc["modules"].append(module)
113+
114+
return doc
115+
116+
def main(args):
117+
import sys
118+
doc = json.load(args.filename)
119+
moduleTypes = doc['cpptypes']
120+
moduleTransitions = dict()
121+
for transition in transitionTypes:
122+
moduleTransition = dict()
123+
processModuleTransition("source", "PoolSource", doc["source"], transition, moduleTransition)
124+
for moduleLabel, moduleInfo in doc["modules"].items():
125+
processModuleTransition(moduleLabel, moduleTypes[moduleLabel], moduleInfo, transition, moduleTransition)
126+
moduleTransitions[transition] = moduleTransition
127+
128+
json.dump(formatToCircles(moduleTransitions), sys.stdout, indent=2)
129+
130+
if __name__ == "__main__":
131+
import argparse
132+
133+
parser = argparse.ArgumentParser(description='Convert the JSON output of edmModuleAllocMonitorAnalyze.py to JSON for Circles')
134+
parser.add_argument('filename',
135+
type=argparse.FileType('r'), # open file
136+
help='file to process')
137+
args = parser.parse_args()
138+
main(args)

PerfTools/AllocMonitor/scripts/edmModuleAllocMonitorAnalyze.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,9 @@ def jsonInfo(self, syncs, temp, data):
620620
start = temp.findTime("source", self.transition, self.index)
621621
#we do not know the sync yet so have to wait until the framework transition
622622
if self.transition in [ Phase.construction, Phase.getNextTransition, Phase.destruction, Phase.openFile]:
623-
data.insert( "source" , "sourceType", start, self.time, self.transition, self.index, (0,) , Activity.process, self.allocInfo)
623+
data.insert( "source" , "PoolSource", start, self.time, self.transition, self.index, (0,) , Activity.process, self.allocInfo)
624624
else:
625-
data.insert( "source" , "sourceType", start, self.time, self.transition, self.index, self.index , Activity.process, self.allocInfo)
625+
data.insert( "source" , "PoolSource", start, self.time, self.transition, self.index, self.index , Activity.process, self.allocInfo)
626626
def jsonVisInfo(self, data):
627627
index = self.index
628628
if self.transition == Phase.Event:
@@ -812,7 +812,7 @@ def jsonVisInfo(self, data):
812812
return self._postJsonVis(data, self.allocInfo)
813813
def jsonInfo(self, syncs, temp, data):
814814
start = temp.findTime(self.moduleInfo._name+'source', self.transition, self.index)
815-
data.insert( "source" , "sourceType", start, self.time, self.transition, self.index, syncs.get(self.transition, self.index) , Activity.delayedGet, self.allocInfo)
815+
data.insert( "source" , "PoolSource", start, self.time, self.transition, self.index, syncs.get(self.transition, self.index) , Activity.delayedGet, self.allocInfo)
816816

817817
class ESModuleTransitionParser(object):
818818
def __init__(self, payload, moduleInfos, esModuleInfos, recordNames):

0 commit comments

Comments
 (0)