Skip to content

Commit 08a1129

Browse files
authored
Merge branch 'main' into kaixi/dtensor-redistribute
2 parents a2af65e + bc399d4 commit 08a1129

6 files changed

Lines changed: 92 additions & 29 deletions

File tree

‎thunder/core/rematerialization.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ def rematerialize(trace: TraceCtx) -> TraceCtx:
458458
computed_cuts_for_producers[producer] += cut
459459

460460
rematerialized_trace = from_trace(trace)
461-
rematerialized_trace.bound_symbols = tuple(new_bsyms.get(bsym, bsym) for bsym in trace.bound_symbols)
461+
rematerialized_trace.bound_symbols = list(new_bsyms.get(bsym, bsym) for bsym in trace.bound_symbols)
462462

463463
end_time_ns = time.perf_counter_ns()
464464
elapsed_time_ns = end_time_ns - start_time_ns

‎thunder/core/symbol.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,12 @@ def tag_tensorproxy_output_as_detached(proxy):
350350
exception_type=AssertionError,
351351
)
352352

353+
# When using symbolic values, there may be duplicate prims.eq and prims.shape subsymbols that can be removed.
354+
from thunder.core.transform_common import dce_bsyms
355+
356+
subsymbols = dce_bsyms(subsymbols, result)
357+
bsym = bsym.from_bsym(subsymbols=subsymbols)
358+
353359
symbols_list.append(bsym)
354360
return result
355361

‎thunder/core/transform_common.py‎

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,20 +142,32 @@ def keep_or_swap(p):
142142
# that only produce non-proxy objects
143143
# NOTE needed_proxies is an in/out argument, it takes an initial set of Variables you want to keep, and return
144144
# all the needed proxies of the input trace
145-
def dce(trace: Trace, needed_proxies: None | set[Variable] = None) -> Trace:
146-
start_time_ns = time.perf_counter_ns()
145+
def dce_bsyms(
146+
bsyms: list[BoundSymbolInterface],
147+
output: Any,
148+
needed_proxies: None | set[Variable] = None,
149+
) -> Trace | list[BoundSymbolInterface]:
150+
"""Runs a Dead Code Elimination (DCE) pass
151+
152+
Args:
153+
bsyms: The list of bound symbols to run the DCE pass on.
154+
needed_proxies: The set of variables to keep.
155+
output: The output of the list of bound symbols.
147156
148-
producer_map: ProxyDict = producers(trace)
157+
Returns:
158+
The list of bound symbols after the DCE pass.
159+
"""
160+
producer_map: ProxyDict = producers(bsyms)
149161

150-
flat_trace_outputs, _ = tree_flatten(trace.output)
162+
flat_trace_outputs, _ = tree_flatten(output)
151163
if needed_proxies is None:
152164
needed_proxies: set[Variable] = set(tuple(variableify(x) for x in flat_trace_outputs if isinstance(x, Proxy)))
153165
else:
154166
needed_proxies.update(tuple(variableify(x) for x in flat_trace_outputs if isinstance(x, Proxy)))
155167
dced = []
156168

157169
bsym: BoundSymbol
158-
for bsym in reversed(trace.bound_symbols):
170+
for bsym in reversed(bsyms):
159171
# Preserves symbols that should never be collected
160172
if has_tags(bsym, {prims.OpTags.DONT_DCE}):
161173
needed = True
@@ -182,19 +194,28 @@ def dce(trace: Trace, needed_proxies: None | set[Variable] = None) -> Trace:
182194
for x in nbsym.flat_proxy_args:
183195
needed_proxies.add(variableify(x))
184196

185-
dcetrace = from_trace(trace)
186197
dced_bound_symbols = list(reversed(dced))
187198
# duplicate number proxies happen with the symbolic shapes and are
188199
# not covered by the above (due to being in tuples?).
189200
dced_bound_symbols = remove_duplicate_number_proxies(dced_bound_symbols)
190-
dcetrace.bound_symbols = dced_bound_symbols
201+
202+
return dced_bound_symbols
203+
204+
205+
def dce(trace: Trace, needed_proxies: set[Variable] = None) -> Trace:
206+
start_time_ns = time.perf_counter_ns()
207+
208+
bsyms = trace.bound_symbols
209+
dced_bsyms = dce_bsyms(bsyms, trace.output, needed_proxies)
210+
result = from_trace(trace)
211+
result.bound_symbols = dced_bsyms
191212

