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.
Files changed (55) hide show
  1. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/PKG-INFO +3 -1
  2. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/README.md +2 -0
  3. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/Paraver_CARM.py +284 -91
  4. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/style.css +13 -18
  5. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/PKG-INFO +3 -1
  6. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/pyproject.toml +1 -1
  7. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/LICENSE +0 -0
  8. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/GUI_utils.py +0 -0
  9. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/__init__.py +0 -0
  10. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/__main__.py +0 -0
  11. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/analysis_helpers.py +0 -0
  12. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/CARM_icon3.svg +0 -0
  13. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/CHAMP_logo.svg +0 -0
  14. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/__init__.py +0 -0
  15. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/bsc.svg +0 -0
  16. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/carm_bsc.png +0 -0
  17. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/carm_bsc.svg +0 -0
  18. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/assets/menu_icon.png +0 -0
  19. {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
  20. {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
  21. {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
  22. {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
  23. {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
  24. {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
  25. {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
  26. {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
  27. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_Loads.cfg +0 -0
  28. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/Intel_Stores.cfg +0 -0
  29. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel/__init__.py +0 -0
  30. {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
  31. {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
  32. {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
  33. {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
  34. {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
  35. {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
  36. {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
  37. {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
  38. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_Loads.cfg +0 -0
  39. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/Intel_Stores.cfg +0 -0
  40. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/IntelV2/__init__.py +0 -0
  41. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_DP.cfg +0 -0
  42. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_DPV2.cfg +0 -0
  43. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_DP_Extrae.xml +0 -0
  44. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_SPV2.cfg +0 -0
  45. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/Intel_CARM_SP_Extrae.xml +0 -0
  46. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/paraver_carm_configs/__init__.py +0 -0
  47. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver/sample_data/roofline/MN5_roofline.csv +0 -0
  48. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/SOURCES.txt +0 -0
  49. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/dependency_links.txt +0 -0
  50. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/entry_points.txt +0 -0
  51. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/requires.txt +0 -0
  52. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/carm_paraver.egg-info/top_level.txt +0 -0
  53. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/setup.cfg +0 -0
  54. {carm_paraver-1.0.0.dev1 → carm_paraver-1.0.1.dev0}/tests/test_analysis_helpers.py +0 -0
  55. {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.0.dev1
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
- "\nAdd these counters to your XML file to monitor all possible events, \nthese counters should remain in a single "
588
- "counter set:\n\n "
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
- dbc.Button(
1468
- "Send Timestamps Roof Labels",
1469
- id="button-roof-labels",
1470
- className="mb-2",
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
- dbc.Button(
1475
- "Send Timestamps LD/ST Percentage Colors",
1476
- id="button-carm-ldst-colors",
1477
- className="mb-2",
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
- dbc.Button(
1482
- "Send Timestamps SP/DP Percentage Colors",
1483
- id="button-carm-spdp-colors",
1484
- className="mb-2",
1485
- style={"width": "100%"},
1486
- n_clicks=0,
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
- "display": "inline-block",
1607
- "width": "770px",
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.Div(
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
- "display": "flex",
1654
- "justify-content": "center",
1655
- "align-items": "center",
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
- "display": "inline-block",
1695
- "width": "710px",
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-top": "-15px",
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
- log = logging.getLogger("werkzeug")
4076
- log.setLevel(logging.ERROR)
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
- .rc-slider-track {
8
- height: 11px; /* Thicker track */
9
- width: 100px;
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
- .rc-slider-handle {
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
- .rc-slider-rail {
24
- height: 10px; /* Bigger handle height */
25
- background-color: #b8b6b6; /* Darker handle color */
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
- .cQJXpB .chrome-picker {
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.0.dev1
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)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "carm-paraver"
7
- version = "1.0.0.dev1"
7
+ version = "1.0.1.dev0"
8
8
  description = "Dash-based CARM analysis for Paraver traces"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"