Skip to content

Commit 590b3d6

Browse files
committed
[frontend,examples,notebooks,tests] Finish Network spec implementation
1 parent 9cd4bdd commit 590b3d6

10 files changed

Lines changed: 290 additions & 37 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ push-arm64:
6666
run-docker:
6767
docker-compose up
6868

69+
clean-notebooks:
70+
nb-clean clean notebooks/*.ipynb
71+
6972
.PHONY: generate-docs
7073
generate-docs:
7174
# pip install sphinx-autobuild sphinx_autodoc_typehints sphinx-copybutton pydata-sphinx-theme

accelforge/frontend/arch/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Arch.model_rebuild()
1616
Fork.model_rebuild()
1717
Hierarchical.model_rebuild()
18+
Flat.model_rebuild()
1819
Network.model_rebuild()
1920

2021
__all__ = [
@@ -28,6 +29,7 @@
2829
"Compute",
2930
"Container",
3031
"Fork",
32+
"Flat",
3133
"Hierarchical",
3234
"Leaf",
3335
"Memory",

accelforge/frontend/arch/components.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,5 +1000,11 @@ def _render_node_color(self) -> str:
10001000
return "#E0EEFF"
10011001

10021002

1003-
class Network(Component, Branch):
1004-
shape: EvalableList[Spatial]
1003+
class Network(Component, Leaf):
1004+
shape: EvalableList[Spatial] = EvalableList()
1005+
1006+
def _render_node_shape(self) -> str:
1007+
return "Msquare"
1008+
1009+
def _render_node_color(self) -> str:
1010+
return "#FAF8C8"

accelforge/frontend/arch/structure.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,29 @@ def find(self, name: str, default: Any = _FIND_SENTINEL) -> Union["Leaf", Any]:
6868
return default
6969
raise ValueError(f"Leaf {name} not found in {self}")
7070

71+
def iterate_hierarchically(self, _parents=None):
72+
"""
73+
Iterates over all Leaf nodes while also yielding the list of all Leaf
74+
nodes that are hierarchical parents over the current node.
75+
"""
76+
if _parents is None:
77+
_parents = []
78+
79+
if isinstance(self, Leaf):
80+
yield self, _parents
81+
_parents.append(self)
82+
return
83+
84+
assert isinstance(self, Branch)
85+
if isinstance(self, (Fork, Flat)):
86+
for node in self.nodes:
87+
yield from node.iterate_hierarchically(list(_parents))
88+
elif isinstance(self, Hierarchical):
89+
for node in self.nodes:
90+
yield from node.iterate_hierarchically(_parents)
91+
else:
92+
raise RuntimeError("unhandled structure type")
93+
7194
def _render_node_name(self) -> str:
7295
"""The name for a Pydot node."""
7396
return f"{self.__class__.__name__}_{id(self)}"
@@ -164,6 +187,7 @@ class Branch(ArchNode):
164187
Annotated["Container", Tag("Container")],
165188
Annotated["Network", Tag("Network")],
166189
Annotated["Hierarchical", Tag("Hierarchical")],
190+
Annotated["Flat", Tag("Flat")],
167191
Annotated["Fork", Tag("Fork")],
168192
],
169193
Discriminator(_get_tag),
@@ -236,6 +260,68 @@ def _power_gating(
236260
return result, non_power_gated_porp
237261

238262

263+
class Flat(Branch):
264+
def _parent2child_names(
265+
self, parent_name: str = None
266+
) -> tuple[list[tuple[str, str]], str]:
267+
from accelforge.frontend.arch.components import Compute
268+
269+
edges = []
270+
current_parent_name = parent_name
271+
272+
for node in self.nodes:
273+
if isinstance(node, (Hierarchical, Fork)):
274+
child_edges, _last_child_name = node._parent2child_names(
275+
current_parent_name
276+
)
277+
edges.extend(child_edges)
278+
elif isinstance(node, Flat):
279+
child_edges, _last_child_name = node._parent2child_names(
280+
current_parent_name
281+
)
282+
edges.extend(child_edges)
283+
elif isinstance(node, Compute):
284+
# Compute nodes branch off to the side like a Fork
285+
if current_parent_name is not None:
286+
edges.append((current_parent_name, node._render_node_name()))
287+
else:
288+
if current_parent_name is not None:
289+
edges.append((current_parent_name, node._render_node_name()))
290+
return edges, current_parent_name
291+
292+
def _render_node(self) -> pydot.Node:
293+
"""Hierarchical nodes should not be rendered."""
294+
return None
295+
296+
def _render_make_children(self) -> list[pydot.Node]:
297+
"""Renders only children, not the Hierarchical node itself."""
298+
result = []
299+
for node in self.nodes:
300+
children = node._render_make_children()
301+
result.extend(child for child in children if child is not None)
302+
return result
303+
304+
def render(self) -> str:
305+
"""Renders the architecture as a Pydot graph."""
306+
graph = _pydot_graph()
307+
308+
# Render all nodes (Hierarchical nodes return None and are filtered out)
309+
for node in self._render_make_children():
310+
if node is not None:
311+
graph.add_node(node)
312+
313+
# Add edges
314+
edges, _ = self._parent2child_names()
315+
print(edges)
316+
for parent_name, child_name in edges:
317+
graph.add_edge(pydot.Edge(parent_name, child_name))
318+
319+
return _SVGJupyterRender(graph.create_svg(prog="dot").decode("utf-8"))
320+
321+
def _repr_svg_(self) -> str:
322+
return self.render()
323+
324+
239325
class Hierarchical(Branch):
240326
def _flatten(
241327
self,
@@ -300,6 +386,13 @@ def _parent2child_names(
300386
edges.extend(child_edges)
301387
if last_child_name is not None:
302388
current_parent_name = last_child_name
389+
elif isinstance(node, Flat):
390+
child_edges, last_child_name = node._parent2child_names(
391+
current_parent_name
392+
)
393+
edges.extend(child_edges)
394+
if last_child_name is not None:
395+
current_parent_name = last_child_name
303396
elif isinstance(node, Compute):
304397
# Compute nodes branch off to the side like a Fork
305398
if current_parent_name is not None:

accelforge/frontend/spec.py

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from accelforge.frontend.mapper import FFM
44
from accelforge.frontend.renames import EinsumName, Renames
55
from accelforge.util._eval_expressions import EvaluationError, ParseExpressionsContext
6-
from accelforge.frontend.arch import Compute, Leaf, Component, Arch, Container
6+
from accelforge.frontend.arch import Compute, Leaf, Component, Arch, Container, Spatialable
77

88
from accelforge.frontend.workload import Workload
99
from accelforge.frontend.variables import Variables
@@ -194,7 +194,6 @@ def calculate_component_area_energy_latency_leak(
194194
if einsum_name is None and len(self.workload.einsums) > 0:
195195
einsum_name = self.workload.einsums[0].name
196196

197-
components = set()
198197
try:
199198
if not getattr(self, "_evaluated", False):
200199
self = self._spec_eval_expressions(einsum_name=einsum_name)
@@ -211,36 +210,37 @@ def calculate_component_area_energy_latency_leak(
211210
)
212211
raise
213212

214-
for arch in self._get_flattened_architecture():
215-
fanout = 1
216-
for component in arch:
217-
fanout *= component.get_fanout()
218-
if component.name in components or isinstance(component, Container):
219-
continue
220-
assert isinstance(component, Component)
221-
components.add(component.name)
222-
orig: Component = self.arch.find(component.name)
223-
c = component
224-
if area:
225-
c = c.calculate_area(models)
226-
orig.area = c.area
227-
orig.total_area = c.area * fanout
228-
if energy:
229-
c = c.calculate_action_energy(models)
230-
for a in c.actions:
231-
orig_action = orig.actions[a.name]
232-
orig_action.energy = a.energy
233-
if latency:
234-
c = c.calculate_action_latency(models)
235-
for a in c.actions:
236-
orig_action = orig.actions[a.name]
237-
orig_action.latency = a.latency
238-
if leak:
239-
c = c.calculate_leak_power(models)
240-
orig.leak_power = c.leak_power
241-
orig.total_leak_power = c.leak_power * fanout
242-
orig.component_modeling_log.extend(c.component_modeling_log)
243-
orig.component_model = c.component_model
213+
for leaf, parents in self.arch.iterate_hierarchically():
214+
if not isinstance(leaf, Component):
215+
continue
216+
217+
global_fanout = 1
218+
for p in parents:
219+
if isinstance(p, Spatialable):
220+
global_fanout *= p.get_fanout()
221+
222+
orig: Component = self.arch.find(leaf.name)
223+
c = leaf
224+
if area:
225+
c = c.calculate_area(models)
226+
orig.area = c.area
227+
orig.total_area = c.area * global_fanout
228+
if energy:
229+
c = c.calculate_action_energy(models)
230+
for a in c.actions:
231+
orig_action = orig.actions[a.name]
232+
orig_action.energy = a.energy
233+
if latency:
234+
c = c.calculate_action_latency(models)
235+
for a in c.actions:
236+
orig_action = orig.actions[a.name]
237+
orig_action.latency = a.latency
238+
if leak:
239+
c = c.calculate_leak_power(models)
240+
orig.leak_power = c.leak_power
241+
orig.total_leak_power = c.leak_power * global_fanout
242+
orig.component_modeling_log.extend(c.component_modeling_log)
243+
orig.component_model = c.component_model
244244

245245
return self
246246

examples/arches/networked/flat.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ arch:
1212

1313
- !Network
1414
name: NoC
15+
area: 0
16+
leak_power: 0
1517
shape:
1618
- {name: X, fanout: 4}
1719
- {name: Y, fanout: 4}
1820
actions: []
21+
22+
- !Flat
1923
nodes:
2024
- !Memory
2125
name: GlobalBuffer
@@ -80,5 +84,7 @@ arch:
8084

8185
- !Compute
8286
name: MAC
87+
area: 0
88+
leak_power: 0
8389
actions:
8490
- {name: compute, energy: 0, latency: 0}
File renamed without changes.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
arch:
2+
nodes:
3+
- !Memory
4+
name: MainMemory
5+
size: inf
6+
area: 0
7+
leak_power: 0
8+
tensors: {keep: All}
9+
actions:
10+
- {name: read, energy: 0, latency: 0}
11+
- {name: write, energy: 0, latency: 0}
12+
13+
- !Network
14+
name: NoC
15+
area: 0
16+
leak_power: 0
17+
actions: []
18+
19+
- !Memory
20+
name: HBM
21+
size: inf
22+
area: 0
23+
leak_power: 0
24+
tensors: {keep: ~MainMemory, may_keep: All}
25+
actions:
26+
- {name: read, energy: 0, latency: 0}
27+
- {name: write, energy: 0, latency: 0}
28+
spatial:
29+
- {name: X, fanout: 4}
30+
- {name: Y, fanout: 4}
31+
32+
- !Memory
33+
name: Buffer
34+
size: inf
35+
area: 0
36+
leak_power: 0
37+
actions:
38+
- {name: read, energy: 0, latency: 0}
39+
- {name: write, energy: 0, latency: 0}
40+
41+
- !Compute
42+
name: MAC
43+
area: 0
44+
leak_power: 0
45+
actions:
46+
- {name: compute, energy: 0, latency: 0}

0 commit comments

Comments
 (0)