Skip to content

Commit

Permalink
v0.21.0 (#364)
Browse files Browse the repository at this point in the history
  • Loading branch information
aidemsined authored Feb 12, 2025
2 parents 8df79f7 + f5025c6 commit 3fe151e
Show file tree
Hide file tree
Showing 35 changed files with 1,932 additions and 257 deletions.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -68,6 +69,10 @@ https://github.com/user-attachments/assets/d00a2629-0bd1-4ee1-bb12-bd796d85221d
|-----------------------------------------------|------------------------------------------|
| <img width="400" alt="Performance analysis" src="https://github.com/user-attachments/assets/03f64a7a-262a-4b2a-a0b6-c70f3f14705c" /> | <img width="400" alt="" src="https://github.com/user-attachments/assets/224d14f7-c7b3-4f4c-99c6-528c800b1a84" /> |

| NPE | |
|-----------------------------------------------|------------------------------------------|
| <img width="400" alt="NPE" src="https://github.com/user-attachments/assets/1f4441c6-9d9e-4834-9e71-edec1955243c" /> | <img width="400" alt="NPE" src="https://github.com/user-attachments/assets/7159992e-7691-41cf-a152-8bc6a3606ade" /> |

## Getting started

How to [get started](./docs/getting-started.md) with TT-NN Visualizer.
Expand All @@ -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)
Expand All @@ -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

Expand Down
69 changes: 68 additions & 1 deletion backend/ttnn_visualizer/csv_queries.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
4 changes: 4 additions & 0 deletions backend/ttnn_visualizer/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ def __init__(self, message, status):

class DatabaseFileNotFoundException(Exception):
pass


class DataFormatError(Exception):
pass
2 changes: 1 addition & 1 deletion backend/ttnn_visualizer/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

gunicorn~=22.0.0
uvicorn==0.30.1
paramiko~=3.4.0
Expand All @@ -17,6 +16,7 @@ wheel
build
PyYAML==6.0.2
python-dotenv==1.0.1
tt-perf-report==1.0.0

# Dev dependencies
mypy
Expand Down
17 changes: 16 additions & 1 deletion backend/ttnn_visualizer/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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):
Expand Down
6 changes: 6 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ Consult the TT Metal documentation on [how to generate a performance trace](http

<img width="916" alt="Screenshot 2024-12-13 at 12 29 44 PM" src="https://github.com/user-attachments/assets/8209f500-7913-41dc-8952-c1307e7720c3" />

### 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`.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ttnn-visualzer",
"private": true,
"version": "0.20.0",
"version": "0.21.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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 = [
Expand Down
37 changes: 0 additions & 37 deletions src/components/DeviceOperations.tsx

This file was deleted.

33 changes: 24 additions & 9 deletions src/components/FooterInfobar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand All @@ -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 (
<footer className={classNames('app-footer', { 'is-open': sliderIsOpen })}>
Expand Down Expand Up @@ -97,12 +114,10 @@ function FooterInfobar() {
)}
</div>

{operationRange && (
{(operationRange || performanceRange) && (
<div className='slider-controls'>
{!sliderIsOpen && !hasRangeSelected(selectedRange, operationRange) && (
<span className='current-range'>
Selected: {selectedRange && `${selectedRange[0]} - ${selectedRange[1]}`}
</span>
<span className='current-range'>{getSelectedRangeLabel()}</span>
)}

<Button
Expand Down
11 changes: 11 additions & 0 deletions src/components/MainNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ function MainNavigation() {
large
className='performance-button'
/>
<Button
text='NPE'
onClick={() => handleNavigate(ROUTES.NPE)}
active={window.location.pathname === ROUTES.NPE}
icon={IconNames.Random}
minimal
large
className='npe-button'
>
<small>beta</small>
</Button>
</Navbar.Group>
</Navbar>
);
Expand Down
Loading

0 comments on commit 3fe151e

Please sign in to comment.