Skip to content

Commit

Permalink
Add explorer model execution
Browse files Browse the repository at this point in the history
Most basic execution without overrides.
  • Loading branch information
odjuricicTT committed Nov 8, 2024
1 parent 7122775 commit 606d3d0
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ jobs:
fail-fast: false
matrix:
build: [
{runs-on: ubuntu-latest, enable_perf: OFF, name: "run", ttrt_flags: ""},
{runs-on: n150, enable_perf: ON, name: "perf"},
]

name: Build and test tt-explorer
Expand Down
23 changes: 12 additions & 11 deletions runtime/tools/python/ttrt/common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,17 +559,18 @@ def __init__(self, logger, file_manager, file_path, capsule=None):

# populate golden tensors if they exist
if "debug_info" in self.fbb_dict["programs"][i]:
golden_info_list = self.fbb_dict["programs"][i]["debug_info"][
"golden_info"
]["golden_map"]

for golden_tensor_dict in golden_info_list:
Golden(
golden_tensor_dict["key"],
golden_tensor_dict["value"]["shape"],
golden_tensor_dict["value"]["stride"],
golden_tensor_dict["value"]["data"],
)
if "golden_info" in self.fbb_dict["programs"][i]["debug_info"]:
golden_info_list = self.fbb_dict["programs"][i]["debug_info"][
"golden_info"
]["golden_map"]

for golden_tensor_dict in golden_info_list:
Golden(
golden_tensor_dict["key"],
golden_tensor_dict["value"]["shape"],
golden_tensor_dict["value"]["stride"],
golden_tensor_dict["value"]["data"],
)

def check_system_desc(self, query):
import ttrt.binary
Expand Down
4 changes: 2 additions & 2 deletions tools/explorer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ include(ExternalProject)
set(TT_EXPLORER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/run.py)
set(TTMLIR_BUILD_BIN_DIR ${TTMLIR_BINARY_DIR}/bin)

