Skip to content

Commit 6ff29cf

Browse files
authored
Prepare artifact (apache#40)
2 parents dddc10f + 75e6004 commit 6ff29cf

6 files changed

Lines changed: 151 additions & 50 deletions

File tree

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# DeepVisor
2+
DeepVisor is a JIT compiler for PyTorch programs. It can extract the operator graph from PyTorch programs and optimize the graph with a wide range of deep learning graph compilers.
3+
4+
# Installation
5+
DeepVisor now supports Python 3.9. The support of other Python versions is working in progress.
6+
7+
1. Install CUDA. CUDA 11.8 is recommended.
8+
2. Install dependencies:
9+
```bash
10+
pip install -r requirements.txt -f https://download.pytorch.org/whl/torch_stable.html
11+
```
12+
3. Install DeepVisor:
13+
```bash
14+
pip install -e .
15+
```
16+
4. Compile a shared library to disable Python integer cache by LD_PRELOAD. This script will generates a ``ldlong.v3.9.12.so'' file in build/ directory. You need to set the LD_PRELOAD environment variable to this file when running the PyTorch program.
17+
```bash
18+
cd scripts
19+
./compile_longobj.sh
20+
```
21+
22+
# Example Usage
23+
24+
The following script compiles and runs a simple PyTorch program with DeepVisor.
25+
26+
```python
27+
LD_PRELOAD=build/ldlong.v3.9.12.so python test/example.py
28+
```
29+
30+
# Citation
31+
If you find DeepVisor useful in your research, please consider citing the following paper:
32+
33+
> DeepVisor: Effective Operator Graph Instantiation for Deep Learning by Execution State Monitoring; Chen Zhang, Rongchao Dong, Haojie Wang, Runxin Zhong, Jike Chen, and Jidong Zhai, Tsinghua University; will be appeared in USENIX ATC'24.
34+

frontend/cache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def update_code(self, f_code: CodeType, frame_id: int,
7777
from .bytecode_writter import rewrite_bytecode
7878
for i in (False, True):
7979
if i == is_callee or self.code[i] is not None:
80-
print("new_code for is_callee =", i)
80+
# print("new_code for is_callee =", i)
8181
new_code, code_map = rewrite_bytecode(f_code, frame_id, i)
8282
self.set_new_code(new_code, code_map, i)
8383
self.updated = False

frontend/fx_graph.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,25 @@ def eager_due_to_inductor_bug(node: torch.fx.Node) -> bool:
9696
gm, example_inputs)
9797
elif backend == 'script':
9898
import os, importlib, re, random
99-
random_number = str(random.randint(0, 1000000))
100-
os.makedirs('tmp/fx_module_' + random_number, exist_ok=True)
101-
gm.to_folder('tmp/fx_module_' + random_number)
99+
model_name = config.get_config('model_name')
100+
if model_name != "":
101+
folder_name = f'tmp/fx_module_{model_name}'
102+
else:
103+
random_number = str(random.randint(0, 1000000))
104+
folder_name = f'tmp/fx_module_{random_number}'
105+
106+
os.makedirs(folder_name, exist_ok=True)
107+
gm.to_folder(folder_name)
108+
109+
# replace "device(type='cuda', index=0)" with "device('cuda:0')"
110+
with open(f"{folder_name}/module.py", "r") as f:
111+
content = f.read()
112+
content = re.sub(r"device\(type='cuda', index=([0-9]+)\)",
113+
r"device('cuda:\1')", content)
114+
with open(f"{folder_name}/module.py", "w") as f:
115+
f.write(content)
102116

103-
module = importlib.import_module('tmp.fx_module_' + random_number)
117+
module = importlib.import_module(folder_name.replace('/', '.'))
104118
model = module.FxModule().cuda().eval()
105119
real_inputs = generate_real_tensors(example_inputs)
106120
with torch.no_grad():

frontend/guard_tracker.py

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,9 @@ def store_pos_in_caller(self, pos: StorePos,
511511
raise NotImplementedError
512512

513513
def merge_call(self, state: 'State', stack_objs: list[Any]) -> None:
514-
print("to merge graph", state.fx_graph.result_graph)
515-
print("to merge frameid", state.frame_id, self.frame_id)
514+
if config.get_config('debug'):
515+
print("to merge graph", state.fx_graph.result_graph)
516+
print("to merge frameid", state.frame_id, self.frame_id)
516517
# self.written = True
517518
# self.defer_restart = None
518519
replacement_mapping: dict[torch.fx.Node, torch.fx.Node] = {}
@@ -562,9 +563,9 @@ def merge_call_guard() -> None:
562563
ExtractFromMethod, ExtractFromFunction)):
563564
self_pos = self.store_pos_in_caller(pos, idx)
564565
if self_pos is None:
565-
print(
566-
"\033[34m[warning] cannot find store pos in caller, skip guard check\033[0m",
567-
type(var), var.extract_code_at_start)
566+
# print(
567+
# "\033[34m[warning] cannot find store pos in caller, skip guard check\033[0m",
568+
# type(var), var.extract_code_at_start)
568569
new_var.need_guard_check = False
569570
else:
570571
new_var.extract_code_at_start.append(self_pos)
@@ -1169,8 +1170,9 @@ def commit(self) -> None:
11691170
end_pc = self.code.get_orig_pc(lasti)
11701171
if end_pc == -1:
11711172
end_pc = self.code.get_next_orig_pc(lasti)
1172-
print("commiting", self.frame_id, self.state.start_pc, end_pc,
1173-
self.code.original_insts[end_pc], lasti)
1173+
if config.get_config('debug'):
1174+
print("commiting", self.frame_id, self.state.start_pc, end_pc,
1175+
self.code.original_insts[end_pc], lasti)
11741176
# TODO: can be optimized by only reproduce the modified variables
11751177
if self.state.defer_restart is not None:
11761178
stack_objs = self.state.defer_restart.stack_objs
@@ -1180,14 +1182,16 @@ def commit(self) -> None:
11801182

