Skip to content

Commit 1903dbf

Browse files
authored
Merge pull request #49088 from gartung/gartung-PerTools-AllocMonitorCircles
PerfTools/AllocMonitor: Update edmModuleAllocJsonToCircles.py to add "event setup" transition for ESProducers
2 parents 534614f + 0a60919 commit 1903dbf

File tree

1 file changed

+168
-50
lines changed

1 file changed

+168
-50
lines changed

PerfTools/AllocMonitor/scripts/edmModuleAllocJsonToCircles.py

Lines changed: 168 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
#!/usr/bin/env python3
22
import json
3+
import sys
4+
from collections import namedtuple
5+
6+
# Constants
7+
BYTES_TO_KB = 1024
8+
EVENTSETUP_TRANSITION = "event setup"
9+
10+
# Named tuple for unique module identification
11+
UniqueKey = namedtuple('UniqueKey', ['moduleLabel', 'moduleType', 'recordName'])
12+
313
transitionTypes = [
414
"construction",
515
"begin job",
@@ -9,15 +19,62 @@
919
"global begin luminosity block",
1020
"stream begin luminosity block",
1121
"event",
22+
EVENTSETUP_TRANSITION,
1223
]
1324
allocTypes = ["added", "nAlloc", "nDealloc", "maxTemp", "max1Alloc"]
1425

1526
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"])
27+
"""
28+
Processes module transitions for a given transition type.
29+
30+
The expected schema for each 'alloc' dictionary is:
31+
{
32+
"added": int, # Bytes added during transition
33+
"nAlloc": int, # Number of allocations
34+
"nDealloc": int, # Number of deallocations
35+
"maxTemp": int, # Maximum temporary memory (bytes)
36+
"max1Alloc": int # Largest single allocation (bytes)
37+
}
38+
Any missing field defaults to 0.
39+
40+
Note: Entries with record names are excluded as they belong to EventSetup transition only.
41+
"""
42+
moduleKey = UniqueKey(moduleLabel, moduleType, "")
43+
moduleTransition[moduleKey] = {"cpptype": moduleType, "allocs": []}
44+
for entry in moduleInfo:
45+
# Only process entries that match the transition type AND don't have record names
46+
# (entries with record names are EventSetup only)
47+
if (entry.get("transition", None) == transitionType and
48+
not ("record" in entry and "name" in entry["record"])):
49+
moduleTransition[moduleKey]["allocs"].append(entry.get("alloc", {}))
50+
moduleTransition[moduleKey]["nTransitions"] = len(moduleTransition[moduleKey]["allocs"])
51+
52+
def processESModuleTransition(moduleLabel, moduleType, moduleInfo, moduleTransition):
53+
"""Process EventSetup transitions - entries with record names
54+
55+
Creates unique entries for each module+type+record combination.
56+
"""
57+
# Group allocations by record name
58+
recordAllocations = {}
59+
for entry in moduleInfo:
60+
# EventSetup entries are those with a "record" field containing "name"
61+
if "record" in entry and "name" in entry["record"]:
62+
recordName = entry["record"]["name"]
63+
if recordName not in recordAllocations:
64+
recordAllocations[recordName] = []
65+
recordAllocations[recordName].append(entry.get("alloc", {}))
66+
67+
# Create separate entries for each record
68+
for recordName, allocs in recordAllocations.items():
69+
# Create unique key: module + type + record
70+
uniqueKey = UniqueKey(moduleLabel, moduleType, recordName)
71+
moduleTransition[uniqueKey] = {
72+
"cpptype": moduleType,
73+
"allocs": allocs,
74+
"nTransitions": len(allocs),
75+
"moduleLabel": moduleLabel,
76+
"recordName": recordName
77+
}
2178

2279
def formatToCircles(moduleTransitions):
2380
modules_dict = {}
@@ -62,67 +119,128 @@ def formatToCircles(moduleTransitions):
62119
]
63120
# The circles code uses the "events" field to normalize the values between files with different number of events
64121
# 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"
122+
doc["total"]["events"] = 1
123+
doc["total"]["label"] = "Job"
124+
doc["total"]["type"] = "Job"
125+
# Initialize totals for all transition types and allocation types
126+
for transType in transitionTypes:
68127
for allocType in allocTypes:
69-
doc["total"][f"{allocType} {transitionType}"] = 0
128+
doc["total"][f"{allocType} {transType}"] = 0
70129

130+
# First pass: collect all unique module keys across all transitions
131+
all_module_keys = set()
71132
for transitionType, moduleTransition in moduleTransitions.items():
72-
for label, info in moduleTransition.items():
133+
for uniqueKey in moduleTransition.keys():
134+
all_module_keys.add(uniqueKey)
135+
136+
# Initialize all modules with default values for all transitions
137+
for displayKey in all_module_keys:
138+
if displayKey not in modules_dict:
139+
# UniqueKey namedtuple provides direct access to fields
140+
modules_dict[displayKey] = {
141+
"label": displayKey.moduleLabel,
142+
"type": displayKey.moduleType,
143+
"record": displayKey.recordName
144+
}
145+
146+
# Initialize all transition metrics to zero
147+
for transType in transitionTypes:
148+
for allocType in allocTypes:
149+
modules_dict[displayKey][f"{allocType} {transType}"] = 0.0
150+
151+
# Second pass: populate actual values
152+
for transitionType, moduleTransition in moduleTransitions.items():
153+
for uniqueKey, info in moduleTransition.items():
73154
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}"]
108155