192213
end_time_ns = time.perf_counter_ns()
193214
elapsed_time_ns = end_time_ns - start_time_ns
194215
elapsed_time_millis = elapsed_time_ns // 1000000
195-
dcetrace.set_provenance(TraceProvenance(f"Dead Code Elimination (took {elapsed_time_millis} milliseconds)"))
196216

197-
return dcetrace
217+
result.set_provenance(TraceProvenance(f"Dead Code Elimination (took {elapsed_time_millis} milliseconds)"))
218+
return result
198219

199220

200221
#

‎thunder/dynamo/utils.py‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,20 +259,20 @@ def get_backed_value(s):
259259
return tuple(map(get_backed_value, vals))
260260

261261

262-
def get_proxy_inputs_from_node(node: torch.fx.Node) -> tuple[tuple, dict]:
262+
def get_proxy_inputs_from_node(node: torch.fx.Node, tracectx) -> tuple[tuple, dict]:
263263
"""Creates proxy inputs from a torch.fx.Node for use with Thunder.
264264
265265
This function generates proxy inputs for a given torch.fx.Node
266266
267267
Args:
268268
node (torch.fx.Node): The FX graph node to create proxy inputs for.
269+
tracectx (TraceCtx): The trace context to use to generate proxy inputs.
269270
"""
270271
import thunder
271-
from thunder.core.trace import TraceCtx
272272
from thunder.core.proxies import proxy
273273

274274
# We need to be under trace context to generate proxies.
275-
with thunder.core.trace.tracectx(TraceCtx()):
275+
with thunder.core.trace.tracectx(tracectx):
276276

277277
def make_input_proxy(arg_node):
278278
# This is a Node in the graph representing a Tensor or tuple of Tensors or
@@ -380,8 +380,10 @@ def _run_with_cache_info():
380380
cache_info["default_dtype"] = torch.get_default_dtype()
381381
cache_info["default_device"] = torch.get_default_device()
382382

