carm-paraver 1.0.0.dev1__tar.gz → 1.0.1.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/PKG-INFO +3 -1
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/README.md +2 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/Paraver_CARM.py +284 -91
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/style.css +13 -18
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/PKG-INFO +3 -1
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/pyproject.toml +1 -1
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/LICENSE +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/GUI_utils.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/__init__.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/__main__.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/analysis_helpers.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/CARM_icon3.svg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/CHAMP_logo.svg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/__init__.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/bsc.svg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/carm_bsc.png +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/carm_bsc.svg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/menu_icon.png +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_AVX2_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_AVX2_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_AVX512_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_AVX512_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_SSE_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_SSE_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_Scalar_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_FP_Scalar_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_Loads.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_Stores.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/__init__.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_AVX2_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_AVX2_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_AVX512_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_AVX512_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_SSE_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_SSE_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_Scalar_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_FP_Scalar_SP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_Loads.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_Stores.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/__init__.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_DP.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_DPV2.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_DP_Extrae.xml +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_SPV2.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_SP_Extrae.xml +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/__init__.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/sample_data/roofline/MN5_roofline.csv +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/SOURCES.txt +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/dependency_links.txt +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/entry_points.txt +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/requires.txt +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/top_level.txt +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/setup.cfg +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/tests/test_analysis_helpers.py +0 -0
- {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/tools/aggregate_profiles.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carm-paraver
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1.dev0
|
|
4
4
|
Summary: Dash-based CARM analysis for Paraver traces
|
|
5
5
|
Author: CARM Contributors
|
|
6
6
|
Requires-Python: >=3.9
|
|
@@ -19,6 +19,8 @@ Dynamic: license-file
|
|
|
19
19
|
|
|
20
20
|
This GUI allows the analysis of [Paraver](https://tools.bsc.es/paraver) traces on the Cache-Aware Roofline Model (CARM) for floating-point operations. It can be launched from the Paraver interface and send labeled events back to Paraver for visualization.
|
|
21
21
|
|
|
22
|
+
The CARM allows for roofline analysis of your application, displaying its computational bursts as points on the roofline. This can be used to identify bottlenecks and optimization opportunities for the respective code section. Points on the memory-bound or the compute-bound sections of the roof benefit from different optimization strategies, and the distance of your points to the roofline can be used to identify how much performance can be gained by optimizing your code.
|
|
23
|
+
|
|
22
24
|
# Requirements
|
|
23
25
|
- Python (tested with 3.9.25, 3.10.12, 3.12.3)
|
|
24
26
|
- [Paraver, Extrae](https://tools.bsc.es/downloads)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
This GUI allows the analysis of [Paraver](https://tools.bsc.es/paraver) traces on the Cache-Aware Roofline Model (CARM) for floating-point operations. It can be launched from the Paraver interface and send labeled events back to Paraver for visualization.
|
|
4
4
|
|
|
5
|
+
The CARM allows for roofline analysis of your application, displaying its computational bursts as points on the roofline. This can be used to identify bottlenecks and optimization opportunities for the respective code section. Points on the memory-bound or the compute-bound sections of the roof benefit from different optimization strategies, and the distance of your points to the roofline can be used to identify how much performance can be gained by optimizing your code.
|
|
6
|
+
|
|
5
7
|
# Requirements
|
|
6
8
|
- Python (tested with 3.9.25, 3.10.12, 3.12.3)
|
|
7
9
|
- [Paraver, Extrae](https://tools.bsc.es/downloads)
|
|
@@ -23,6 +23,7 @@ from typing import Any
|
|
|
23
23
|
import dash
|
|
24
24
|
import dash_bootstrap_components as dbc
|
|
25
25
|
import dash_daq as daq
|
|
26
|
+
import numpy as np
|
|
26
27
|
|
|
27
28
|
# Third Party Libraries
|
|
28
29
|
# Run: pip install dash dash-bootstrap-components dash-daq numpy pandas plotly
|
|
@@ -70,6 +71,60 @@ def set_process_death_signal():
|
|
|
70
71
|
raise OSError("prctl failed")
|
|
71
72
|
|
|
72
73
|
|
|
74
|
+
class ProgressBar:
|
|
75
|
+
"""Prints a terminal progress bar that updates in-place, ensuring 100% is printed exactly once."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, total: int, bar_width: int = 30):
|
|
78
|
+
self.total = total
|
|
79
|
+
self.bar_width = bar_width
|
|
80
|
+
self._done = False
|
|
81
|
+
|
|
82
|
+
def update(self, processed: int) -> None:
|
|
83
|
+
"""Print the progress bar for the given number of processed items.
|
|
84
|
+
|
|
85
|
+
When ``processed >= total`` the bar is shown at 100% followed by a
|
|
86
|
+
newline; subsequent calls are no-ops.
|
|
87
|
+
"""
|
|
88
|
+
if self._done:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
if self.total <= 0:
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
if processed >= self.total:
|
|
95
|
+
self._print(1.0)
|
|
96
|
+
print()
|
|
97
|
+
self._done = True
|
|
98
|
+
else:
|
|
99
|
+
progress = min(processed / self.total, 0.99)
|
|
100
|
+
self._print(progress)
|
|
101
|
+
|
|
102
|
+
def _print(self, progress: float) -> None:
|
|
103
|
+
if progress >= 1.0:
|
|
104
|
+
segments = self.bar_width
|
|
105
|
+
else:
|
|
106
|
+
segments = min(math.ceil(self.bar_width * progress), self.bar_width - 1)
|
|
107
|
+
print(
|
|
108
|
+
f"[{'#' * segments}{' ' * (self.bar_width - segments)}] {progress * 100:.1f}%",
|
|
109
|
+
end="\r",
|
|
110
|
+
flush=True,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _carm_btn(label: str, button_id: str, tooltip: str | None = None):
|
|
115
|
+
"""Create a sidebar button with optional Tooltip."""
|
|
116
|
+
btn = dbc.Button(
|
|
117
|
+
label,
|
|
118
|
+
id=button_id,
|
|
119
|
+
className="mb-2",
|
|
120
|
+
style={"width": "100%"},
|
|
121
|
+
n_clicks=0,
|
|
122
|
+
)
|
|
123
|
+
if tooltip:
|
|
124
|
+
return html.Div([btn, dbc.Tooltip(tooltip, target=button_id)])
|
|
125
|
+
return btn
|
|
126
|
+
|
|
127
|
+
|
|
73
128
|
set_process_death_signal()
|
|
74
129
|
|
|
75
130
|
VERSION = "1.0.0"
|
|
@@ -556,6 +611,7 @@ if memory_counters <= missing_files:
|
|
|
556
611
|
flush=True,
|
|
557
612
|
)
|
|
558
613
|
no_mem = True
|
|
614
|
+
sys.exit(1)
|
|
559
615
|
|
|
560
616
|
# Check if all FP (floating point) counters are missing
|
|
561
617
|
if fp_counters <= missing_files:
|
|
@@ -571,8 +627,6 @@ if fp_counters <= missing_files:
|
|
|
571
627
|
)
|
|
572
628
|
sys.exit(1)
|
|
573
629
|
|
|
574
|
-
if no_mem:
|
|
575
|
-
sys.exit(1)
|
|
576
630
|
|
|
577
631
|
if "Intel_Loads" not in missing_files and "Intel_Stores" not in missing_files and "Intel_Loads_Stores" in missing_files:
|
|
578
632
|
missing_files.remove("Intel_Loads_Stores")
|
|
@@ -582,16 +636,16 @@ if any("SP" in s for s in found_files):
|
|
|
582
636
|
if any("DP" in s for s in found_files):
|
|
583
637
|
dp_counters_available = True
|
|
584
638
|
|
|
585
|
-
|
|
586
639
|
missing_msg = (
|
|
587
|
-
"
|
|
588
|
-
"
|
|
640
|
+
"Counters for some events are missing from the trace. If any of the following operations\nare relevant to your "
|
|
641
|
+
"application, you should add the corresponding counters:\n\n"
|
|
589
642
|
+ "\n ".join(
|
|
590
643
|
[
|
|
591
644
|
f"{f.replace('_', ' ')} -> {intel_performance_counters_mapping.get(f, 'No mapping found')}"
|
|
592
645
|
for f in missing_files
|
|
593
646
|
]
|
|
594
647
|
)
|
|
648
|
+
+ "\n\nThe analysis will proceed with the available counters.\nSee the documentation for more details."
|
|
595
649
|
)
|
|
596
650
|
|
|
597
651
|
is_modal_open = len(missing_files) > 0
|
|
@@ -739,6 +793,7 @@ rows_chars = len(str(total_rows))
|
|
|
739
793
|
step = max(1, total_rows // 100) if total_rows > 0 else 1
|
|
740
794
|
prog_bar_width = 30 # Total width of the progress bar
|
|
741
795
|
processed = 0
|
|
796
|
+
bar = ProgressBar(total_rows, prog_bar_width)
|
|
742
797
|
if total_rows > 50_000:
|
|
743
798
|
print(
|
|
744
799
|
f"WARNING: Displaying a large number of rows ({total_rows}) may slow down the UI. Consider zooming "
|
|
@@ -765,17 +820,9 @@ else:
|
|
|
765
820
|
counter_data_df = counter_data_df.copy()
|
|
766
821
|
|
|
767
822
|
for row in counter_data_df.itertuples(index=False):
|
|
768
|
-
# Print progress bar N times and at the end of processing
|
|
769
|
-
if processed % step == 0 or processed == total_rows:
|
|
770
|
-
# print a progress bar
|
|
771
|
-
progress = processed / total_rows
|
|
772
|
-
segments = math.ceil(prog_bar_width * progress)
|
|
773
|
-
print(
|
|
774
|
-
f"[{'#' * segments}{' ' * (prog_bar_width - segments)}] {progress * 100:.1f}%",
|
|
775
|
-
end="\r",
|
|
776
|
-
flush=True,
|
|
777
|
-
)
|
|
778
823
|
processed += 1
|
|
824
|
+
if processed % step == 0 or processed == total_rows:
|
|
825
|
+
bar.update(processed)
|
|
779
826
|
duration = row.Duration * scaling_unit
|
|
780
827
|
timestamp = row.Timestamp
|
|
781
828
|
# if FLOP counters are all zero or NaN, skip calculations and set metrics to zero/defaults
|
|
@@ -916,15 +963,6 @@ for row in counter_data_df.itertuples(index=False):
|
|
|
916
963
|
|
|
917
964
|
del counter_data_df
|
|
918
965
|
|
|
919
|
-
# Finish progress bar
|
|
920
|
-
if total_rows > 0:
|
|
921
|
-
print(
|
|
922
|
-
f"[{'#' * prog_bar_width}] {100:.1f}%",
|
|
923
|
-
end="\r",
|
|
924
|
-
flush=True,
|
|
925
|
-
)
|
|
926
|
-
print()
|
|
927
|
-
|
|
928
966
|
_runtime = time.time() - _time_start
|
|
929
967
|
print(f"Finished processing {total_rows} rows in {_runtime:.2f} seconds. ")
|
|
930
968
|
|
|
@@ -1464,26 +1502,31 @@ sidebar2 = dbc.Offcanvas(
|
|
|
1464
1502
|
className="mb-2",
|
|
1465
1503
|
style={"color": "white", "textAlign": "center", "fontSize": "20px"},
|
|
1466
1504
|
),
|
|
1467
|
-
|
|
1468
|
-
"Send
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
style={"width": "100%"},
|
|
1472
|
-
n_clicks=0,
|
|
1505
|
+
_carm_btn(
|
|
1506
|
+
"Send Roof Labels",
|
|
1507
|
+
"button-roof-labels",
|
|
1508
|
+
"Labels each timestamp based on which roof is above it (L2, DRAM, etc.)",
|
|
1473
1509
|
),
|
|
1474
|
-
|
|
1475
|
-
"Send
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
style={"width": "100%"},
|
|
1479
|
-
n_clicks=0,
|
|
1510
|
+
_carm_btn(
|
|
1511
|
+
"Send LD/ST Ratio",
|
|
1512
|
+
"button-carm-ldst-colors",
|
|
1513
|
+
"Labels each timestamp based on the load-store ratio",
|
|
1480
1514
|
),
|
|
1481
|
-
|
|
1482
|
-
"Send
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1515
|
+
_carm_btn(
|
|
1516
|
+
"Send SP/DP Ratio",
|
|
1517
|
+
"button-carm-spdp-colors",
|
|
1518
|
+
"Labels each timestamp based on the single-precision/double-precision ratio",
|
|
1519
|
+
),
|
|
1520
|
+
_carm_btn("Send Performance", "button-carm-gflops", "Labels each timestamp based on the GFLOPS performance"),
|
|
1521
|
+
_carm_btn(
|
|
1522
|
+
"Send Arithmetic Intensity", "button-carm-ai", "Labels each timestamp based on the arithmetic intensity"
|
|
1523
|
+
),
|
|
1524
|
+
_carm_btn(
|
|
1525
|
+
"Send Roof Proximity",
|
|
1526
|
+
"button-carm-roof-proximity",
|
|
1527
|
+
"Labels each timestamp based on its proximity to each of the roofs. e.g. 0.2 relative to the L1 means a "
|
|
1528
|
+
"perfectly optimization could achieve a 5x speedup. A value of 1.0 means the timestamp is at or above the "
|
|
1529
|
+
"roof.",
|
|
1487
1530
|
),
|
|
1488
1531
|
],
|
|
1489
1532
|
id="offcanvas2",
|
|
@@ -1603,57 +1646,29 @@ app.layout = dbc.Container(
|
|
|
1603
1646
|
),
|
|
1604
1647
|
],
|
|
1605
1648
|
style={
|
|
1606
|
-
"
|
|
1607
|
-
"
|
|
1608
|
-
"margin": "0px 20px auto 15px",
|
|
1649
|
+
"width": "100%",
|
|
1650
|
+
"margin": "0 10px",
|
|
1609
1651
|
},
|
|
1610
1652
|
),
|
|
1611
1653
|
]
|
|
1612
1654
|
),
|
|
1613
1655
|
style={
|
|
1614
|
-
"height": "100px",
|
|
1656
|
+
# "height": "100px",
|
|
1615
1657
|
"margin": "0px auto 10px auto",
|
|
1616
1658
|
"padding": "0px",
|
|
1617
1659
|
"text-align": "center",
|
|
1660
|
+
"flex": "1",
|
|
1618
1661
|
},
|
|
1619
1662
|
),
|
|
1620
1663
|
dbc.Card(
|
|
1621
1664
|
dbc.CardBody(
|
|
1622
1665
|
[
|
|
1623
|
-
html.
|
|
1624
|
-
|
|
1625
|
-
html.P(
|
|
1626
|
-
f"Execution Timestamps To Plot ({time_unit})",
|
|
1627
|
-
style={
|
|
1628
|
-
"textAlign": "center",
|
|
1629
|
-
"fontWeight": "bold",
|
|
1630
|
-
"margin": "0 200px",
|
|
1631
|
-
"margin-top": "-6px",
|
|
1632
|
-
},
|
|
1633
|
-
),
|
|
1634
|
-
html.P(
|
|
1635
|
-
"Grouping",
|
|
1636
|
-
style={
|
|
1637
|
-
"textAlign": "center",
|
|
1638
|
-
"fontWeight": "bold",
|
|
1639
|
-
"margin": "0 40px",
|
|
1640
|
-
},
|
|
1641
|
-
),
|
|
1642
|
-
html.P(
|
|
1643
|
-
"Average",
|
|
1644
|
-
style={
|
|
1645
|
-
"textAlign": "center",
|
|
1646
|
-
"fontWeight": "bold",
|
|
1647
|
-
"margin": "0 5px",
|
|
1648
|
-
"display": "none",
|
|
1649
|
-
},
|
|
1650
|
-
),
|
|
1651
|
-
],
|
|
1666
|
+
html.P(
|
|
1667
|
+
f"Execution Timestamps To Plot ({time_unit})",
|
|
1652
1668
|
style={
|
|
1653
|
-
"
|
|
1654
|
-
"
|
|
1655
|
-
"
|
|
1656
|
-
"margin-bottom": "10px",
|
|
1669
|
+
"textAlign": "center",
|
|
1670
|
+
"fontWeight": "bold",
|
|
1671
|
+
"margin-top": "-6px",
|
|
1657
1672
|
},
|
|
1658
1673
|
),
|
|
1659
1674
|
dcc.Store(id="data-points-store"),
|
|
@@ -1669,8 +1684,6 @@ app.layout = dbc.Container(
|
|
|
1669
1684
|
"fontSize": "24px",
|
|
1670
1685
|
"backgroundColor": "transparent",
|
|
1671
1686
|
"cursor": "pointer",
|
|
1672
|
-
"margin-top": "-22px",
|
|
1673
|
-
"margin-left": "-30px",
|
|
1674
1687
|
"margin-right": "10px",
|
|
1675
1688
|
},
|
|
1676
1689
|
),
|
|
@@ -1691,11 +1704,19 @@ app.layout = dbc.Container(
|
|
|
1691
1704
|
),
|
|
1692
1705
|
],
|
|
1693
1706
|
style={
|
|
1694
|
-
"
|
|
1695
|
-
"width": "
|
|
1707
|
+
"flex": "1",
|
|
1708
|
+
"min-width": "0",
|
|
1696
1709
|
"margin": "0px",
|
|
1697
1710
|
},
|
|
1698
1711
|
),
|
|
1712
|
+
html.Span(
|
|
1713
|
+
"Grouping",
|
|
1714
|
+
style={
|
|
1715
|
+
"fontWeight": "bold",
|
|
1716
|
+
"marginLeft": "15px",
|
|
1717
|
+
"whiteSpace": "nowrap",
|
|
1718
|
+
},
|
|
1719
|
+
),
|
|
1699
1720
|
html.Div(
|
|
1700
1721
|
[
|
|
1701
1722
|
html.Button(
|
|
@@ -1723,15 +1744,13 @@ app.layout = dbc.Container(
|
|
|
1723
1744
|
],
|
|
1724
1745
|
style={
|
|
1725
1746
|
"display": "inline-block",
|
|
1726
|
-
"margin-
|
|
1727
|
-
"margin-left": "15px",
|
|
1747
|
+
"margin-left": "5px",
|
|
1728
1748
|
},
|
|
1729
1749
|
),
|
|
1730
1750
|
dbc.Checkbox(
|
|
1731
1751
|
id="average-checkbox",
|
|
1732
1752
|
label="",
|
|
1733
1753
|
style={
|
|
1734
|
-
"margin-top": "-15px",
|
|
1735
1754
|
"margin-left": "40px",
|
|
1736
1755
|
"display": "none",
|
|
1737
1756
|
},
|
|
@@ -1746,11 +1765,11 @@ app.layout = dbc.Container(
|
|
|
1746
1765
|
]
|
|
1747
1766
|
),
|
|
1748
1767
|
style={
|
|
1749
|
-
"height": "100px",
|
|
1750
|
-
"width": "1070px",
|
|
1768
|
+
# "height": "100px",
|
|
1751
1769
|
"margin": "0px 10px 10px 10px",
|
|
1752
1770
|
"padding": "0px",
|
|
1753
1771
|
"text-align": "center",
|
|
1772
|
+
"flex": "1",
|
|
1754
1773
|
},
|
|
1755
1774
|
),
|
|
1756
1775
|
],
|
|
@@ -2377,6 +2396,180 @@ def generate_color_csv(n_clicks_ldst, n_clicks_spdp, graph):
|
|
|
2377
2396
|
return
|
|
2378
2397
|
|
|
2379
2398
|
|
|
2399
|
+
@app.callback(
|
|
2400
|
+
Input("button-carm-gflops", "n_clicks"),
|
|
2401
|
+
Input("graph-lines", "data"),
|
|
2402
|
+
prevent_initial_call=True,
|
|
2403
|
+
)
|
|
2404
|
+
def generate_gflops_csv(n_clicks, lines):
|
|
2405
|
+
global full_base_statistics_df, prv_trace_path, time_unit
|
|
2406
|
+
ctx = callback_context
|
|
2407
|
+
if not ctx.triggered:
|
|
2408
|
+
raise PreventUpdate
|
|
2409
|
+
|
|
2410
|
+
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
|
|
2411
|
+
if trigger_id != "button-carm-gflops":
|
|
2412
|
+
raise PreventUpdate
|
|
2413
|
+
|
|
2414
|
+
if lines is None:
|
|
2415
|
+
print("Graph lines data is None, cannot generate GFLOPS CSV.", flush=True)
|
|
2416
|
+
return
|
|
2417
|
+
|
|
2418
|
+
df: pd.DataFrame = full_base_statistics_df.copy()
|
|
2419
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2420
|
+
min_gflops = df["GFLOPS"].min()
|
|
2421
|
+
max_gflops = df["GFLOPS"].max()
|
|
2422
|
+
metadata_line = (
|
|
2423
|
+
f"#{timestamp}:CSV:RUNAPP:{prv_trace_path}:{time_unit}:window_in_null_gradient_mode:{min_gflops}:{max_gflops}"
|
|
2424
|
+
)
|
|
2425
|
+
|
|
2426
|
+
csv_df = df[["ThreadID", "Timestamp", "Duration", "GFLOPS"]].copy()
|
|
2427
|
+
csv_df["GFLOPS"] = csv_df["GFLOPS"].apply(lambda x: f"{x:.10f}")
|
|
2428
|
+
csv_df = csv_df.sort_values(
|
|
2429
|
+
["ThreadID", "Timestamp"],
|
|
2430
|
+
key=lambda col: ut.natural_sort_series(col) if col.name == "ThreadID" else col,
|
|
2431
|
+
)
|
|
2432
|
+
|
|
2433
|
+
output_dir = os.path.dirname(prv_trace_path)
|
|
2434
|
+
csv_filepath = os.path.join(output_dir, "carm_gflops.csv")
|
|
2435
|
+
with open(csv_filepath, "w") as f:
|
|
2436
|
+
f.write(metadata_line + "\n")
|
|
2437
|
+
csv_df.to_csv(f, index=False, header=False, sep="\t")
|
|
2438
|
+
|
|
2439
|
+
print("carm_gflops.csv file written.", flush=True)
|
|
2440
|
+
|
|
2441
|
+
return
|
|
2442
|
+
|
|
2443
|
+
|
|
2444
|
+
@app.callback(
|
|
2445
|
+
Input("button-carm-ai", "n_clicks"),
|
|
2446
|
+
Input("graph-lines", "data"),
|
|
2447
|
+
prevent_initial_call=True,
|
|
2448
|
+
)
|
|
2449
|
+
def generate_ai_csv(n_clicks, lines):
|
|
2450
|
+
global full_base_statistics_df, prv_trace_path, time_unit
|
|
2451
|
+
ctx = callback_context
|
|
2452
|
+
if not ctx.triggered:
|
|
2453
|
+
raise PreventUpdate
|
|
2454
|
+
|
|
2455
|
+
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
|
|
2456
|
+
if trigger_id != "button-carm-ai":
|
|
2457
|
+
raise PreventUpdate
|
|
2458
|
+
|
|
2459
|
+
if lines is None:
|
|
2460
|
+
print("Graph lines data is None, cannot generate AI CSV.", flush=True)
|
|
2461
|
+
return
|
|
2462
|
+
|
|
2463
|
+
df: pd.DataFrame = full_base_statistics_df.copy()
|
|
2464
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2465
|
+
min_ai = df["Arithmetic_Intensity"].min()
|
|
2466
|
+
max_ai = df["Arithmetic_Intensity"].max()
|
|
2467
|
+
metadata_line = (
|
|
2468
|
+
f"#{timestamp}:CSV:RUNAPP:{prv_trace_path}:{time_unit}:window_in_null_gradient_mode:{min_ai}:{max_ai}"
|
|
2469
|
+
)
|
|
2470
|
+
|
|
2471
|
+
csv_df = df[["ThreadID", "Timestamp", "Duration", "Arithmetic_Intensity"]].copy()
|
|
2472
|
+
csv_df["Arithmetic_Intensity"] = csv_df["Arithmetic_Intensity"].apply(lambda x: f"{x:.10f}")
|
|
2473
|
+
csv_df = csv_df.sort_values(
|
|
2474
|
+
["ThreadID", "Timestamp"],
|
|
2475
|
+
key=lambda col: ut.natural_sort_series(col) if col.name == "ThreadID" else col,
|
|
2476
|
+
)
|
|
2477
|
+
|
|
2478
|
+
output_dir = os.path.dirname(prv_trace_path)
|
|
2479
|
+
csv_filepath = os.path.join(output_dir, "carm_ai.csv")
|
|
2480
|
+
with open(csv_filepath, "w") as f:
|
|
2481
|
+
f.write(metadata_line + "\n")
|
|
2482
|
+
csv_df.to_csv(f, index=False, header=False, sep="\t")
|
|
2483
|
+
|
|
2484
|
+
print("carm_ai.csv file written.", flush=True)
|
|
2485
|
+
|
|
2486
|
+
return
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
@app.callback(
|
|
2490
|
+
Input("button-carm-roof-proximity", "n_clicks"),
|
|
2491
|
+
Input("graph-lines", "data"),
|
|
2492
|
+
prevent_initial_call=True,
|
|
2493
|
+
)
|
|
2494
|
+
def generate_roof_proximity_csv(n_clicks, lines):
|
|
2495
|
+
global full_base_statistics_df, prv_trace_path, time_unit
|
|
2496
|
+
ctx = callback_context
|
|
2497
|
+
if not ctx.triggered:
|
|
2498
|
+
raise PreventUpdate
|
|
2499
|
+
|
|
2500
|
+
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
|
|
2501
|
+
if trigger_id != "button-carm-roof-proximity":
|
|
2502
|
+
raise PreventUpdate
|
|
2503
|
+
|
|
2504
|
+
if lines is None:
|
|
2505
|
+
print("Graph lines data is None, cannot generate roof proximity CSV.", flush=True)
|
|
2506
|
+
return
|
|
2507
|
+
|
|
2508
|
+
df: pd.DataFrame = full_base_statistics_df.copy()
|
|
2509
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2510
|
+
output_dir = os.path.dirname(prv_trace_path)
|
|
2511
|
+
|
|
2512
|
+
ai = df["Arithmetic_Intensity"].values
|
|
2513
|
+
perf = df["GFLOPS"].values
|
|
2514
|
+
|
|
2515
|
+
level_names = {"L1": "l1", "L2": "l2", "L3": "l3", "DRAM": "dram"}
|
|
2516
|
+
|
|
2517
|
+
for level, suffix in level_names.items():
|
|
2518
|
+
if level not in lines:
|
|
2519
|
+
continue
|
|
2520
|
+
|
|
2521
|
+
roof = lines[level]
|
|
2522
|
+
start_x, start_y = roof["start"]
|
|
2523
|
+
ridge_x, ridge_y = roof["ridge"]
|
|
2524
|
+
end_x, end_y = roof["end"]
|
|
2525
|
+
|
|
2526
|
+
roof_vals = np.zeros_like(ai)
|
|
2527
|
+
|
|
2528
|
+
left = ai <= ridge_x
|
|
2529
|
+
if np.any(left):
|
|
2530
|
+
if ridge_x == start_x:
|
|
2531
|
+
roof_vals[left] = start_y
|
|
2532
|
+
else:
|
|
2533
|
+
slope = (ridge_y - start_y) / (ridge_x - start_x)
|
|
2534
|
+
roof_vals[left] = start_y + slope * (ai[left] - start_x)
|
|
2535
|
+
|
|
2536
|
+
right = ai > ridge_x
|
|
2537
|
+
if np.any(right):
|
|
2538
|
+
if end_x == ridge_x:
|
|
2539
|
+
roof_vals[right] = ridge_y
|
|
2540
|
+
else:
|
|
2541
|
+
slope = (end_y - ridge_y) / (end_x - ridge_x)
|
|
2542
|
+
roof_vals[right] = ridge_y + slope * (ai[right] - ridge_x)
|
|
2543
|
+
|
|
2544
|
+
valid = (ai > 0) & (perf > 0) & (roof_vals > 0)
|
|
2545
|
+
ratios = np.where(valid, np.minimum(perf / roof_vals, 1.0), 0.0)
|
|
2546
|
+
|
|
2547
|
+
metadata_line = f"#{timestamp}:CSV:RUNAPP:{prv_trace_path}:{time_unit}:window_in_null_gradient_mode:0.0:1.0"
|
|
2548
|
+
|
|
2549
|
+
rel_df = pd.DataFrame(
|
|
2550
|
+
{
|
|
2551
|
+
"ThreadID": df["ThreadID"],
|
|
2552
|
+
"Timestamp": df["Timestamp"],
|
|
2553
|
+
"Duration": df["Duration"],
|
|
2554
|
+
"Ratio": ratios,
|
|
2555
|
+
}
|
|
2556
|
+
)
|
|
2557
|
+
rel_df["Ratio"] = rel_df["Ratio"].apply(lambda x: f"{x:.10f}")
|
|
2558
|
+
rel_df = rel_df.sort_values(
|
|
2559
|
+
["ThreadID", "Timestamp"],
|
|
2560
|
+
key=lambda col: ut.natural_sort_series(col) if col.name == "ThreadID" else col,
|
|
2561
|
+
)
|
|
2562
|
+
|
|
2563
|
+
csv_filepath = os.path.join(output_dir, f"carm_rel_{suffix}.csv")
|
|
2564
|
+
with open(csv_filepath, "w") as f:
|
|
2565
|
+
f.write(metadata_line + "\n")
|
|
2566
|
+
rel_df.to_csv(f, index=False, header=False, sep="\t")
|
|
2567
|
+
|
|
2568
|
+
print(f"carm_rel_{suffix}.csv file written.", flush=True)
|
|
2569
|
+
|
|
2570
|
+
return
|
|
2571
|
+
|
|
2572
|
+
|
|
2380
2573
|
@app.callback(
|
|
2381
2574
|
Output("slider-components", "style"),
|
|
2382
2575
|
Output("paraver-sync-check", "disabled"),
|
|
@@ -4072,13 +4265,13 @@ app.clientside_callback(
|
|
|
4072
4265
|
|
|
4073
4266
|
|
|
4074
4267
|
def run_server() -> None:
|
|
4075
|
-
|
|
4076
|
-
|
|
4268
|
+
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
|
4269
|
+
logging.getLogger("dash.dash").setLevel(logging.ERROR)
|
|
4077
4270
|
|
|
4078
4271
|
# Force the host to a loopback address instead of letting Dash/Flask resolve the local hostname, which seems to
|
|
4079
4272
|
# cause issues in some distributions.
|
|
4080
4273
|
host = "127.0.0.1"
|
|
4081
|
-
print(f"Starting Dash app on {host}:{SELECTED_PORT}")
|
|
4274
|
+
print(f"Starting Dash app on http://{host}:{SELECTED_PORT}/")
|
|
4082
4275
|
|
|
4083
4276
|
from werkzeug.middleware.profiler import ProfilerMiddleware
|
|
4084
4277
|
|
|
@@ -3,30 +3,25 @@ body {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
/* Style for making the slider track thicker and darker */
|
|
7
|
-
.
|
|
8
|
-
height: 11px; /* Thicker
|
|
9
|
-
|
|
10
|
-
background-color: #000000; /* Darker track color */
|
|
6
|
+
/* Style for making the slider filled range (track) thicker and darker */
|
|
7
|
+
.dash-slider-range {
|
|
8
|
+
height: 11px; /* Thicker range */
|
|
9
|
+
background-color: #000000; /* Darker range color */
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
|
|
14
12
|
/* Style for making the slider thumb (handle) thicker and darker */
|
|
15
|
-
.
|
|
16
|
-
top: 7px;
|
|
13
|
+
.dash-slider-thumb {
|
|
17
14
|
width: 15px; /* Bigger handle width */
|
|
18
15
|
height: 15px; /* Bigger handle height */
|
|
19
16
|
background-color: #333; /* Darker handle color */
|
|
20
17
|
border-color: #333; /* Darker border color for the handle */
|
|
21
18
|
}
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
height: 10px; /*
|
|
25
|
-
background-color: #b8b6b6; /* Darker
|
|
26
|
-
border-color: #b8b6b6; /* Darker border color for the handle */
|
|
19
|
+
/* Style for making the slider track (rail) thicker and darker */
|
|
20
|
+
.dash-slider-track {
|
|
21
|
+
height: 10px; /* Thicker track */
|
|
22
|
+
background-color: #b8b6b6; /* Darker track color */
|
|
27
23
|
}
|
|
28
|
-
|
|
29
|
-
.rc-slider-dot {
|
|
24
|
+
.dash-slider-dot {
|
|
30
25
|
position: absolute;
|
|
31
26
|
bottom: -2px;
|
|
32
27
|
margin-left: -2px;
|
|
@@ -37,9 +32,9 @@ body {
|
|
|
37
32
|
cursor: pointer;
|
|
38
33
|
border-radius: 50%;
|
|
39
34
|
vertical-align: middle;
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
}
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
/* Color picker — remove border-radius rounding */
|
|
38
|
+
.chrome-picker {
|
|
44
39
|
border-radius: 0% !important;
|
|
45
40
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carm-paraver
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1.dev0
|
|
4
4
|
Summary: Dash-based CARM analysis for Paraver traces
|
|
5
5
|
Author: CARM Contributors
|
|
6
6
|
Requires-Python: >=3.9
|
|
@@ -19,6 +19,8 @@ Dynamic: license-file
|
|
|
19
19
|
|
|
20
20
|
This GUI allows the analysis of [Paraver](https://tools.bsc.es/paraver) traces on the Cache-Aware Roofline Model (CARM) for floating-point operations. It can be launched from the Paraver interface and send labeled events back to Paraver for visualization.
|
|
21
21
|
|
|
22
|
+
The CARM allows for roofline analysis of your application, displaying its computational bursts as points on the roofline. This can be used to identify bottlenecks and optimization opportunities for the respective code section. Points on the memory-bound or the compute-bound sections of the roof benefit from different optimization strategies, and the distance of your points to the roofline can be used to identify how much performance can be gained by optimizing your code.
|
|
23
|
+
|
|
22
24
|
# Requirements
|
|
23
25
|
- Python (tested with 3.9.25, 3.10.12, 3.12.3)
|
|
24
26
|
- [Paraver, Extrae](https://tools.bsc.es/downloads)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|