Skip to content

Commit 76e261b

Browse files
committed
Add deathstar benchmark test for new IR
1 parent c5f202a commit 76e261b

File tree

25 files changed

+602
-458
lines changed

25 files changed

+602
-458
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import uuid
2+
from cascade import cascade
3+
4+
@cascade
5+
class ComposeReview:
6+
def __init__(self, req_id: str, *args): # *args is a temporary hack to allow for creation of composereview on the fly
7+
self.req_id = req_id
8+
self.review_data = {}
9+
10+
def upload_unique_id(self, review_id: int):
11+
self.review_data["review_id"] = review_id
12+
13+
# could use the User class instead?
14+
def upload_user_id(self, user_id: str):
15+
self.review_data["userId"] = user_id
16+
17+
def upload_movie_id(self, movie_id: str):
18+
self.review_data["movieId"] = movie_id
19+
20+
def upload_rating(self, rating: int):
21+
self.review_data["rating"] = rating
22+
23+
def upload_text(self, text: str):
24+
self.review_data["text"] = text
25+
26+
def get_data(self):
27+
x = self.review_data
28+
return x
29+
30+
@cascade
31+
class User:
32+
def __init__(self, username: str, user_data: dict):
33+
self.username = username
34+
self.user_data = user_data
35+
36+
def upload_user(self, review: ComposeReview):
37+
user_id = self.user_data["userId"]
38+
review.upload_user_id(user_id)
39+
40+
@cascade
41+
class MovieId:
42+
# key: 'title'
43+
def __init__(self, title: str, movie_id: str):
44+
self.title = title
45+
self.movie_id = movie_id
46+
47+
def upload_movie(self, review: ComposeReview, rating: int):
48+
# if self.movie_id is not None:
49+
# review.upload_movie_id(self.movie_id)
50+
# else:
51+
# review.upload_rating(rating)
52+
movie_id = self.movie_id
53+
review.upload_movie_id(movie_id)
54+
55+
@cascade
56+
class Frontend():
57+
@staticmethod
58+
def compose(review: ComposeReview, user: User, title: MovieId, rating: int, text: str):
59+
UniqueId.upload_unique_id_2(review)
60+
user.upload_user(review)
61+
title.upload_movie(review, rating)
62+
# text = text[:CHAR_LIMIT] # an operation like this could be reorderd for better efficiency!
63+
Text.upload_text_2(review, text)
64+
65+
@cascade
66+
class UniqueId():
67+
@staticmethod
68+
def upload_unique_id_2(review: ComposeReview):
69+
# TODO: support external libraries
70+
# review_id = uuid.uuid1().int >> 64
71+
review_id = 424242
72+
review.upload_unique_id(review_id)
73+
74+
@cascade
75+
class Text():
76+
@staticmethod
77+
def upload_text_2(review: ComposeReview, text: str):
78+
review.upload_text(text)
79+
80+
81+
@cascade
82+
class Plot:
83+
def __init__(self, movie_id: str, plot: str):
84+
self.movie_id = movie_id
85+
self.plot = plot
86+
87+
@cascade
88+
class MovieInfo:
89+
def __init__(self, movie_id: str, info: dict):
90+
self.movie_id = movie_id
91+
self.info = info

deathstar_movie_review/entities/frontend.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from deathstar_movie_review.entities.user import User
99
from deathstar_movie_review.entities.text import Text, text_op
1010

11-
1211
CHAR_LIMIT = 50
1312

1413
# frontend is made stateless

deathstar_movie_review/entities/unique_id.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from cascade.dataflow.operator import Block, StatelessOperator
55
from deathstar_movie_review.entities.compose_review import ComposeReview
66

7-
87
class UniqueId():
98
@staticmethod
109
def upload_unique_id_2(review: ComposeReview):
Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import sys
22
import os
33

4+
45
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../src")))
56

7+
from cascade.dataflow.operator import StatefulOperator, StatelessOperator
68
from cascade.dataflow.dataflow import Event, InitClass, InvokeMethod, OpNode
79
from cascade.dataflow.optimization.dead_node_elim import dead_node_elimination
810
from cascade.runtime.python_runtime import PythonClientSync, PythonRuntime
@@ -11,35 +13,36 @@
1113
from deathstar_movie_review.entities.movie import MovieId, movie_id_op, movie_info_op, plot_op
1214
from deathstar_movie_review.entities.frontend import frontend_op, text_op, unique_id_op, frontend_df_serial
1315