383+
tracectx = TraceCtx()
384+
383385
try:
384-
proxy_args, proxy_kwargs = get_proxy_inputs_from_node(node)
386+
proxy_args, proxy_kwargs = get_proxy_inputs_from_node(node, tracectx)
385387
except Exception as e:
386388
return False, SplitReason(
387389
SplitReasonType.EXCEPTION_PROXY_THUNDER_OP,
@@ -395,7 +397,7 @@ def _run_with_cache_info():
395397
else thunder_symbol
396398
)
397399
# We need to be under trace context to generate proxies.
398-
with thunder.core.trace.tracectx(TraceCtx()):
400+
with thunder.core.trace.tracectx(tracectx):
399401
try:
400402
function_to_run(*proxy_args, **proxy_kwargs)
401403
except Exception as e:
@@ -478,6 +480,7 @@ def is_node_supported_by_thunder(
478480
"""
479481
Determine whether thunder can execute the operation described by this node.
480482
"""
483+
from thunder.core.trace import TraceCtx
481484
# Docs from the torch.fx.Node - https://pytorch.org/docs/stable/fx.html#torch.fx.Node
482485
# Each Node has a function specified by its op property
483486
# Below are the details for the ones this function is interested in -
@@ -555,7 +558,7 @@ def is_node_supported_by_thunder(
555558
if torchctx.has_method(node.target):
556559
# `torchctx.get_method` requires args and kwargs to resolve which overload of the method is picked.
557560
try:
558-
args, kwargs = get_proxy_inputs_from_node(node)
561+
args, kwargs = get_proxy_inputs_from_node(node, TraceCtx())
559562
except Exception as e:
560563
return False, SplitReason(
561564
SplitReasonType.EXCEPTION_PROXY_THUNDER_OP,

‎thunder/executors/nvfuserex_impl.py‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from thunder.core.trace import TraceCtx, from_trace, TraceProvenance
4444
from thunder.core.symbol import BoundSymbol, BoundSymbolRHS, Symbol, has_tags
4545
from thunder.core.devices import Device, DeviceType, cpu
46-
from thunder.core.transform_common import dce, cse_single_bsym, replace_redundant_inputs
46+
from thunder.core.transform_common import dce, dce_bsyms, cse_single_bsym, replace_redundant_inputs
4747
from thunder.core.profile import annotate_for_profile
4848
from thunder.core.compile_data import get_compile_option
4949
from thunder.torch.experimental.dtensor_torch_and_prims import DTensorPrimIDs
@@ -707,14 +707,11 @@ def has_cuda_input_or_output(self, bsym: BoundSymbol) -> bool:
707707
return False
708708

709709
def _dce_bsyms(self, input_list, output, bsyms: list[BoundSymbol]) -> list[BoundSymbol]:
710-
trace = TraceCtx(None)
711-
trace.bound_symbols = bsyms
712-
bsyms.append(prims.python_return.bind(output, output=None))
713710
needed_proxies: set[Variable] = set()
714-
trace = dce(trace, needed_proxies)
711+
bsyms = dce_bsyms(bsyms, output, needed_proxies)
715712
# update the input_list by removing the unused inputs
716713
input_list[:] = [x for x in input_list if variableify(x) in needed_proxies]
717-
return list(filter(lambda x: x.sym != prims.python_return, trace.bound_symbols))
714+
return bsyms
718715

719716
def fuse(self, region: Region, fusion_counter: int) -> BoundSymbol:
720717
sorted_unique_inputs: list[Proxy] = [unvariableify(x) for x in region.inputs]

‎thunder/tests/test_core.py‎

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,36 @@ def func(x, y, device):
21702170
assert [t.name for t in tree_flatten(flatten_cse_trace.output)[0]] == ["t4", "t4", "t6", "t14", "t15", "t16", "t17"]
21712171

21722172

2173+
@instantiate(
2174+
dtypes=NOTHING,
2175+
)
2176+
def test_dce(executor, device, _):
2177+
def func(x):
2178+
dead_code = x + 1 # noqa: F841
2179+
y = x * x
2180+
return y
2181+
2182+
x = make_tensor((2, 2), device=device, dtype=torch.float32)
2183+
compiled = thunder.jit(func, executors=executor.executors_list())
2184+
compiled(x)
2185+
traces = thunder.last_traces(compiled)
2186+
2187+
# find last trace before DCE is applied
2188+
for i, trace in enumerate(traces):
2189+
provenance = trace.get_provenance().pss if trace.get_provenance() else ""
2190+
if "Dead Code Elimination" in provenance:
2191+
break
2192+
i -= 1
2193+
trace = traces[i]
2194+
2195+
from thunder.core.transform_common import dce, dce_bsyms
2196+
2197+
dced_trace = dce(trace)
2198+
dced_bsyms = dce_bsyms(trace.bound_symbols, trace.output)
2199+
assert len(dced_trace.bound_symbols) == len(trace.bound_symbols) - 1
2200+
assert len(dced_trace.bound_symbols) == len(dced_bsyms)
2201+
2202+
21732203
def test_symbol_flat_args():
21742204
from thunder.core.symbol import Symbol, BoundSymbol
21752205

@@ -3295,22 +3325,28 @@ def clean(tr):
32953325

32963326

32973327
def test_prims_pack_list():
3298-
def foo():
3299-
pass
3300-
3301-
trace = TraceCtx(foo)
3328+
def foo(x):
3329+
a, b = x
3330+
return [a, b]
33023331

33033332
a = torch.randn(2, 2)
33043333
b = torch.randn(2, 2)
33053334

3335+
jfoo = thunder.jit(foo)
3336+
jfoo((a, b))
3337+
3338+
trace = thunder.last_traces(jfoo)[-1]
3339+
3340+
return_bsym = trace.bound_symbols[-1]
3341+
trace.bound_symbols = trace.bound_symbols[:-1]
3342+
33063343
with tracectx(trace):
3307-
x = prims.unpack_trivial(a, name="x")
3308-
y = prims.unpack_trivial(b, name="y")
3344+
x, y = return_bsym.flat_args
33093345
packed_list = prims.pack_list(x, y)
33103346
prims.python_return(packed_list)
33113347

33123348
func = trace.python_callable()
3313-
actual = func()
3349+
actual = func(a, b)
33143350
expected = [a, b]
33153351

33163352
assert isinstance(actual, list) and actual == expected

0 commit comments

Comments
 (0)