ttnn-visualizer 0.70.0__py3-none-any.whl → 0.71.0__py3-none-any.whl

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.
ttnn_visualizer/app.py CHANGED
@@ -28,7 +28,12 @@ from ttnn_visualizer.exceptions import (
28
28
  )
29
29
  from ttnn_visualizer.instances import create_instance_from_local_paths
30
30
  from ttnn_visualizer.settings import Config, DefaultConfig
31
- from ttnn_visualizer.utils import find_gunicorn_path, get_app_data_directory
31
+ from ttnn_visualizer.utils import (
32
+ find_gunicorn_path,
33
+ get_app_data_directory,
34
+ get_report_data_directory,
35
+ migrate_old_data_directory,
36
+ )
32
37
  from werkzeug.debug import DebuggedApplication
33
38
  from werkzeug.middleware.proxy_fix import ProxyFix
34
39
 
@@ -123,7 +128,10 @@ def extensions(app: flask.Flask):
123
128
  if app.config["USE_WEBSOCKETS"]:
124
129
  socketio.init_app(app)
125
130
 
131
+ # Create app data and report directories
126
132
  Path(app.config["APP_DATA_DIRECTORY"]).mkdir(parents=True, exist_ok=True)
133
+ Path(app.config["LOCAL_DATA_DIRECTORY"]).mkdir(parents=True, exist_ok=True)
134
+ Path(app.config["REMOTE_DATA_DIRECTORY"]).mkdir(parents=True, exist_ok=True)
127
135
  db.init_app(app)
128
136
 
129
137
  if app.config["USE_WEBSOCKETS"]:
@@ -345,6 +353,29 @@ def main():
345
353
  )
346
354
  config.SQLALCHEMY_DATABASE_URI = f"sqlite:///{_db_file_path}"
347
355
 
356
+ # Check for and migrate old data from site-packages if needed
357
+ # Only migrate if environment variables are not explicitly set
358
+ if not os.getenv("APP_DATA_DIRECTORY") and not os.getenv("REPORT_DATA_DIRECTORY"):
359
+ # Calculate what the old directories would have been (in site-packages)
360
+ old_app_data_dir = config.APPLICATION_DIR
361
+ old_report_data_dir = str(
362
+ Path(config.APPLICATION_DIR).joinpath("ttnn_visualizer", "data")
363
+ )
364
+
365
+ # Get new directories (already calculated in config)
366
+ new_app_data_dir = config.APP_DATA_DIRECTORY
367
+ new_report_data_dir = config.REPORT_DATA_DIRECTORY
368
+
369
+ # Only migrate if we're not in TT-Metal mode (migration doesn't apply there)
370
+ if not config.TT_METAL_HOME:
371
+ migrate_old_data_directory(
372
+ old_app_data_dir,
373
+ old_report_data_dir,
374
+ new_app_data_dir,
375
+ new_report_data_dir,
376
+ config.DB_VERSION,
377
+ )
378
+
348
379
  display_mode_info_without_db(config)
349
380
 
350
381
  # If profiler/performance paths are provided, create an instance
@@ -423,26 +423,30 @@ class OpsPerformanceReportQueries:
423
423
  ]
424
424
 
425
425
  STACKED_REPORT_COLUMNS = [
426
- "percent",
427
- "op_code",
428
- "device_time_sum_us",
429
- "ops_count",
430
- "flops_min",
431
- "flops_max",
432
- "flops_mean",
433
- "flops_std",
426
+ "%",
427
+ "OP Code Joined",
428
+ "Device_Time_Sum_us",
429
+ "Ops_count",
430
+ "Op_Category",
431
+ "Flops_min",
432
+ "Flops_max",
433
+ "Flops_mean",
434
+ "Flops_std",
435
+ "Flops_weighted_mean",
434
436
  ]
435
437
 
436
438
  STACKED_REPORT_COLUMNS_WITH_DEVICE = [
437
- "percent",
438
- "op_code",
439
- "device",
440
- "device_time_sum_us",
441
- "ops_count",
442
- "flops_min",
443
- "flops_max",
444
- "flops_mean",
445
- "flops_std",
439
+ "%",
440
+ "OP Code Joined",
441
+ "Device",
442
+ "Device_Time_Sum_us",
443
+ "Ops_count",
444
+ "Op_Category",
445
+ "Flops_min",
446
+ "Flops_max",
447
+ "Flops_mean",
448
+ "Flops_std",
449
+ "Flops_weighted_mean",
446
450
  ]