16+
import cascade
1417

15-
16-
def test_deathstar_movie_demo_python():
17-
print("starting")
18+
def init_python_runtime() -> tuple[PythonRuntime, PythonClientSync]:
1819
runtime = PythonRuntime()
20+
for op in cascade.core.operators.values():
21+
if isinstance(op, StatefulOperator):
22+
runtime.add_operator(op)
23+
elif isinstance(op, StatelessOperator):
24+
runtime.add_stateless_operator(op)
1925

20-
# make sure we're running the serial version
21-
prev_df = frontend_op.dataflow
22-
frontend_op.dataflow = frontend_df_serial()
23-
26+
runtime.run()
27+
return runtime, PythonClientSync(runtime)
2428

25-
print(frontend_op.dataflow.to_dot())
26-
dead_node_elimination([], [frontend_op])
27-
print(frontend_op.dataflow.to_dot())
29+
import time
30+
def test_deathstar_movie_demo_python():
31+
print("starting")
32+
cascade.core.clear()
33+
exec(f'import deathstar_movie_review.entities.entities')
34+
cascade.core.init()
2835

29-
runtime.add_operator(compose_review_op)
30-
runtime.add_operator(user_op)
31-
runtime.add_operator(movie_info_op)
32-
runtime.add_operator(movie_id_op)
33-
runtime.add_operator(plot_op)
34-
runtime.add_stateless_operator(frontend_op)
35-
runtime.add_stateless_operator(unique_id_op)
36-
runtime.add_stateless_operator(text_op)
36+
runtime, client = init_python_runtime()
37+
user_op = cascade.core.operators["User"]
38+
compose_op = cascade.core.operators["ComposeReview"]
39+
movie_op = cascade.core.operators["MovieId"]
40+
frontend_op = cascade.core.operators["Frontend"]
3741

38-
runtime.run()
39-
client = PythonClientSync(runtime)
42+
for df in cascade.core.dataflows.values():
43+
print(df.to_dot())
4044

41-
init_user = OpNode(User, InitClass(), read_key_from="username")
42-
username = "username_1"
45+
username = "myUsername"
4346
user_data = {
4447
"userId": "user1",
4548
"FirstName": "firstname",
@@ -48,27 +51,32 @@ def test_deathstar_movie_demo_python():
4851
"Password": "****",
4952
"Salt": "salt"
5053
}
54+
5155
print("testing user create")
52-
event = Event(init_user, {"username": username, "user_data": user_data}, None)
56+
57+
event = user_op.dataflows["__init__"].generate_event({"username": username, "user_data": user_data}, username)
5358
result = client.send(event)
54-
assert isinstance(result, User) and result.username == username
59+
print(result)
60+
assert result.username == username
5561

5662
print("testing compose review")
57-
req_id = 1
63+
req_id = "1"
5864
movie_title = "Cars 2"
5965
movie_id = 1
6066

6167
# make the review
62-
init_compose_review = OpNode(ComposeReview, InitClass(), read_key_from="req_id")
63-
event = Event(init_compose_review, {"req_id": req_id}, None)
68+
event = compose_op.dataflows["__init__"].generate_event({"req_id": req_id}, req_id)
6469
result = client.send(event)
6570
print("review made")
6671

6772

68-
# make the movie
69-
init_movie = OpNode(MovieId, InitClass(), read_key_from="title")
70-
event = Event(init_movie, {"title": movie_title, "movie_id": movie_id}, None)
73+
74+
# # make the movie
75+
# init_movie = OpNode(MovieId, InitClass(), read_key_from="title")
76+
event = movie_op.dataflows["__init__"].generate_event({"title": movie_title, "movie_id": movie_id}, movie_title)
7177
result = client.send(event)
78+
# event = Event(init_movie, {"title": movie_title, "movie_id": movie_id}, None)
79+
# result = client.send(event)
7280
print("movie made")
7381

7482
# compose the review
@@ -80,11 +88,11 @@ def test_deathstar_movie_demo_python():
8088
"text": "good movie!"
8189
}
8290

83-
event = Event(
84-
frontend_op.dataflow.entry,
85-
review_data,
86-
frontend_op.dataflow)
87-
result = client.send(event)
91+
r_data = {r+"_0": v for r, v in review_data.items()}
92+
93+
event = frontend_op.dataflows["compose"].generate_event(r_data)
94+
result = client.send(event, block=False)
95+
8896
print(result)
8997
print("review composed")
9098