11811183
if self.state.start_pc == 0 and self.code.original_insts[
11821184
end_pc].opname == "RETURN_VALUE" and self.caller is not None:
1183-
print("callee is full graph, merge to caller")
1185+
if config.get_config('debug'):
1186+
print("callee is full graph, merge to caller")
11841187
assert len(stack_objs) == 1
11851188
caller = self.caller
11861189
assert caller is not None
11871190
caller.state.merge_call(self.state,
11881191
[get_value_stack_from_top(self.frame, 0)])
11891192
elif self.cf_info is not None and self.num_breaks == 1 and self.cf_info.end_pc == end_pc:
1190-
print("reach end of nested tracker, merge to caller")
1193+
if config.get_config('debug'):
1194+
print("reach end of nested tracker, merge to caller")
11911195
self.rewrite_loop_graph()
11921196
stack_objs = get_all_objects_in_stack(self.frame)
11931197
nest_caller = self.caller
@@ -1257,18 +1261,19 @@ def commit(self) -> None:
12571261

12581262
self.state.fx_graph.set_output_nodes(
12591263
graph_codegen.get_graph_outputs())
1260-
print("graph input", [
1261-
(name, x) for x, name in self.state.fx_graph.example_inputs
1262-
])
1263-
print("graph", self.state.fx_graph.result_graph)
1264-
from .control_flow import CondModule
1265-
for node in self.state.fx_graph.result_graph.nodes:
1266-
if node.op == 'call_module' and '.' not in node.target:
1267-
mod = getattr(self.state.root, node.target)
1268-
if isinstance(mod, CondModule):
1269-
print("CondModule:", node.target)
1270-
print("true_body:", mod.true_body.graph)
1271-
print("false_body:", mod.false_body.graph)
1264+
if config.get_config('debug'):
1265+
print("graph input",
1266+
[(name, x)
1267+
for x, name in self.state.fx_graph.example_inputs])
1268+
print("graph", self.state.fx_graph.result_graph)
1269+
from .control_flow import CondModule
1270+
for node in self.state.fx_graph.result_graph.nodes:
1271+
if node.op == 'call_module' and '.' not in node.target:
1272+
mod = getattr(self.state.root, node.target)
1273+
if isinstance(mod, CondModule):
1274+
print("CondModule:", node.target)
1275+
print("true_body:", mod.true_body.graph)
1276+
print("false_body:", mod.false_body.graph)
12721277

