Skip to content

Commit

Permalink
Performance Report API (#360)
Browse files Browse the repository at this point in the history
This PR adds a new API endpoint `/api/profiler/perf-results/report`,
which takes the `ops_perf_results_*.csv` file, and returns a report
generated with tt-perf-report.

It uses the same input CSV file that is used by the
`/profiler/perf-results/raw` endpoint, and it makes use of the existing
code to download that file. But instead of returning the original CSV
file, it runs it through tt-perf-report, and takes the output CSV file,
and turns it into a JSON response, where each line of the CSV is
represented as an object.

You can get the report from cURL like this:

`curl
'http://localhost:5173/api/profiler/perf-results/report?tabId=r5lr5xs95ai'`

Where the `tabId` would be the same `tabId` already used by the frontend
for other features. There is nowhere in this PR that takes a new csv
parameter, since the source file is already there and can already be
uploaded from the UI. Except now it processes it with the
`tt-perf-report` package from PyPI, instead of returning the raw data
from the `ops_perf_results_*.csv` file.

The following is truncated output from the new endpoint to show the
format of its response:

```
curl -s 'http://localhost:5173/api/profiler/perf-results/report?tabId=r5lr5xs95ai' \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Accept-Language: en-US,en;q=0.9' \
  -H 'Connection: keep-alive' \
  -H 'Referer: http://localhost:5173/performance' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' \
  -H 'sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' | python -m json.tool
[
    {
        "bound": "",
        "cores": "1",
        "device_time": "4.534",
        "dram": "None",
        "dram_percent": "None",
        "dram_sharded": "BFLOAT16",
        "flops": "None",
        "flops_percent": "None",
        "id": "2",
        "inner_dim_block_size": "DEV_0_DRAM_INTERLEAVED",
        "input_0_datatype": "BFLOAT16",
        "input_0_memory": "False",
        "input_1_datatype": "UINT32",
        "math_fidelity": "UINT32",
        "op_code": "Embeddings",
        "op_to_op_gap": "None",
        "output_datatype": " BF16 => BF16",
        "output_subblock_h": "None",
        "output_subblock_w": "None",
        "total_percent": "8.537923423325384e-06"
    },
    {
        "bound": "",
        "cores": "1",
        "device_time": "4.641",
        "dram": "None",
        "dram_percent": "None",
        "dram_sharded": "BFLOAT16",
        "flops": "None",
        "flops_percent": "None",
        "id": "4",
        "inner_dim_block_size": "DEV_0_DRAM_INTERLEAVED",
        "input_0_datatype": "BFLOAT16",
        "input_0_memory": "False",
        "input_1_datatype": "UINT32",
        "math_fidelity": "UINT32",
        "op_code": "Embeddings",
        "op_to_op_gap": "18755.338",
        "output_datatype": " BF16 => BF16",
        "output_subblock_h": "None",
        "output_subblock_w": "None",
        "total_percent": "0.035326701395057857"
    },
    {
        "bound": "",
        "cores": "64",
        "device_time": "9.557",
        "dram": "None",
        "dram_percent": "None",
        "dram_sharded": "False",
        "flops": "None",
        "flops_percent": "None",
        "id": "7",
        "inner_dim_block_size": "None",
        "input_0_datatype": "BFLOAT16",
        "input_0_memory": "DEV_1_DRAM_INTERLEAVED",
        "input_1_datatype": "nan",
        "math_fidelity": "BF16 => BF16",
        "op_code": "Transpose",
        "op_to_op_gap": "708678.341",
        "output_datatype": "BFLOAT16",
        "output_subblock_h": "None",
        "output_subblock_w": "None",
        "total_percent": "1.3345220564978897"
    },
...
```
  • Loading branch information
dcblundell authored Feb 12, 2025
2 parents 2a5a28c + 0584e27 commit 766b97c
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 4 deletions.
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
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 766b97c

Please sign in to comment.