Skip to content

Commit 3bffc0b

Browse files
author
Masato Shinokawa
committed
Merge branch 'main' into do-not-return-deepcopy-of-GraphModule
2 parents d17937f + 73d321b commit 3bffc0b

4 files changed

Lines changed: 136 additions & 2 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

thunder/tests/opinfos.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,14 @@ def imag_error_generator(op, device, **kwargs):
21922192
elementwise_unary_ops.append(clone_opinfo)
21932193

21942194

2195+
square_opinfo = OpInfo(
2196+
ltorch.square,
2197+
sample_input_generator=elementwise_unary_generator,
2198+
torch_reference=_elementwise_unary_torch(torch.square),
2199+
)
2200+
elementwise_unary_ops.append(square_opinfo)
2201+
2202+
21952203
# Puts all opinfos into the "opinfos" list
21962204
opinfos.extend(elementwise_unary_ops)
21972205

thunder/torch/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,25 @@ def threshold_(a: TensorProxy, /, threshold: float, value: float) -> TensorLike:
22482248
_inplace_to_out_of_place[threshold_] = threshold, -1
22492249

22502250

2251+
@torchsymbol(torch.square, is_method=True)
2252+
def square(a):
2253+
if isinstance(dtypes.to_dtype(a), dtypes.bool_):
2254+
a = clang.maybe_convert_to_dtype(a, dtypes.int64)
2255+
return a * a
2256+
2257+
2258+
@torchsymbol(torch.square_, is_method=True, tags=(prims.OpTags.IN_PLACE,))
2259+
def square_(a):
2260+
utils.check(
2261+
not isinstance(dtypes.to_dtype(a), dtypes.bool_),
2262+
lambda: f"Result type of {dtypes.int64} cannot be stored into {dtypes.to_dtype(a)}",
2263+
)
2264+
return _copy_(a, square(a))
2265+
2266+
2267+
_inplace_to_out_of_place[square_] = square, -1
2268+
2269+
22512270
#
22522271
# Elementwise binary operations
22532272
#

thunder/torch/default_torch_ops.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@
252252
torch.split_with_sizes,
253253
torch.split_with_sizes_copy,
254254
torch.spmm,
255-
torch.square,
256255
torch.squeeze_copy,
257256
torch.sspaddmm,
258257
torch.std_mean,
@@ -548,7 +547,6 @@
548547
torch.Tensor.sparse_dim,
549548
torch.Tensor.sparse_mask,
550549
torch.Tensor.split_with_sizes,
551-
torch.Tensor.square,
552550
torch.Tensor.sspaddmm,
553551
torch.Tensor.stft,
554552
torch.Tensor.subtract,

0 commit comments

Comments
 (0)