12731278
graph_code = graph_codegen.get_code()
12741279
compiled_graph = self.state.fx_graph.compile()
@@ -1278,16 +1283,19 @@ def commit(self) -> None:
12781283
{guard_code}
12791284
"""
12801285
out: Dict[str, Any] = dict()
1281-
print("RUNNING PY CODE")
1282-
print(py_code)
1286+
if config.get_config('debug'):
1287+
print("RUNNING PY CODE")
1288+
print(py_code)
12831289
exec(py_code, self.frame.f_globals, out)
12841290
guard_fn = out["___make_guard_fn"](*guard_codegen.objs.values())
12851291
graph_fn = out["___make_graph_fn"](compiled_graph,
12861292
*graph_codegen.objs.values())
12871293

1288-
print("guard_fn:", guard_fn)
1289-
print("pc:", self.state.start_pc, end_pc)
1290-
print("stack:", self.state.start_stack_size, len(stack_objs))
1294+
if config.get_config('debug'):
1295+
print("guard_fn:", guard_fn)
1296+
print("pc:", self.state.start_pc, end_pc)
1297+
print("stack:", self.state.start_stack_size,
1298+
len(stack_objs))
12911299

12921300
get_frame_cache(self.frame_id).add(
12931301
CachedGraph(
@@ -1526,7 +1534,8 @@ def make_sub_var(value: Any, fx_node: torch.fx.Node) -> None:
15261534
self.state.defer_restart = None
15271535

15281536
def restart(self, restart_reason: str, restart_caller: bool = True) -> None:
1529-
print(f"restart: {restart_reason}")
1537+
if config.get_config('debug'):
1538+
print(f"restart: {restart_reason}")
15301539
self.have_error = True
15311540
self.num_breaks += 1
15321541
self.commit()
@@ -1585,7 +1594,7 @@ def is_builtin_func(self, func: Callable[..., Any]) -> bool:
15851594
str.split, sorted)
15861595

15871596
def is_numpy_constant_func(self, func: Callable[..., Any]) -> bool:
1588-
print(dir(func))
1597+
# print(dir(func))
15891598
if (hasattr(func, '__module__') and 'numpy' in func.__module__ and
15901599
'random' not in func.__module__):
15911600
return True
@@ -1741,7 +1750,8 @@ def call_function(
17411750
]
17421751
})
17431752
return
1744-
print("run into user defined function", func)
1753+
if config.get_config('debug'):
1754+
print("run into user defined function", func)
17451755
stack_objs = get_all_objects_in_stack(self.frame)
17461756
self.state.mark_calling_func(func)
17471757
self.state.mark_defer_restart(
@@ -1825,7 +1835,8 @@ def set_if_inplace_return() -> None:
18251835
elif hasattr(func, "__self__") and isinstance(
18261836
func.__self__, torch.autograd.profiler.record_function):
18271837
return
1828-
print("record function in graph", func)
1838+
if config.get_config("debug"):
1839+
print("record function in graph", func)
18291840
self.state.record_function(
18301841
func,
18311842
args,
@@ -2772,15 +2783,17 @@ def push_tracker(frame: FrameType,
27722783
caller = None
27732784
new_tracker = GuardTracker(frame, frame_id, caller, read_stack, cf_info)
27742785
trackers.append(new_tracker)
2775-
print("push tracker", frame_id, "frame", hex(id(frame)),
2776-
"frame_id", frame_id, "read_stack", read_stack, "cf_info",
2777-
type(cf_info), "all", [t.frame_id for t in trackers])
2786+
if config.get_config('debug'):
2787+
print("push tracker", frame_id, "frame", hex(id(frame)),
2788+
"frame_id", frame_id, "read_stack", read_stack, "cf_info",
2789+
type(cf_info), "all", [t.frame_id for t in trackers])
27782790
return new_tracker
27792791

27802792

27812793
def pop_tracker(frame_id: int) -> None:
2782-
print("before pop_tracker", [t.frame_id for t in trackers], "frame_id",
2783-
frame_id)
2794+
if config.get_config('debug'):
2795+
print("before pop_tracker", [t.frame_id for t in trackers], "frame_id",
2796+
frame_id)
27842797
to_pop = trackers.pop()
27852798
if not get_config("enable_fallback"):
27862799
assert to_pop.frame_id == frame_id
@@ -2790,7 +2803,7 @@ def pop_tracker(frame_id: int) -> None:
27902803
def record(frame: FrameType, frame_id: int) -> None:
27912804
if id(frame) != id(trackers[-1].frame):
27922805
if trackers[-1].state.calling_func is not None:
2793-
print("push tracker due to record")
2806+
# print("push tracker due to record")
27942807
push_tracker(frame, frame_id)
27952808
trackers[-1].record(frame, frame_id)
27962809

frontend/tracer.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818

1919
def get_trace_func(frame_id: int) -> Callable[[FrameType, str, Any], None]:
20+
is_debug = get_config("debug")
2021

2122
def trace_func(frame: FrameType, event: str, arg: Any) -> None:
2223
global run_trace_func
@@ -26,16 +27,19 @@ def trace_func(frame: FrameType, event: str, arg: Any) -> None:
2627
if event == "opcode":
2728
opcode = frame.f_code.co_code[frame.f_lasti]
2829
opname = dis.opname[opcode]
29-
print(
30-
f"tracing {event} {opname} {arg} pc={frame.f_lasti} frame={frame_id}({hex(id(frame))})"
31-
)
30+
if is_debug:
31+
print(
32+
f"tracing {event} {opname} {arg} pc={frame.f_lasti} frame={frame_id}({hex(id(frame))})"
33+
)
3234
record(frame, frame_id)
3335
elif event == "line":
34-
print(
35-
f"tracing {event} {frame.f_code.co_filename}:{frame.f_lineno}"
36-
)
36+
if is_debug:
37+
print(
38+
f"tracing {event} {frame.f_code.co_filename}:{frame.f_lineno}"
39+
)
3740
else:
38-
print(f"tracing {event} in {frame.f_code.co_filename}")
41+
if is_debug:
42+
print(f"tracing {event} in {frame.f_code.co_filename}")
3943
except Exception as e:
4044
print("exception in trace_func:", e, type(e))
4145
print(traceback.format_exc())
@@ -61,7 +65,7 @@ def empty_trace_func(_frame: FrameType, _event: str, _arg: Any) -> None:
6165

6266
def enable_trace(frame_id: int) -> None:
6367
try:
64-
print("enable_trace")
68+
# print("enable_trace")
6569
this_frame = inspect.currentframe()
6670
assert this_frame is not None
6771
caller_frame = this_frame.f_back
@@ -76,7 +80,7 @@ def enable_trace(frame_id: int) -> None:
7680

7781
def disable_trace(frame_id: int) -> None:
7882
try:
79-
print("disable_trace")
83+
# print("disable_trace")
8084
pop_tracker(frame_id)
8185
sys.settrace(None)
8286
except Exception as e:

test/example.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import torch
2+
from frontend.compile import compile
3+
from frontend.utils import SetConfig
4+
5+
6+
class Example(torch.nn.Module):
7+
8+
def __init__(self):
9+
super(Example, self).__init__()
10+
self.conv = torch.nn.Conv2d(3, 3, 3)
11+
self.relu = torch.nn.ReLU()
12+
13+
def forward(self, x):
14+
x = self.conv(x)
15+
x = self.relu(x)
16+
return x
17+
18+
19+
with torch.no_grad():
20+
model = Example().eval()
21+
x = torch.randn(1, 3, 4, 4)
22+
expect_output = model(x)
23+
print("expect:", expect_output)
24+
25+
# set the graph compiler to inductor
26+
with SetConfig({'backend': 'inductor'}):
27+
compiled = compile(model)
28+
# run the python code to compile the model. The fx graph and the guards will be printed out
29+
output1 = compiled(x)
30+
print("output1:", output1)
31+
32+
# run the compiled model. "guard cache hit" means we find the compiled record and use it directly
33+
output2 = compiled(x)
34+
print("output2", output2)
35+
assert torch.allclose(expect_output, output1)
36+
assert torch.allclose(expect_output, output2)

0 commit comments

Comments
 (0)