set(MODEL_EXPLORER_VERSION "123046643fbe5524f40076fcffcaa642066c9c30")
set(MODEL_EXPLORER_VERSION "785e0911263bfa02840582cf47eeb59539f7351d")
ExternalProject_Add(
model-explorer
PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/model-explorer
Expand All @@ -20,7 +20,7 @@ add_custom_target(explorer
COMMAND pip install ${CMAKE_CURRENT_SOURCE_DIR}/tt_adapter
COMMAND pip install ${CMAKE_CURRENT_SOURCE_DIR}/model-explorer/src/model-explorer/src/server/package

DEPENDS TTMLIRPythonModules model-explorer
DEPENDS TTMLIRPythonModules model-explorer ttrt
)

add_custom_command(TARGET explorer POST_BUILD
Expand Down
30 changes: 25 additions & 5 deletions tools/explorer/test/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@

HOST = "localhost"
PORT = 8002
CONVERT_URL = "http://" + HOST + ":" + str(PORT) + "/apipost/v1/send_command"
COMMAND_URL = "http://" + HOST + ":" + str(PORT) + "/apipost/v1/send_command"
TEST_LOAD_MODEL_PATHS = [
"test/ttmlir/Dialect/TTNN/mnist_sharding.mlir",
"tools/explorer/test/models/*.mlir",
]
TEST_EXECUTE_MODEL_PATHS = [
"test/ttmlir/Silicon/TTNN/sharded/mnist_sharding_tiled.mlir",
]


def get_test_files():
def get_test_files(paths):
files = []
for path in TEST_LOAD_MODEL_PATHS:
for path in paths:
files.extend(glob.glob(path))
return files

Expand All @@ -37,7 +40,7 @@ def start_server(request):
request.addfinalizer(lambda: server_thread.terminate())


@pytest.mark.parametrize("model_path", get_test_files())
@pytest.mark.parametrize("model_path", get_test_files(TEST_LOAD_MODEL_PATHS))
def test_load_model(model_path):
cmd = {
"extensionId": "tt_adapter",
Expand All @@ -47,7 +50,24 @@ def test_load_model(model_path):
"settings": {},
}

result = requests.post(CONVERT_URL, json=cmd)
result = requests.post(COMMAND_URL, json=cmd)
assert result.ok
if "error" in result.json():
print(result.json())
assert False


@pytest.mark.parametrize("model_path", get_test_files(TEST_EXECUTE_MODEL_PATHS))
def test_execute_model(model_path):
cmd = {
"extensionId": "tt_adapter",
"cmdId": "execute",
"modelPath": model_path,
"deleteAfterConversion": False,
"settings": {},
}

result = requests.post(COMMAND_URL, json=cmd)
assert result.ok
if "error" in result.json():
print(result.json())
Expand Down
23 changes: 15 additions & 8 deletions tools/explorer/tt_adapter/src/tt_adapter/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
from typing import Dict
import model_explorer
import ttmlir
from . import ttir
from . import ttir, runner, utils


class TTAdapter(model_explorer.Adapter):
Expand All @@ -15,20 +14,28 @@ class TTAdapter(model_explorer.Adapter):
source_repo="https://github.com/tenstorrent/tt-mlir/tree/main/tools/explorer/tt_adapter",
fileExts=["mlir", "ttir"],
)
model_runner = None

# Required.
def __init__(self):
super().__init__()
self.model_runner = runner.ModelRunner()

def convert(
self, model_path: str, settings: Dict
) -> model_explorer.ModelExplorerGraphs:
with ttmlir.ir.Context() as ctx, open(model_path, "r") as model_file:
ttmlir.dialects.ttkernel.register_dialect(ctx)
ttmlir.dialects.ttir.register_dialect(ctx)
ttmlir.dialects.tt.register_dialect(ctx)
module = ttmlir.ir.Module.parse("".join(model_file.readlines()), ctx)
module = utils.parse_mlir_file(model_path)

# Convert TTIR to Model Explorer Graphs and Display/Return
graph = ttir.ttir_to_graph(module, ctx)
graph = ttir.ttir_to_graph(module)
return {"graphs": [graph]}

def execute(
self, model_path: str, settings: Dict
) -> model_explorer.ModelExplorerGraphs:
# TODO(odjuricic, #1178) settings need to be parsed.
# Waiting on override class for this.
ttnn_ir = self.model_runner.run(model_path)

# TODO(odjuricic, #933) Parse TTNN IR and return the post optimized graph.
return {"graphs": []}
134 changes: 134 additions & 0 deletions tools/explorer/tt_adapter/src/tt_adapter/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# SPDX-FileCopyrightText: (c) 2024 Tenstorrent AI ULC
#
# SPDX-License-Identifier: Apache-2.0
import subprocess
import os
import tempfile

# TODO(odjuricic) Cleaner to implement ttrt --quiet flag.
os.environ["TTRT_LOGGER_LEVEL"] = "ERROR"
from ttrt import API as ttrt
import ttmlir.passes
from . import utils
import pandas as pd


class ModelRunner:
"""
ModelRunner is a singleton class used for compilation and running of models. Ensuring only one can be run at a time.
This is necessary because the adaptor class is reinitialized on every request from the frontend, so it cannot keep state.
"""

_instance = None
_explorer_artifacts_dir = None

def __new__(cls, *args, **kwargs):
if not cls._instance:
print("Creating a new ModelRunner instance.")
cls._instance = super(ModelRunner, cls).__new__(cls, *args, **kwargs)
cls._instance.initialize()
return cls._instance

def initialize(self):
# Initialize machine to generate SystemDesc and load up functionality to begin
print("Running ttrt initialization.")
ttrt.initialize_apis()

if "TT_MLIR_HOME" not in os.environ:
raise RuntimeError("TT_MLIR_HOME not set. Did you run source env/activate?")

# TODO(odjuricic, #1200) ttrt perf breaks if artifacts dir is changed from default.
# self._explorer_artifacts_dir = os.environ['TT_MLIR_HOME'] + '/explorer-artifacts'
self._explorer_artifacts_dir = os.environ["TT_MLIR_HOME"] + "/ttrt-artifacts"
os.makedirs(self._explorer_artifacts_dir, exist_ok=True)

# Save the system descriptor.
ttrt.Query(
args={
"--save-artifacts": True,
"--artifact-dir": self._explorer_artifacts_dir,
}
)()

def run(self, model_path):
# TODO(odjuricic, #1174) This should be in a separete thread later.
model_name = os.path.basename(model_path).split(".")[0]

ttir_to_ttnn_options = " ".join(
[
f'system-desc-path={f"{self._explorer_artifacts_dir}/system_desc.ttsys"}',
"enable-optimizer=true",
"memory-layout-analysis-enabled=true",
]
)

module = utils.parse_mlir_file(model_path)

try:
print("Running MLIR compile: TTIR to TTNN Backend Pipeline")
print("With options: ", ttir_to_ttnn_options)
# TODO(odjuricic) When we hit compiler assert it terminates the process. We should catch this and return an error to the frontend.
ttmlir.passes.ttir_to_ttnn_backend_pipeline(module, ttir_to_ttnn_options)
except Exception as e:
print("Error running MLIR compile: TTIR to TTNN Backend Pipeline")
raise e

# TODO(odjuricic) Move this file somewhere else, but keep the name.
flatbuffer_file = model_name + ".ttnn"
try:
print("Running TTNN to Flatbuffer File")
ttmlir.passes.ttnn_to_flatbuffer_file(module, flatbuffer_file, {})
except Exception as e:
print("Error running TTNN to Flatbuffer File")
raise e

# TODO(odjuricic) validate that the module was converted to TTNN without fail

if os.path.exists(f"{self._explorer_artifacts_dir}/{flatbuffer_file}"):
print("Removing artifacts of previous run.")
os.system(f"rm -rf {self._explorer_artifacts_dir}/{flatbuffer_file}")

ttrt_perf_command = " ".join(
[
"ttrt",
"perf",
flatbuffer_file,
f"--artifact-dir={self._explorer_artifacts_dir}",
"--save-artifacts",
]
)

print("Running", ttrt_perf_command)
process = subprocess.Popen(
ttrt_perf_command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)

for line in process.stdout:
print(line, end="")

process.stdout.close()
process.wait()

if process.returncode != 0:
print(f"Error: TTRT process exited with code {process.returncode}")
raise RuntimeError("Error running TTRT")

op_perf_file = f"{self._explorer_artifacts_dir}/{flatbuffer_file}/perf/ops_perf_results.csv"
if not os.path.exists(op_perf_file):
raise FileNotFoundError(f"Performance file {op_perf_file} not found.")
perf = pd.read_csv(op_perf_file)
columns = [
"GLOBAL CALL COUNT",
"OP CODE",
"DEVICE FW DURATION [ns]",
"CORE COUNT",
"OUTPUT_0_MEMORY",
]
perf = perf[columns]
print(perf)

print("Total device duration: ", perf["DEVICE FW DURATION [ns]"].sum(), "ns")
2 changes: 1 addition & 1 deletion tools/explorer/tt_adapter/src/tt_adapter/ttir.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def get_layout_attrs(tensor):
return attrs


def ttir_to_graph(module, ctx):
def ttir_to_graph(module):
# Can assume that to-layout pass has already been run on the module.
name_dict = defaultdict(int)
output_connections = defaultdict(int)
Expand Down
13 changes: 13 additions & 0 deletions tools/explorer/tt_adapter/src/tt_adapter/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: (c) 2024 Tenstorrent AI ULC
#
# SPDX-License-Identifier: Apache-2.0
import ttmlir


def parse_mlir_file(model_path):
with ttmlir.ir.Context() as ctx, open(model_path, "r") as model_file:
ttmlir.dialects.ttkernel.register_dialect(ctx)
ttmlir.dialects.ttir.register_dialect(ctx)
ttmlir.dialects.tt.register_dialect(ctx)
module = ttmlir.ir.Module.parse("".join(model_file.readlines()), ctx)
return module

0 comments on commit 606d3d0

Please sign in to comment.