diff --git a/README.md b/README.md
index 15906b9d..c1819e44 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@ For the latest updates and features, please see [releases](https://github.com/te
- Operation flow graph for a holistic view of model execution
- Load reports via the local file system or through an SSH connection
- Supports multiple instances of the application running concurrently
+- BETA: Network-on-chip performance estimator (NPE) for Tenstorrent Tensix-based devices
### Demo
@@ -68,6 +69,10 @@ https://github.com/user-attachments/assets/d00a2629-0bd1-4ee1-bb12-bd796d85221d
|-----------------------------------------------|------------------------------------------|
|
|
|
+| NPE | |
+|-----------------------------------------------|------------------------------------------|
+|
|
|
+
## Getting started
How to [get started](./docs/getting-started.md) with TT-NN Visualizer.
@@ -76,11 +81,11 @@ How to [get started](./docs/getting-started.md) with TT-NN Visualizer.
Use [remote querying](./docs/remote-querying.md) instead of syncing the report data to your local file system.
-## Sample models
+## Sample reports
You may test the application using the following sample reports.
-Unzip the files into their own directories and select them with the local folder selector.
+Unzip the files into their own directories and select them with the local folder selector, or load the NPE data on the `/npe` route.
**Segformer encoder**
[report](https://github.com/user-attachments/files/17996493/segformer_encoder.zip)
@@ -89,8 +94,13 @@ Unzip the files into their own directories and select them with the local folder
[report](https://github.com/user-attachments/files/17996491/segformer_decoder_good.zip)
**Llama mlp**
-[report](https://github.com/user-attachments/files/18129462/llama_mlp.zip)
-[performance trace](https://github.com/user-attachments/files/18129457/llama_mlp_tracy.zip)
+[report + performance trace](https://github.com/user-attachments/files/18770763/llama_attn_32l_10iter_30jan.zip)
+
+### NPE report
+
+**Llama decode**
+[npe data](https://github.com/user-attachments/files/18772778/llama-decode-3B.zip)
+
## Contributing
diff --git a/backend/ttnn_visualizer/csv_queries.py b/backend/ttnn_visualizer/csv_queries.py
index 4b119575..542b593d 100644
--- a/backend/ttnn_visualizer/csv_queries.py
+++ b/backend/ttnn_visualizer/csv_queries.py
@@ -1,13 +1,17 @@
# SPDX-License-Identifier: Apache-2.0
#
# SPDX-FileCopyrightText: © 2024 Tenstorrent Inc.
-
+import csv
import os
+import tempfile
+from io import StringIO
from pathlib import Path
from typing import List, Dict, Union, Optional
import pandas as pd
+from tt_perf_report import perf_report
+from ttnn_visualizer.exceptions import DataFormatError
from ttnn_visualizer.models import TabSession
from ttnn_visualizer.ssh_client import get_client
@@ -538,3 +542,66 @@ def get_all_entries(
return self.runner.execute_query(
columns=self.PERF_RESULTS_COLUMNS, as_dict=as_dict, limit=limit
)
+
+
+class OpsPerformanceReportQueries:
+ REPORT_COLUMNS = [
+ "id",
+ "total_percent",
+ "bound",
+ "op_code",
+ "device_time",
+ "op_to_op_gap",
+ "cores",
+ "dram",
+ "dram_percent",
+ "flops",
+ "flops_percent",
+ "math_fidelity",
+ "output_datatype",
+ "input_0_datatype",
+ "input_1_datatype",
+ "dram_sharded",
+ "input_0_memory",
+ "inner_dim_block_size",
+ "output_subblock_h",
+ "output_subblock_w"
+ ]
+
+ DEFAULT_SIGNPOST = None
+ DEFAULT_IGNORE_SIGNPOSTS = None
+ DEFAULT_MIN_PERCENTAGE = 0.5
+ DEFAULT_ID_RANGE = None
+ DEFAULT_NO_ADVICE = False
+
+ @classmethod
+ def generate_report(cls, session):
+ raw_csv = OpsPerformanceQueries.get_raw_csv(session)
+ csv_file = StringIO(raw_csv)
+ csv_output_file = tempfile.mktemp(suffix=".csv")
+ perf_report.generate_perf_report(
+ csv_file,
+ cls.DEFAULT_SIGNPOST,
+ cls.DEFAULT_IGNORE_SIGNPOSTS,
+ cls.DEFAULT_MIN_PERCENTAGE,
+ cls.DEFAULT_ID_RANGE,
+ csv_output_file,
+ cls.DEFAULT_NO_ADVICE,
+ )
+
+ report = []
+
+ try:
+ with open(csv_output_file, newline="") as csvfile:
+ reader = csv.reader(csvfile, delimiter=",")
+ next(reader, None)
+ for row in reader:
+ report.append({
+ column: row[index] for index, column in enumerate(cls.REPORT_COLUMNS)
+ })
+ except csv.Error as e:
+ raise DataFormatError() from e
+ finally:
+ os.unlink(csv_output_file)
+
+ return report
diff --git a/backend/ttnn_visualizer/exceptions.py b/backend/ttnn_visualizer/exceptions.py
index 03d53b74..066cd2bc 100644
--- a/backend/ttnn_visualizer/exceptions.py
+++ b/backend/ttnn_visualizer/exceptions.py
@@ -34,3 +34,7 @@ def __init__(self, message, status):
class DatabaseFileNotFoundException(Exception):
pass
+
+
+class DataFormatError(Exception):
+ pass
diff --git a/backend/ttnn_visualizer/requirements.txt b/backend/ttnn_visualizer/requirements.txt
index c263df9e..29965c43 100644
--- a/backend/ttnn_visualizer/requirements.txt
+++ b/backend/ttnn_visualizer/requirements.txt
@@ -1,4 +1,3 @@
-
gunicorn~=22.0.0
uvicorn==0.30.1
paramiko~=3.4.0
@@ -17,6 +16,7 @@ wheel
build
PyYAML==6.0.2
python-dotenv==1.0.1
+tt-perf-report==1.0.0
# Dev dependencies
mypy
diff --git a/backend/ttnn_visualizer/views.py b/backend/ttnn_visualizer/views.py
index 9b4d69ee..39365b2e 100644
--- a/backend/ttnn_visualizer/views.py
+++ b/backend/ttnn_visualizer/views.py
@@ -14,8 +14,9 @@
from flask import Blueprint, Response, jsonify
from flask import request, current_app
-from ttnn_visualizer.csv_queries import DeviceLogProfilerQueries, OpsPerformanceQueries
+from ttnn_visualizer.csv_queries import DeviceLogProfilerQueries, OpsPerformanceQueries, OpsPerformanceReportQueries
from ttnn_visualizer.decorators import with_session
+from ttnn_visualizer.exceptions import DataFormatError
from ttnn_visualizer.enums import ConnectionTestStates
from ttnn_visualizer.exceptions import RemoteConnectionException
from ttnn_visualizer.file_uploads import (
@@ -387,6 +388,20 @@ def get_profiler_perf_results_data_raw(session: TabSession):
)
+@api.route("/profiler/perf-results/report", methods=["GET"])
+@with_session
+def get_profiler_perf_results_report(session: TabSession):
+ if not session.profiler_path:
+ return Response(status=HTTPStatus.NOT_FOUND)
+
+ try:
+ report = OpsPerformanceReportQueries.generate_report(session)
+ except DataFormatError:
+ return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY)
+
+ return jsonify(report), 200
+
+
@api.route("/profiler/device-log/raw", methods=["GET"])
@with_session
def get_profiler_data_raw(session: TabSession):
diff --git a/docs/getting-started.md b/docs/getting-started.md
index c69492af..3e5c8294 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -45,6 +45,12 @@ Consult the TT Metal documentation on [how to generate a performance trace](http
+### NPE
+
+Network-on-chip performance estimator data can be loaded separately on the `/npe` route.
+
+To generate this data for your model, refer to the [tt-npe documentation](https://github.com/tenstorrent/tt-npe).
+
## Installing as a Python Wheel
The application is designed to run on user local system and has python requirement of `3.12.3`.
diff --git a/package.json b/package.json
index c8270cdc..d3a43d45 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ttnn-visualzer",
"private": true,
- "version": "0.20.0",
+ "version": "0.21.0",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/pyproject.toml b/pyproject.toml
index 98c47994..be8c3548 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "ttnn_visualizer"
authors = []
-version = "0.20.0"
+version = "0.21.0"
description = "TT Visualizer"
readme = "README.md"
requires-python = ">=3.12"
@@ -21,7 +21,8 @@ dependencies = [
"flask-socketio==5.4.1",
"flask-sqlalchemy==3.1.1",
"PyYAML==6.0.2",
- "python-dotenv==1.0.1"
+ "python-dotenv==1.0.1",
+ "tt-perf-report==1.0.0"
]
classifiers = [
diff --git a/src/components/DeviceOperations.tsx b/src/components/DeviceOperations.tsx
deleted file mode 100644
index 23a1ce48..00000000
--- a/src/components/DeviceOperations.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-//
-// SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
-
-import { Node } from '../model/APIData';
-
-interface DeviceOperationsData {
- deviceOperations: Node[];
-}
-
-function DeviceOperations({ deviceOperations }: DeviceOperationsData) {
- return (
-
-
- Device Operations
-
-
-
- ID |
- Type |
- Params |
-
-
- {deviceOperations.map((op) => (
-
- {op.id} |
- {op.node_type} |
- {JSON.stringify(op.params)} |
-
- ))}
-
-
-
- );
-}
-
-export default DeviceOperations;
diff --git a/src/components/FooterInfobar.tsx b/src/components/FooterInfobar.tsx
index 688672d6..30e67788 100644
--- a/src/components/FooterInfobar.tsx
+++ b/src/components/FooterInfobar.tsx
@@ -8,7 +8,13 @@ import { IconNames } from '@blueprintjs/icons';
import { useEffect, useState } from 'react';
import { useAtomValue } from 'jotai';
import { useLocation } from 'react-router';
-import { activePerformanceTraceAtom, activeReportAtom, operationRangeAtom, selectedRangeAtom } from '../store/app';
+import {
+ activePerformanceTraceAtom,
+ activeReportAtom,
+ operationRangeAtom,
+ performanceRangeAtom,
+ selectedOperationRangeAtom,
+} from '../store/app';
import { useGetDeviceOperationListPerf } from '../hooks/useAPI';
import Range from './RangeSlider';
import ROUTES from '../definitions/Routes';
@@ -17,9 +23,10 @@ import 'styles/components/FooterInfobar.scss';
const MAX_TITLE_LENGTH = 20;
function FooterInfobar() {
- const [sliderIsOpen, setSliderIsOpen] = useState(true);
- const selectedRange = useAtomValue(selectedRangeAtom);
+ const [sliderIsOpen, setSliderIsOpen] = useState(false);
+ const selectedRange = useAtomValue(selectedOperationRangeAtom);
const operationRange = useAtomValue(operationRangeAtom);
+ const performanceRange = useAtomValue(performanceRangeAtom);
const activeReport = useAtomValue(activeReportAtom);
const activePerformanceTrace = useAtomValue(activePerformanceTraceAtom);
const location = useLocation();
@@ -28,12 +35,22 @@ function FooterInfobar() {
const isInSync = useGetDeviceOperationListPerfResult.length > 0;
const isOperationDetails = location.pathname.includes(`${ROUTES.OPERATIONS}/`);
+ const isPerformanceRoute = location.pathname === ROUTES.PERFORMANCE;
+ const isNPE = location.pathname.includes(`${ROUTES.NPE}`);
useEffect(() => {
- if (isOperationDetails) {
+ if (isOperationDetails || isNPE) {
setSliderIsOpen(false);
}
- }, [isOperationDetails]);
+ }, [isNPE, isOperationDetails]);
+
+ const getSelectedRangeLabel = (): string | null => {
+ if (isPerformanceRoute) {
+ return performanceRange && `Selected Performance: ${performanceRange[0]} - ${performanceRange[1]}`;
+ }
+
+ return selectedRange && `Selected: ${selectedRange[0]} - ${selectedRange[1]}`;
+ };
return (