@@ -96,19 +104,19 @@ def test_deathstar_movie_demo_python():
96104
{"req_id": req_id},
97105
None
98106
)
107+
event = compose_op.dataflows["get_data"].generate_event({"req_id": req_id}, req_id)
99108
result = client.send(event)
109+
print(result)
110+
print(runtime.statefuloperators["ComposeReview"].states["1"].review_data)
111+
# time.sleep(0.5)
112+
113+
# result = client.send(event)
100114
expected = {
101115
"userId": user_data["userId"],
102116
"movieId": movie_id,
103117
"text": review_data["text"]
104118
}
105-
print(result, expected)
119+
# print(result, expected)
106120
assert "review_id" in result
107121
del result["review_id"] # randomly generated
108-
assert result == expected
109-
110-
print("Success!")
111-
112-
# put the df back
113-
frontend_op.dataflow = prev_df
114-
122+
assert result == expected

src/cascade/core.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
from klara.core import nodes
55
from klara.core.tree_rewriter import AstBuilder
66
from klara.core.cfg import Cfg
7-
7+
from klara.core.node_classes import Arguments
88

99
from cascade.dataflow.operator import Block, StatefulOperator, StatelessOperator
1010
from cascade.wrappers import ClassWrapper
1111
from cascade.descriptors import ClassDescriptor, MethodDescriptor
12-
from cascade.frontend.generator.generate_split_functions import GenerateSplitFunctions
12+
from cascade.frontend.generator.generate_split_functions import GenerateSplitFunctions, GroupStatements
1313
from cascade.frontend.generator.generate_dataflow import GenerateDataflow
14-
from cascade.dataflow.dataflow import DataFlow, Operator
14+
from cascade.dataflow.dataflow import CallLocal, DataFlow, DataflowRef, InitClass, Operator
1515
from cascade.frontend.intermediate_representation import StatementDataflowGraph
1616
from cascade.frontend.generator.build_compiled_method_string import BuildCompiledMethodsString
1717
from cascade.frontend.ast_visitors import ExtractTypeVisitor
@@ -28,6 +28,7 @@ def setup_cfg(code: str) -> Cfg:
2828
registered_classes: list[ClassWrapper] = []
2929

3030
operators: dict[str, Operator] = {}
31+
dataflows: dict[DataflowRef, DataFlow] = {}
3132

3233
def cascade(cls, parse_file=True):
3334
if not isclass(cls):
@@ -54,12 +55,8 @@ def cascade(cls, parse_file=True):
5455
registered_classes.append(class_wrapper)
5556

5657

57-
58-
def build(method) -> tuple[DataFlow, list[Block]]:
59-
# TODO: implement
60-
pass
61-
6258
def init():
59+
# First pass: register operators/classes
6360
for cls in registered_classes:
6461
op_name = cls.class_desc.class_name
6562

@@ -72,13 +69,35 @@ def init():
7269

7370
# generate split functions
7471
for method in cls.class_desc.methods_dec:
75-
method.build_dataflow()
76-
df, blocks = build(method)
72+
df_ref = DataflowRef(op_name, method.method_name)
73+
# Add version number manually
74+
args = [f"{str(arg)}_0" for arg in method.method_node.args.args]
75+
# TODO: cleaner solution that checks if the function is stateful or not
76+
if args[0] == "self_0":
77+
args = args[1:]
78+
dataflows[df_ref] = DataFlow(method.method_name, op_name, args)
79+
80+
operators[op_name] = op
81+
82+
# Second pass: build dataflows
83+
for cls in registered_classes:
84+
op_name = cls.class_desc.class_name
85+
op = operators[op_name]
86+
87+
# generate split functions
88+
for method in cls.class_desc.methods_dec:
89+
if method.method_name == "__init__":
90+
df = DataFlow("__init__", op_name)
91+
n0 = CallLocal(InitClass())
92+
df.entry = [n0]
93+
blocks = []
94+
else:
95+
df, blocks = GroupStatements(method.method_node).build(dataflows, op_name)
96+
7797
op.dataflows[df.name] = df
7898
for b in blocks:
7999
op.methods[b.name] = b
80100

81-
operators[op_name] = op
82101

83102

84103

0 commit comments

Comments
 (0)