156+
# Only update metrics if this module actually has data for this transition
157+
if uniqueKey in modules_dict:
158+
added = 0
159+
nAlloc = 0
160+
nDealloc = 0
161+
maxTemp = 0
162+
max1Alloc = 0
163+
for alloc in allocs:
164+
added += alloc.get("added", 0)
165+
nAlloc += alloc.get("nAlloc", 0)
166+
nDealloc += alloc.get("nDealloc", 0)
167+
maxTemp += alloc.get("maxTemp", 0)
168+
max1Alloc += alloc.get("max1Alloc", 0)
169+
ntransitions = moduleTransitions[transitionType][uniqueKey].get("nTransitions", 0)
170+
# Normalize by number of transitions if > 0, otherwise use raw values
171+
divisor = max(ntransitions, 1) # Avoid division by zero
172+
173+
modules_dict[uniqueKey][f"nAlloc {transitionType}"] = nAlloc / divisor
174+
modules_dict[uniqueKey][f"nDealloc {transitionType}"] = nDealloc / divisor
175+
modules_dict[uniqueKey][f"added {transitionType}"] = (added / divisor) / BYTES_TO_KB
176+
modules_dict[uniqueKey][f"maxTemp {transitionType}"] = (maxTemp / divisor) / BYTES_TO_KB
177+
modules_dict[uniqueKey][f"max1Alloc {transitionType}"] = (max1Alloc / divisor) / BYTES_TO_KB
178+
doc["total"][f"nAlloc {transitionType}"] += modules_dict[uniqueKey][f"nAlloc {transitionType}"]
179+
doc["total"][f"nDealloc {transitionType}"] += modules_dict[uniqueKey][f"nDealloc {transitionType}"]
180+
doc["total"][f"maxTemp {transitionType}"] += modules_dict[uniqueKey][f"maxTemp {transitionType}"]
181+
doc["total"][f"added {transitionType}"] += modules_dict[uniqueKey][f"added {transitionType}"]
182+
doc["total"][f"max1Alloc {transitionType}"] += modules_dict[uniqueKey][f"max1Alloc {transitionType}"]
109183
for key in sorted(modules_dict.keys()):
110184
module = modules_dict[key]
111-
module["events"] = moduleTransitions['event'][key].get("nTransitions")
185+
186+
# Check if this is an empty entry (record="" and all allocations are zero)
187+
if module["record"] == "":
188+
# Check if all allocation metrics are zero across all transition types
189+
hasNonZeroAllocations = False
190+
for transType in transitionTypes:
191+
for allocType in allocTypes:
192+
if module.get(f"{allocType} {transType}", 0) != 0:
193+
hasNonZeroAllocations = True
194+
break
195+
if hasNonZeroAllocations:
196+
break
197+
198+
# Skip this entry if no allocations and empty record
199+
if not hasNonZeroAllocations:
200+
continue
201+
202+
# Use the module label from the UniqueKey namedtuple for event count lookup
203+
moduleLabel = key.moduleLabel
204+
# Look for the corresponding regular module key for event transitions
205+
eventKey = UniqueKey(moduleLabel, key.moduleType, "")
206+
eventCount = moduleTransitions['event'].get(eventKey, {}).get("nTransitions", 0)
207+
# Set events to 1 if it's 0 to prevent NaNs in Circles visualization
208+
module["events"] = max(eventCount, 1)
112209
doc["modules"].append(module)
113210

114211
return doc
115212

116213
def main(args):
117-
import sys
118-
doc = json.load(args.filename)
214+
try:
215+
doc = json.load(args.filename)
216+
except json.JSONDecodeError as e:
217+
print(f"Error parsing JSON: {e}", file=sys.stderr)
218+
sys.exit(1)
219+
except Exception as e:
220+
print(f"Error reading file: {e}", file=sys.stderr)
221+
sys.exit(1)
222+
223+
# Validate required fields
224+
if 'cpptypes' not in doc:
225+
print("Error: Missing 'cpptypes' field in input JSON", file=sys.stderr)
226+
sys.exit(1)
227+
if 'modules' not in doc:
228+
print("Error: Missing 'modules' field in input JSON", file=sys.stderr)
229+
sys.exit(1)
230+
119231
moduleTypes = doc['cpptypes']
120232
moduleTransitions = dict()
121233
for transition in transitionTypes:
122234
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)
235+
if transition == EVENTSETUP_TRANSITION:
236+
# EventSetup transitions are handled differently - look for records with names
237+
for moduleLabel, moduleInfo in doc["modules"].items():
238+
processESModuleTransition(moduleLabel, moduleTypes[moduleLabel], moduleInfo, moduleTransition)
239+
else:
240+
# Regular transition processing
241+
processModuleTransition("source", "PoolSource", doc["source"], transition, moduleTransition)
242+
for moduleLabel, moduleInfo in doc["modules"].items():
243+
processModuleTransition(moduleLabel, moduleTypes[moduleLabel], moduleInfo, transition, moduleTransition)
126244
moduleTransitions[transition] = moduleTransition
127245

128246
json.dump(formatToCircles(moduleTransitions), sys.stdout, indent=2)

0 commit comments

Comments
 (0)