Skip to content

Commit 238dc76

Browse files
committed
Add flamegraph from precice/precice/pull/2116
1 parent 6ffd3e4 commit 238dc76

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

preciceprofiling/flamegraph.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from preciceprofiling.common import Run, ns_to_unit_factor
2+
from preciceprofiling.parsers import addInputArgument, addUnitArgument
3+
import polars as pl
4+
from plotly.subplots import make_subplots
5+
import plotly.graph_objects as go
6+
import argparse
7+
8+
9+
def makeFlamegraphParser(add_help: bool = True):
10+
flamegraph_help = """Plots the hierarchical duration data of a solver as a flamegraph plot.
11+
Event durations are displayed in the unit of choice.
12+
Parallel solvers show one plot for the primary rank and a combined plot for all secondary ranks.
13+
"""
14+
parser = argparse.ArgumentParser(description=flamegraph_help, add_help=add_help)
15+
parser.add_argument("participant", type=str, help="The participant to analyze")
16+
addInputArgument(parser)
17+
addUnitArgument(parser)
18+
return parser
19+
20+
21+
def runFlamegraph(ns):
22+
return flamegraphCommand(ns.profilingfile, ns.participant, ns.unit)
23+
24+
25+
def flamegraphCommand(profilingfile, participant, unit="us"):
26+
run = Run(profilingfile)
27+
df = run.toDataFrame()
28+
29+
assert df.select(
30+
pl.col("participant").is_in([participant]).any()
31+
).item(), f"Given participant {participant} doesn't exist."
32+
33+
print(f"Output timing are in {unit}.")
34+
35+
def toParent(eid):
36+
if eid == "_GLOBAL":
37+
return ""
38+
if "/" not in eid:
39+
return "_GLOBAL"
40+
return "/".join(eid.split("/")[:-1])
41+
42+
def toLabel(eid):
43+
if eid == "_GLOBAL":
44+
return participant
45+
return eid.split("/")[-1]
46+
47+
# Filter by participant
48+
# Convert duration to requested unit
49+
dur_factor = 1000 * ns_to_unit_factor(unit)
50+
df = (
51+
df.filter(pl.col("participant") == participant)
52+
.drop("participant")
53+
.with_columns(
54+
pl.col("dur") * dur_factor,
55+
pl.col("eid")
56+
.map_elements(toParent, return_dtype=pl.String)
57+
.alias("parent"),
58+
pl.col("eid").map_elements(toLabel, return_dtype=pl.String).alias("label"),
59+
)
60+
)
61+
62+
primary = (
63+
df.filter(pl.col("rank") == 0)
64+
.group_by("eid", "parent", "label")
65+
.agg(pl.sum("dur"))
66+
.sort("eid")
67+
)
68+
69+
primaryPlot = go.Icicle(
70+
labels=primary["label"].to_list(),
71+
ids=primary["eid"].to_list(),
72+
parents=primary["parent"].to_list(),
73+
values=primary["dur"].to_list(),
74+
branchvalues="total",
75+
tiling=dict(orientation="v", flip="y"),
76+
root_color="lightgrey",
77+
hovertemplate="<b>%{label}</b><br><i>%{id}</i><br>%{value} " + unit,
78+
)
79+
80+
if len(df.select("rank").unique()) == 1:
81+
82+
fig = go.Figure(primaryPlot)
83+
fig.update_layout(title=participant)
84+
fig.show()
85+
return 0
86+
87+
secondary = (
88+
df.filter(pl.col("rank") > 0)
89+
.group_by("eid", "parent", "label")
90+
.agg(pl.sum("dur"))
91+
.sort("eid")
92+
)
93+
94+
fig = make_subplots(
95+
rows=1,
96+
cols=2,
97+
specs=[[{"type": "domain"}, {"type": "domain"}]],
98+
subplot_titles=[f"Primary of {participant}", f"Secondaries of {participant}"],
99+
)
100+
fig.add_trace(primaryPlot, row=1, col=1)
101+
fig.add_trace(
102+
go.Icicle(
103+
labels=secondary["label"].to_list(),
104+
ids=secondary["eid"].to_list(),
105+
parents=secondary["parent"].to_list(),
106+
values=secondary["dur"].to_list(),
107+
branchvalues="total",
108+
tiling=dict(orientation="v", flip="y"),
109+
root_color="lightgrey",
110+
hovertemplate="<b>%{label}</b><br><i>%{id}</i><br>%{value} " + unit,
111+
),
112+
row=1,
113+
col=2,
114+
)
115+
fig.show()
116+
return 0
117+
118+
119+
def main():
120+
parser = makeFlamegraphParser()
121+
ns = parser.parse_args()
122+
return runFlamegraph(ns)
123+
124+
125+
if __name__ == "__main__":
126+
sys.exit(main())

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
66
name="precice-profiling"
77
dynamic = [ "version" ]
88
dependencies = [
9-
"typing_extensions", "orjson >= 3", "polars >= 0.19.0", "matplotlib"
9+
"typing_extensions", "orjson >= 3", "polars >= 0.19.0", "matplotlib", "plotly"
1010
]
1111
requires-python = ">=3.8"
1212
authors = [
@@ -40,6 +40,7 @@ precice-profiling-analyze = "preciceprofiling.analyze:main"
4040
precice-profiling-trace = "preciceprofiling.trace:main"
4141
precice-profiling-export = "preciceprofiling.export:main"
4242
precice-profiling-histogram = "preciceprofiling.histogram:main"
43+
precice-profiling-flamegraph = "preciceprofiling.flamegraph:main"
4344

4445
[tool.setuptools]
4546
packages=["preciceprofiling"]

0 commit comments

Comments
 (0)