Skip to content

Commit 64e9426

Browse files
kiya00shino16
authored andcommitted
Adds save_thunderfx_repros to save scripts for all the subgraphs and optionally save fusion region and traces (#2232)
While exploring the split reasons and observing changes in traces and fusion areas when adding missing fallback operators, I realized that having an API to save all repro scripts, with options to also save fusion graphs or traces to files, would be helpful. Before this PR, the user had to do the iteration and save manually.
1 parent 5ca99d0 commit 64e9426

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

thunder/dynamo/report.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import copy
1111
from itertools import chain
1212
from looseversion import LooseVersion
13+
import shutil
1314

1415
import torch
1516
from thunder.core.pytree import tree_flatten
@@ -1394,3 +1395,111 @@ def save_failing_repros(
13941395
report.write_repro(
13951396
repros_folder, compile_fn, extra_comment_str=comment, check_consistency=check_consistency
13961397
)
1398+
1399+
1400+
def create_folder(folder_path: str | PathLike, force_overwrite: bool = False):
1401+
folder_path = Path(folder_path)
1402+
1403+
if folder_path.exists():
1404+
if not folder_path.is_dir():
1405+
raise RuntimeError(f"{folder_path} exists and is not a directory.")
1406+
1407+
if force_overwrite:
1408+
shutil.rmtree(folder_path)
1409+
else:
1410+
raise RuntimeError(f"Folder {folder_path} already exists. Use force_overwrite=True to overwrite.")
1411+
1412+
folder_path.mkdir(parents=True, exist_ok=False)
1413+
1414+
1415+
def save_thunderfx_repros(
1416+
fn: Callable,
1417+
folder_path: str | PathLike,
1418+
*,
1419+
use_benchmark: bool = False,
1420+
check_runnability: bool = False,
1421+
save_fusion: bool = False,
1422+
save_trace: bool = False,
1423+
stream: TextIO = sys.stdout,
1424+
force_overwrite: bool = False,
1425+
**compile_kwargs,
1426+
):
1427+
"""
1428+
Saves reproduction scripts for ThunderFX subgraphs.
1429+
1430+
This function:
1431+
1. Creates a folder structure to organize the repros
1432+
.
1433+
└── graph0
1434+
├── fusion_reports
1435+
│ ├── graph0_thunder_0_nvFusion0_forward_repro_nvfuser.py
1436+
│ ├── graph0_thunder_0_nvFusion1_forward_repro_nvfuser.py
1437+
│ ├── graph0_thunder_0_nvFusion2_backward_repro_nvfuser.py
1438+
├── graph0_thunder_0_bwd_trace.py
1439+
├── graph0_thunder_0_fwd_trace.py
1440+
└── graph0_thunder_0.py
1441+
1442+
2. For each Thunder FX graph and its subgraphs:
1443+
- Checks runnability if requested
1444+
- Saves benchmark or repro scripts
1445+
- Saves trace information if requested
1446+
- Saves nvFusion repros if requested
1447+
1448+
Args:
1449+
fn: The callable to analyze
1450+
folder_path: Path to save repros to
1451+
use_benchmark: If True, saves benchmark scripts instead of repros
1452+
check_runnability: If True, checks if graphs can run with Thunder
1453+
save_fusion: If True, saves nvFusion repros
1454+
save_trace: If True, saves trace information
1455+
stream: Stream to write output log informationto
1456+
force_overwrite: If True, overwrites existing folder at folder_path
1457+
**compile_kwargs: Keyword arguments for Thunder and torch.compile
1458+
1459+
Returns:
1460+
A wrapped function that saves repros when called with inputs
1461+
"""
1462+
from thunder.dynamo.utils import get_torch_compile_kwargs
1463+
1464+
folder_path = Path(folder_path)
1465+
create_folder(folder_path, force_overwrite)
1466+
torch_compile_kwargs = get_torch_compile_kwargs(**compile_kwargs)
1467+
thunder_jit_kwargs = {k: v for k, v in compile_kwargs.items() if k not in torch_compile_kwargs}
1468+
thunderjit = ThunderCompileSpecification(**thunder_jit_kwargs)
1469+
1470+
def inner_fn(*args, **kwargs):
1471+
thunder_fxgraph_reports = get_thunder_fxgraph_reports(fn, stream=stream, **compile_kwargs)(*args, **kwargs)
1472+
for thunder_fxgraph_report in thunder_fxgraph_reports:
1473+
graph_folder = folder_path / thunder_fxgraph_report.graph_name
1474+
graph_folder.mkdir(exist_ok=True, parents=True)
1475+
for split_report in thunder_fxgraph_report.subgraph_reports:
1476+
if check_runnability or save_trace or save_fusion:
1477+
try:
1478+
split_report.create_fusion_reports()
1479+
except Exception as e:
1480+
stream.write(f"Failed to run the {split_report.graph_name} using Thunder with exception: {e}\n")
1481+
split_report.write_repro(
1482+
graph_folder, thunderjit, file_name=f"failed_{split_report.graph_name}.py"
1483+
)
1484+
continue
1485+
else:
1486+
stream.write(f"Successfully ran the {split_report.graph_name} using Thunder\n")
1487+
if use_benchmark:
1488+
split_report.write_benchmark(graph_folder, thunderjit, WallTime)
1489+
else:
1490+
split_report.write_repro(graph_folder, thunderjit)
1491+
if save_trace:
1492+
with open(graph_folder / f"{split_report.graph_name}_fwd_trace.py", "w") as f:
1493+
f.write(str(split_report.fwd_trc))
1494+
with open(graph_folder / f"{split_report.graph_name}_bwd_trace.py", "w") as f:
1495+
f.write(str(split_report.bwd_trc))
1496+
if save_fusion:
1497+
fusion_folder = graph_folder / "fusion_reports"
1498+
fusion_folder.mkdir(exist_ok=True, parents=True)
1499+
for fusion_report in split_report.fusion_reports:
1500+
if use_benchmark:
1501+
fusion_report.write_nvfuser_benchmark(fusion_folder, WallTime)
1502+
else:
1503+
fusion_report.write_nvfuser_repro(fusion_folder)
1504+
1505+
return inner_fn

0 commit comments

Comments
 (0)