447
451
 
448
452
  PASSTHROUGH_COLUMNS = {
@@ -455,6 +459,7 @@ class OpsPerformanceReportQueries:
455
459
  DEFAULT_PRINT_SIGNPOSTS = True
456
460
  DEFAULT_MIN_PERCENTAGE = 0.5
457
461
  DEFAULT_ID_RANGE = None
462
+ DEFAULT_ARCH = None
458
463
  DEFAULT_NO_ADVICE = False
459
464
  DEFAULT_TRACING_MODE = False
460
465
  DEFAULT_RAW_OP_CODES = True
@@ -462,29 +467,36 @@ class OpsPerformanceReportQueries:
462
467
  DEFAULT_NO_STACKED_REPORT = False
463
468
  DEFAULT_NO_STACK_BY_IN0 = True
464
469
  DEFAULT_MERGE_DEVICES = True
470
+ DEFAULT_STACKED_CSV_FILE = None # Stacked report CSV output file
471
+ DEFAULT_NO_SUMMARY = False
472
+ DEFAULT_SUMMARY_FILE = None # Stacked report output file
473
+ DEFAULT_CLASSIC_COLORS = False # Colour scheme for plotted stacked report
474
+ DEFAULT_GROUP_BY = None # Group by method for stacked report
465
475
 
466
476
  @classmethod
467
477
  def generate_report(cls, instance, **kwargs):
468
478
  raw_csv = OpsPerformanceQueries.get_raw_csv(instance)
469
479
  csv_file = StringIO(raw_csv)
470
- csv_output_file = tempfile.mktemp(suffix=".csv")
471
- csv_stacked_output_file = tempfile.mktemp(suffix=".csv")
480
+ csv_summary_file = tempfile.NamedTemporaryFile(delete=False)
481
+ csv_output_file = tempfile.NamedTemporaryFile(suffix=".csv", delete=False)
482
+ # The perf_report library creates files with format: {csv_summary_name}.csv and {csv_summary_name}.png
483
+ summary_csv_path = f"{csv_summary_file.name}.csv"
484
+ summary_png_path = f"{csv_summary_file.name}.png"
485
+ csv_summary_file.close()
486
+ csv_output_file.close()
487
+
472
488
  start_signpost = kwargs.get("start_signpost", cls.DEFAULT_START_SIGNPOST)
473
489
  end_signpost = kwargs.get("end_signpost", cls.DEFAULT_END_SIGNPOST)
474
490
  ignore_signposts = cls.DEFAULT_IGNORE_SIGNPOSTS
475
491
  print_signposts = kwargs.get("print_signposts", cls.DEFAULT_PRINT_SIGNPOSTS)
476
- stack_by_in0 = kwargs.get("stack_by_in0", cls.DEFAULT_NO_STACK_BY_IN0)
477
492
  no_host_ops = kwargs.get("hide_host_ops", cls.DEFAULT_NO_HOST_OPS)
478
493
  merge_devices = kwargs.get("merge_devices", cls.DEFAULT_MERGE_DEVICES)
479
494
  tracing_mode = kwargs.get("tracing_mode", cls.DEFAULT_TRACING_MODE)
495
+ group_by = kwargs.get("group_by", cls.DEFAULT_GROUP_BY)
480
496
 
481
497
  if start_signpost or end_signpost:
482
498
  ignore_signposts = False
483
499
 
484
- # perf_report currently generates a PNG alongside the CSV using the same temp name - we'll just delete it afterwards
485
- stacked_png_file = csv_stacked_output_file + ".png"
486
- stacked_csv_file = csv_stacked_output_file + ".csv"
487
-
488
500
  try:
489
501
  perf_report.generate_perf_report(
490
502
  [csv_file],
@@ -494,14 +506,19 @@ class OpsPerformanceReportQueries:
494
506
  print_signposts,
495
507
  cls.DEFAULT_MIN_PERCENTAGE,
496
508
  cls.DEFAULT_ID_RANGE,
497
- csv_output_file,
509
+ cls.DEFAULT_ARCH,
510
+ csv_output_file.name,
498
511
  cls.DEFAULT_NO_ADVICE,
499
512
  tracing_mode,
500
513
  cls.DEFAULT_RAW_OP_CODES,
501
514
  no_host_ops,
515
+ cls.DEFAULT_NO_SUMMARY,
516
+ group_by,
517
+ cls.DEFAULT_CLASSIC_COLORS,
518
+ csv_summary_file.name,
502
519
  cls.DEFAULT_NO_STACKED_REPORT,
503
- stack_by_in0,
504
- csv_stacked_output_file,
520
+ cls.DEFAULT_NO_STACK_BY_IN0,
521
+ cls.DEFAULT_STACKED_CSV_FILE,
505
522
  not merge_devices,
506
523
  )
507
524
  except Exception as e:
@@ -533,9 +550,9 @@ class OpsPerformanceReportQueries:
533
550
 
534
551
  report = []
535
552
 
536
- if os.path.exists(csv_output_file):
553
+ if os.path.exists(csv_output_file.name):
537
554
  try:
538
- with open(csv_output_file, newline="") as csvfile:
555
+ with open(csv_output_file.name, newline="") as csvfile:
539
556
  reader = csv.reader(csvfile, delimiter=",")
540
557
  next(reader, None)
541
558
  for row in reader:
@@ -575,13 +592,13 @@ class OpsPerformanceReportQueries:
575
592
  except csv.Error as e:
576
593
  raise DataFormatError() from e
577
594
  finally:
578
- os.unlink(csv_output_file)
595
+ os.unlink(csv_output_file.name)
579
596
 
580
597
  stacked_report = []
581
598
 
582
- if os.path.exists(stacked_csv_file):
599
+ if os.path.exists(summary_csv_path):
583
600
  try:
584
- with open(stacked_csv_file, newline="") as csvfile:
601
+ with open(summary_csv_path, newline="") as csvfile:
585
602
  reader = csv.reader(csvfile, delimiter=",")
586
603
  next(reader, None)
587
604
 
@@ -599,6 +616,11 @@ class OpsPerformanceReportQueries:
599
616
  if index < len(row)
600
617
  }
