ttnn-visualizer 0.63.1__py3-none-any.whl → 0.65.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 +65 -2
- ttnn_visualizer/csv_queries.py +41 -12
- ttnn_visualizer/settings.py +12 -4
- ttnn_visualizer/static/assets/{allPaths-dy9nsQF7.js → allPaths-B6-2k5TG.js} +1 -1
- ttnn_visualizer/static/assets/allPathsLoader-Cylj0kPO.js +2 -0
- ttnn_visualizer/static/assets/{index-BzMUwfVf.css → index-C7m_PE7l.css} +1 -1
- ttnn_visualizer/static/assets/{index-B_nutJ_M.js → index-DMzz7de9.js} +238 -248
- ttnn_visualizer/static/assets/{splitPathsBySizeLoader-357aoIrt.js → splitPathsBySizeLoader-BVbD9Udb.js} +1 -1
- ttnn_visualizer/static/index.html +2 -2
- ttnn_visualizer/tests/test_utils.py +258 -2
- ttnn_visualizer/utils.py +63 -0
- ttnn_visualizer/views.py +58 -2
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/METADATA +2 -2
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/RECORD +19 -19
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/licenses/LICENSE +0 -2
- ttnn_visualizer/static/assets/allPathsLoader-CQM4eIEo.js +0 -2
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/WHEEL +0 -0
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/entry_points.txt +0 -0
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/licenses/LICENSE_understanding.txt +0 -0
- {ttnn_visualizer-0.63.1.dist-info → ttnn_visualizer-0.65.0.dist-info}/top_level.txt +0 -0
ttnn_visualizer/app.py
CHANGED
|
@@ -25,7 +25,7 @@ from ttnn_visualizer.exceptions import (
|
|
|
25
25
|
)
|
|
26
26
|
from ttnn_visualizer.instances import create_instance_from_local_paths
|
|
27
27
|
from ttnn_visualizer.settings import Config, DefaultConfig
|
|
28
|
-
from ttnn_visualizer.utils import find_gunicorn_path
|
|
28
|
+
from ttnn_visualizer.utils import find_gunicorn_path, get_app_data_directory
|
|
29
29
|
from werkzeug.debug import DebuggedApplication
|
|
30
30
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
31
31
|
|
|
@@ -55,8 +55,9 @@ def create_app(settings_override=None):
|
|
|
55
55
|
static_folder=config.STATIC_ASSETS_DIR,
|
|
56
56
|
static_url_path=f"{config.BASE_PATH}static",
|
|
57
57
|
)
|
|
58
|
-
logging.basicConfig(level=app.config.get("LOG_LEVEL", "INFO"))
|
|
59
58
|
|
|
59
|
+
# logging.basicConfig(level=app.config.get("LOG_LEVEL", "DEBUG"))
|
|
60
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
60
61
|
app.config.from_object(config)
|
|
61
62
|
|
|
62
63
|
if settings_override:
|
|
@@ -118,6 +119,8 @@ def extensions(app: flask.Flask):
|
|
|
118
119
|
flask_static_digest.init_app(app)
|
|
119
120
|
if app.config["USE_WEBSOCKETS"]:
|
|
120
121
|
socketio.init_app(app)
|
|
122
|
+
|
|
123
|
+
Path(app.config["APP_DATA_DIRECTORY"]).mkdir(parents=True, exist_ok=True)
|
|
121
124
|
db.init_app(app)
|
|
122
125
|
|
|
123
126
|
if app.config["USE_WEBSOCKETS"]:
|
|
@@ -192,6 +195,23 @@ def parse_args():
|
|
|
192
195
|
parser.add_argument(
|
|
193
196
|
"--tt-metal-home", help="Specify a TT-Metal home path", default=None
|
|
194
197
|
)
|
|
198
|
+
parser.add_argument(
|
|
199
|
+
"--host",
|
|
200
|
+
type=str,
|
|
201
|
+
help="Host to bind to (default: auto-detected based on environment)",
|
|
202
|
+
default=None,
|
|
203
|
+
)
|
|
204
|
+
parser.add_argument(
|
|
205
|
+
"--port",
|
|
206
|
+
type=str,
|
|
207
|
+
help="Port to bind to (default: 8000)",
|
|
208
|
+
default=None,
|
|
209
|
+
)
|
|
210
|
+
parser.add_argument(
|
|
211
|
+
"--server",
|
|
212
|
+
action="store_true",
|
|
213
|
+
help="Bind to all network interfaces (0.0.0.0) and enable server mode. Useful for servers and VMs",
|
|
214
|
+
)
|
|
195
215
|
parser.add_argument(
|
|
196
216
|
"-d",
|
|
197
217
|
"--daemon",
|
|
@@ -250,13 +270,56 @@ def main():
|
|
|
250
270
|
|
|
251
271
|
args = parse_args()
|
|
252
272
|
|
|
273
|
+
# Handle host/port CLI overrides
|
|
274
|
+
# Priority: CLI args > env vars > auto-detection (in settings.py)
|
|
275
|
+
# Note: We need to set env vars before creating Config, but also
|
|
276
|
+
# manually update the config object in case it was already instantiated
|
|
277
|
+
if args.host:
|
|
278
|
+
os.environ["HOST"] = args.host
|
|
279
|
+
print(f"🌐 Binding to host: {args.host} (from --host flag)")
|
|
280
|
+
elif args.server:
|
|
281
|
+
os.environ["HOST"] = "0.0.0.0"
|
|
282
|
+
os.environ["SERVER_MODE"] = "true"
|
|
283
|
+
print("🌐 Binding to all interfaces (0.0.0.0) via --server flag")
|
|
284
|
+
print("🖥️ Server mode enabled")
|
|
285
|
+
|
|
286
|
+
if args.port:
|
|
287
|
+
os.environ["PORT"] = args.port
|
|
288
|
+
print(f"🔌 Binding to port: {args.port}")
|
|
289
|
+
|
|
253
290
|
config = cast(DefaultConfig, Config())
|
|
291
|
+
|
|
292
|
+
# Apply CLI overrides directly to config object
|
|
293
|
+
# (Config is a singleton that may have been created before we set env vars)
|
|
294
|
+
if args.host:
|
|
295
|
+
config.HOST = args.host
|
|
296
|
+
elif args.server:
|
|
297
|
+
config.HOST = "0.0.0.0"
|
|
298
|
+
config.SERVER_MODE = True
|
|
299
|
+
|
|
300
|
+
if args.port:
|
|
301
|
+
config.PORT = args.port
|
|
302
|
+
|
|
303
|
+
# Recalculate GUNICORN_BIND with the updated values
|
|
304
|
+
config.GUNICORN_BIND = f"{config.HOST}:{config.PORT}"
|
|
305
|
+
|
|
254
306
|
instance_id = None
|
|
255
307
|
|
|
256
308
|
# Display mode information first (using config only, no DB needed)
|
|
257
309
|
if args.tt_metal_home:
|
|
310
|
+
os.environ["TT_METAL_HOME"] = args.tt_metal_home
|
|
258
311
|
config.TT_METAL_HOME = args.tt_metal_home
|
|
259
312
|
|
|
313
|
+
if not os.getenv("APP_DATA_DIRECTORY"):
|
|
314
|
+
config.APP_DATA_DIRECTORY = get_app_data_directory(
|
|
315
|
+
args.tt_metal_home, config.APPLICATION_DIR
|
|
316
|
+
)
|
|
317
|
+
# Recalculate database path with new APP_DATA_DIRECTORY
|
|
318
|
+
_db_file_path = str(
|
|
319
|
+
Path(config.APP_DATA_DIRECTORY) / f"ttnn_{config.DB_VERSION}.db"
|
|
320
|
+
)
|
|
321
|
+
config.SQLALCHEMY_DATABASE_URI = f"sqlite:///{_db_file_path}"
|
|
322
|
+
|
|
260
323
|
display_mode_info_without_db(config)
|
|
261
324
|
|
|
262
325
|
# If profiler/performance paths are provided, create an instance
|
ttnn_visualizer/csv_queries.py
CHANGED
|
@@ -398,6 +398,7 @@ class OpsPerformanceReportQueries:
|
|
|
398
398
|
"total_percent",
|
|
399
399
|
"bound",
|
|
400
400
|
"op_code",
|
|
401
|
+
"device",
|
|
401
402
|
"device_time",
|
|
402
403
|
"op_to_op_gap",
|
|
403
404
|
"cores",
|
|
@@ -430,12 +431,26 @@ class OpsPerformanceReportQueries:
|
|
|
430
431
|
"flops_std",
|
|
431
432
|
]
|
|
432
433
|
|
|
434
|
+
STACKED_REPORT_COLUMNS_WITH_DEVICE = [
|
|
435
|
+
"percent",
|
|
436
|
+
"op_code",
|
|
437
|
+
"device",
|
|
438
|
+
"device_time_sum_us",
|
|
439
|
+
"ops_count",
|
|
440
|
+
"flops_min",
|
|
441
|
+
"flops_max",
|
|
442
|
+
"flops_mean",
|
|
443
|
+
"flops_std",
|
|
444
|
+
]
|
|
445
|
+
|
|
433
446
|
PASSTHROUGH_COLUMNS = {
|
|
434
447
|
"pm_ideal_ns": "PM IDEAL [ns]",
|
|
435
448
|
}
|
|
436
449
|
|
|
437
|
-
|
|
450
|
+
DEFAULT_START_SIGNPOST = None
|
|
451
|
+
DEFAULT_END_SIGNPOST = None
|
|
438
452
|
DEFAULT_IGNORE_SIGNPOSTS = True
|
|
453
|
+
DEFAULT_PRINT_SIGNPOSTS = True
|
|
439
454
|
DEFAULT_MIN_PERCENTAGE = 0.5
|
|
440
455
|
DEFAULT_ID_RANGE = None
|
|
441
456
|
DEFAULT_NO_ADVICE = False
|
|
@@ -444,6 +459,7 @@ class OpsPerformanceReportQueries:
|
|
|
444
459
|
DEFAULT_NO_HOST_OPS = False
|
|
445
460
|
DEFAULT_NO_STACKED_REPORT = False
|
|
446
461
|
DEFAULT_NO_STACK_BY_IN0 = True
|
|
462
|
+
DEFAULT_MERGE_DEVICES = True
|
|
447
463
|
|
|
448
464
|
@classmethod
|
|
449
465
|
def generate_report(cls, instance, **kwargs):
|
|
@@ -451,31 +467,39 @@ class OpsPerformanceReportQueries:
|
|
|
451
467
|
csv_file = StringIO(raw_csv)
|
|
452
468
|
csv_output_file = tempfile.mktemp(suffix=".csv")
|
|
453
469
|
csv_stacked_output_file = tempfile.mktemp(suffix=".csv")
|
|
454
|
-
|
|
470
|
+
start_signpost = kwargs.get("start_signpost", cls.DEFAULT_START_SIGNPOST)
|
|
471
|
+
end_signpost = kwargs.get("end_signpost", cls.DEFAULT_END_SIGNPOST)
|
|
455
472
|
ignore_signposts = cls.DEFAULT_IGNORE_SIGNPOSTS
|
|
473
|
+
print_signposts = kwargs.get("print_signposts", cls.DEFAULT_PRINT_SIGNPOSTS)
|
|
456
474
|
stack_by_in0 = kwargs.get("stack_by_in0", cls.DEFAULT_NO_STACK_BY_IN0)
|
|
475
|
+
no_host_ops = kwargs.get("hide_host_ops", cls.DEFAULT_NO_HOST_OPS)
|
|
476
|
+
merge_devices = kwargs.get("merge_devices", cls.DEFAULT_MERGE_DEVICES)
|
|
457
477
|
|
|
458
|
-
if
|
|
478
|
+
if start_signpost or end_signpost:
|
|
459
479
|
ignore_signposts = False
|
|
460
480
|
|
|
461
481
|
# perf_report currently generates a PNG alongside the CSV using the same temp name - we'll just delete it afterwards
|
|
462
|
-
stacked_png_file =
|
|
482
|
+
stacked_png_file = csv_stacked_output_file + ".png"
|
|
483
|
+
stacked_csv_file = csv_stacked_output_file + ".csv"
|
|
463
484
|
|
|
464
485
|
try:
|
|
465
486
|
perf_report.generate_perf_report(
|
|
466
487
|
[csv_file],
|
|
467
|
-
|
|
488
|
+
start_signpost,
|
|
489
|
+
end_signpost,
|
|
468
490
|
ignore_signposts,
|
|
491
|
+
print_signposts,
|
|
469
492
|
cls.DEFAULT_MIN_PERCENTAGE,
|
|
470
493
|
cls.DEFAULT_ID_RANGE,
|
|
471
494
|
csv_output_file,
|
|
472
495
|
cls.DEFAULT_NO_ADVICE,
|
|
473
496
|
cls.DEFAULT_TRACING_MODE,
|
|
474
497
|
cls.DEFAULT_RAW_OP_CODES,
|
|
475
|
-
|
|
498
|
+
no_host_ops,
|
|
476
499
|
cls.DEFAULT_NO_STACKED_REPORT,
|
|
477
500
|
stack_by_in0,
|
|
478
501
|
csv_stacked_output_file,
|
|
502
|
+
not merge_devices,
|
|
479
503
|
)
|
|
480
504
|
except Exception as e:
|
|
481
505
|
logger.error(f"Error generating performance report: {e}")
|
|
@@ -552,16 +576,23 @@ class OpsPerformanceReportQueries:
|
|
|
552
576
|
|
|
553
577
|
stacked_report = []
|
|
554
578
|
|
|
555
|
-
if os.path.exists(
|
|
579
|
+
if os.path.exists(stacked_csv_file):
|
|
556
580
|
try:
|
|
557
|
-
with open(
|
|
581
|
+
with open(stacked_csv_file, newline="") as csvfile:
|
|
558
582
|
reader = csv.reader(csvfile, delimiter=",")
|
|
559
583
|
next(reader, None)
|
|
560
584
|
|
|
585
|
+
# Use the appropriate column list based on merge_devices flag
|
|
586
|
+
stacked_columns = (
|
|
587
|
+
cls.STACKED_REPORT_COLUMNS_WITH_DEVICE
|
|
588
|
+
if not merge_devices
|
|
589
|
+
else cls.STACKED_REPORT_COLUMNS
|
|
590
|
+
)
|
|
591
|
+
|
|
561
592
|
for row in reader:
|
|
562
593
|
processed_row = {
|
|
563
594
|
column: row[index]
|
|
564
|
-
for index, column in enumerate(
|
|
595
|
+
for index, column in enumerate(stacked_columns)
|
|
565
596
|
if index < len(row)
|
|
566
597
|
}
|
|
567
598
|
|
|
@@ -577,12 +608,10 @@ class OpsPerformanceReportQueries:
|
|
|
577
608
|
except csv.Error as e:
|
|
578
609
|
raise DataFormatError() from e
|
|
579
610
|
finally:
|
|
580
|
-
os.unlink(
|
|
611
|
+
os.unlink(stacked_csv_file)
|
|
581
612
|
if os.path.exists(stacked_png_file):
|
|
582
613
|
os.unlink(stacked_png_file)
|
|
583
614
|
|
|
584
|
-
stacked_report.append(processed_row)
|
|
585
|
-
|
|
586
615
|
return {
|
|
587
616
|
"report": report,
|
|
588
617
|
"stacked_report": stacked_report,
|
ttnn_visualizer/settings.py
CHANGED
|
@@ -7,7 +7,11 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from dotenv import load_dotenv
|
|
9
9
|
from sqlalchemy.pool import NullPool
|
|
10
|
-
from ttnn_visualizer.utils import
|
|
10
|
+
from ttnn_visualizer.utils import (
|
|
11
|
+
get_app_data_directory,
|
|
12
|
+
is_running_in_container,
|
|
13
|
+
str_to_bool,
|
|
14
|
+
)
|
|
11
15
|
|
|
12
16
|
load_dotenv()
|
|
13
17
|
|
|
@@ -41,9 +45,13 @@ class DefaultConfig(object):
|
|
|
41
45
|
PERFORMANCE_DIRECTORY_NAME = "performance-reports"
|
|
42
46
|
NPE_DIRECTORY_NAME = "npe-reports"
|
|
43
47
|
APPLICATION_DIR = os.path.abspath(os.path.join(__file__, "..", os.pardir))
|
|
44
|
-
APP_DATA_DIRECTORY = os.getenv("APP_DATA_DIRECTORY", APPLICATION_DIR)
|
|
45
|
-
STATIC_ASSETS_DIR = Path(APPLICATION_DIR).joinpath("ttnn_visualizer", "static")
|
|
46
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
|
+
|
|
54
|
+
STATIC_ASSETS_DIR = Path(APPLICATION_DIR).joinpath("ttnn_visualizer", "static")
|
|
47
55
|
SEND_FILE_MAX_AGE_DEFAULT = 0
|
|
48
56
|
|
|
49
57
|
LAUNCH_BROWSER_ON_START = str_to_bool(os.getenv("LAUNCH_BROWSER_ON_START", "true"))
|
|
@@ -75,7 +83,7 @@ class DefaultConfig(object):
|
|
|
75
83
|
GUNICORN_WORKERS = os.getenv("GUNICORN_WORKERS", "1")
|
|
76
84
|
GUNICORN_TIMEOUT = os.getenv("GUNICORN_TIMEOUT", "60")
|
|
77
85
|
PORT = os.getenv("PORT", "8000")
|
|
78
|
-
HOST = os.getenv("HOST", "localhost")
|
|
86
|
+
HOST = os.getenv("HOST", "0.0.0.0" if is_running_in_container() else "localhost")
|
|
79
87
|
DEV_SERVER_PORT = "5173"
|
|
80
88
|
DEV_SERVER_HOST = "localhost"
|
|
81
89
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{I as s}from"./index-voJy5fZe.js";import{I as r}from"./index-BZITDwoa.js";import{p as n,I as c}from"./index-
|
|
1
|
+
import{I as s}from"./index-voJy5fZe.js";import{I as r}from"./index-BZITDwoa.js";import{p as n,I as c}from"./index-DMzz7de9.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-B6-2k5TG.js","assets/index-voJy5fZe.js","assets/index-BZITDwoa.js","assets/index-DMzz7de9.js","assets/index-C7m_PE7l.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{_ as e}from"./index-DMzz7de9.js";const s=async(t,a)=>{const{getIconPaths:o}=await e(async()=>{const{getIconPaths:r}=await import("./allPaths-B6-2k5TG.js");return{getIconPaths:r}},__vite__mapDeps([0,1,2,3,4]));return o(t,a)};export{s as allPathsLoader};
|