601
618
 
619
+ # Map "OP Code Joined" to "op_code" for consistency with non-stacked report
620
+ if "OP Code Joined" in processed_row:
621
+ processed_row["op_code"] = processed_row["OP Code Joined"]
622
+ del processed_row["OP Code Joined"]
623
+
602
624
  if "op_code" in processed_row and any(
603
625
  processed_row["op_code"] in signpost["op_code"]
604
626
  for signpost in signposts
@@ -611,9 +633,14 @@ class OpsPerformanceReportQueries:
611
633
  except csv.Error as e:
612
634
  raise DataFormatError() from e
613
635
  finally:
614
- os.unlink(stacked_csv_file)
615
- if os.path.exists(stacked_png_file):
616
- os.unlink(stacked_png_file)
636
+ # Clean up the files created by perf_report library
637
+ if os.path.exists(summary_csv_path):
638
+ os.unlink(summary_csv_path)
639
+ if os.path.exists(summary_png_path):
640
+ os.unlink(summary_png_path)
641
+ # Clean up the original temp file that was just used for its name
642
+ if os.path.exists(csv_summary_file.name):
643
+ os.unlink(csv_summary_file.name)
617
644
 
618
645
  return {
619
646
  "report": report,
@@ -9,6 +9,7 @@ from dotenv import load_dotenv
9
9
  from sqlalchemy.pool import NullPool
10
10
  from ttnn_visualizer.utils import (
11
11
  get_app_data_directory,
12
+ get_report_data_directory,
12
13
  is_running_in_container,
13
14
  str_to_bool,
14
15
  )
@@ -36,20 +37,21 @@ class DefaultConfig(object):
36
37
 
37
38
  # Path Settings
38
39
  DB_VERSION = "0.29.0" # App version when DB schema last changed
40
+ APPLICATION_DIR = os.path.abspath(os.path.join(__file__, "..", os.pardir))
41
+ TT_METAL_HOME = os.getenv("TT_METAL_HOME", None)
42
+ APP_DATA_DIRECTORY = os.getenv(
43
+ "APP_DATA_DIRECTORY",
44
+ get_app_data_directory(TT_METAL_HOME, APPLICATION_DIR),
45
+ )
39
46
  REPORT_DATA_DIRECTORY = os.getenv(
40
- "REPORT_DATA_DIRECTORY", Path(__file__).parent.absolute().joinpath("data")
47
+ "REPORT_DATA_DIRECTORY",
48
+ get_report_data_directory(TT_METAL_HOME, APPLICATION_DIR),
41
49
  )
42
50
  LOCAL_DATA_DIRECTORY = Path(REPORT_DATA_DIRECTORY).joinpath("local")
43
51
  REMOTE_DATA_DIRECTORY = Path(REPORT_DATA_DIRECTORY).joinpath("remote")
44
52
  PROFILER_DIRECTORY_NAME = "profiler-reports"
45
53
  PERFORMANCE_DIRECTORY_NAME = "performance-reports"
46
54
  NPE_DIRECTORY_NAME = "npe-reports"
47
- APPLICATION_DIR = os.path.abspath(os.path.join(__file__, "..", os.pardir))
48
- TT_METAL_HOME = os.getenv("TT_METAL_HOME", None)
49
- APP_DATA_DIRECTORY = os.getenv(
50
- "APP_DATA_DIRECTORY",
51
- get_app_data_directory(TT_METAL_HOME, APPLICATION_DIR),
52
- )
53
55
 
54
56
  STATIC_ASSETS_DIR = Path(APPLICATION_DIR).joinpath("ttnn_visualizer", "static")
55
57
  SEND_FILE_MAX_AGE_DEFAULT = 0
@@ -27,7 +27,6 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
  TEST_CONFIG_FILE = "config.json"
29
29
  TEST_PROFILER_FILE = "profile_log_device.csv"
30
- REPORT_DATA_DIRECTORY = Path(__file__).parent.absolute().joinpath("data")
31
30
 
32
31
 
33
32
  def start_background_task(task, *args):
@@ -853,7 +852,7 @@ def sync_remote_profiler_folders(
853
852
  """Main function to sync test folders, handles both compressed and individual syncs."""
854
853
  profiler_folder = Path(remote_folder_path).name
855
854
  destination_dir = Path(
856
- REPORT_DATA_DIRECTORY,
855
+ current_app.config["REPORT_DATA_DIRECTORY"],
857
856
  path_prefix,
858
857
  remote_connection.host,
859
858
  current_app.config["PROFILER_DIRECTORY_NAME"],
@@ -877,7 +876,7 @@ def sync_remote_performance_folders(
877
876
  remote_folder_path = performance.remotePath
878
877
  profile_folder = Path(remote_folder_path).name
879
878
  destination_dir = Path(
880
- REPORT_DATA_DIRECTORY,
879
+ current_app.config["REPORT_DATA_DIRECTORY"],
881
880
  path_prefix,
882
881
  remote_connection.host,
883
882
  current_app.config["PERFORMANCE_DIRECTORY_NAME"],
@@ -1 +1 @@
1
- import{I as s}from"./index-BmDjQHI0.js";import{I as r}from"./index-CzNKtOwn.js";import{p as n,I as c}from"./index-SMzJpwbG.js";function p(t,a){const o=n(t);return a===c.STANDARD?s[o]:r[o]}export{s as IconSvgPaths16,r as IconSvgPaths20,p as getIconPaths};
1
+ import{I as s}from"./index-BmDjQHI0.js";import{I as r}from"./index-CzNKtOwn.js";import{p as n,I as c}from"./index-8RVye9cY.js";function p(t,a){const o=n(t);return a===c.STANDARD?s[o]:r[o]}export{s as IconSvgPaths16,r as IconSvgPaths20,p as getIconPaths};
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/allPaths-D6qA1aj4.js","assets/index-BmDjQHI0.js","assets/index-CzNKtOwn.js","assets/index-8RVye9cY.js","assets/index-BVzPYDVR.css"])))=>i.map(i=>d[i]);
2
+ import{_ as e}from"./index-8RVye9cY.js";const s=async(t,a)=>{const{getIconPaths:o}=await e(async()=>{const{getIconPaths:r}=await import("./allPaths-D6qA1aj4.js");return{getIconPaths:r}},__vite__mapDeps([0,1,2,3,4]));return o(t,a)};export{s as allPathsLoader};