Skip to content

Commit

Permalink
perf: buffer in_data_edges and out_data_edges
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasrothenberger committed Jul 30, 2024
1 parent ed1a50e commit 00bc581
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 7 deletions.
120 changes: 118 additions & 2 deletions discopop_explorer/PEGraphX.py
Original file line number Diff line number Diff line change
Expand Up @@ -1316,13 +1316,68 @@ def get_variables(self, nodes: Sequence[Node]) -> Dict[Variable, Set[MemoryRegio
res[v] = set()
out_data_edges = self.out_edges(node.id, EdgeType.DATA)
in_data_edges = self.in_edges(node.id, EdgeType.DATA)

# split depdendencies by variable names
var_name_to_deps: Dict[Optional[str], List[Dependency]] = dict()
for _, _, dep in out_data_edges + in_data_edges:
if dep.var_name not in var_name_to_deps:
var_name_to_deps[dep.var_name] = []
var_name_to_deps[dep.var_name].append(dep)

# try to identify memory regions
for var_name in res:
# since the variable name is checked for equality afterwards,
# it is safe to consider incoming dependencies at this point as well.
# Note that INIT type edges are considered as well!
# for _, _, dep in out_data_edges + in_data_edges:
# if dep.var_name == var_name.name:
# if dep.memory_region is not None:
# res[var_name].add(dep.memory_region)
if var_name.name in var_name_to_deps:
for dep in var_name_to_deps[var_name.name]:
if dep.memory_region is not None:
res[var_name].add(dep.memory_region)
return res

def get_variables_using_buffered_dependencies(
self,
nodes: Sequence[Node],
in_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
out_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
) -> Dict[Variable, Set[MemoryRegion]]:
"""Gets all variables and corresponding memory regions in nodes
:param nodes: nodes
:return: Set of variables
"""
res: Dict[Variable, Set[MemoryRegion]] = dict()
for node in nodes:
if isinstance(node, CUNode):
for v in node.local_vars:
if v not in res:
res[v] = set()
for v in node.global_vars:
if v not in res:
res[v] = set()

# split depdendencies by variable names
var_name_to_deps: Dict[Optional[str], List[Dependency]] = dict()
for _, _, dep in out_data_dependencies[node.id] + in_data_dependencies[node.id]:
if dep.var_name not in var_name_to_deps:
var_name_to_deps[dep.var_name] = []
var_name_to_deps[dep.var_name].append(dep)

# try to identify memory regions
for var_name in res:
# since the variable name is checked for equality afterwards,
# it is safe to consider incoming dependencies at this point as well.
# Note that INIT type edges are considered as well!
for _, _, dep in out_data_edges + in_data_edges:
if dep.var_name == var_name.name:
# for _, _, dep in out_data_dependencies[node.id] + in_data_dependencies[node.id]:
# if dep.var_name == var_name.name:
# if dep.memory_region is not None:
# res[var_name].add(dep.memory_region)
if var_name.name in var_name_to_deps:
for dep in var_name_to_deps[var_name.name]:
if dep.memory_region is not None:
res[var_name].add(dep.memory_region)
return res
Expand Down Expand Up @@ -1427,6 +1482,33 @@ def is_loop_index(self, var_name: Optional[str], loops_start_lines: List[LineID]

return False

def is_loop_index_using_buffered_dependencies(
self,
var_name: Optional[str],
loops_start_lines: List[LineID],
children: Sequence[Node],
out_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
) -> bool:
"""Checks, whether the variable is a loop index.
:param var_name: name of the variable
:param loops_start_lines: start lines of the loops
:param children: children nodes of the loops
:return: true if edge represents loop index
"""

# If there is a raw dependency for var, the source cu is part of the loop
# and the dependency occurs in loop header, then var is loop index+

for c in children:
for t, d in [
(t, d) for s, t, d in out_data_dependencies[c.id] if d.dtype == DepType.RAW and d.var_name == var_name
]:
if d.sink_line == d.source_line and d.source_line in loops_start_lines and self.node_at(t) in children:
return True

return False

def is_readonly_inside_loop_body(
self,
dep: Dependency,
Expand Down Expand Up @@ -1461,6 +1543,40 @@ def is_readonly_inside_loop_body(
return False
return True

def is_readonly_inside_loop_body_using_buffered_dependencies(
self,
dep: Dependency,
root_loop: Node,
children_cus: Sequence[Node],
children_loops: Sequence[Node],
in_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
out_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
loops_start_lines: Optional[List[LineID]] = None,
) -> bool:
"""Checks, whether a variable is read-only in loop body
:param dep: dependency variable
:param root_loop: root loop
:return: true if variable is read-only in loop body
"""
if loops_start_lines is None:
loops_start_lines = [v.start_position() for v in children_loops]

for v in children_cus:
for t, d in [
(t, d) for s, t, d in out_data_dependencies[v.id] if d.dtype == DepType.WAR or d.dtype == DepType.WAW
]:
# If there is a waw dependency for var, then var is written in loop
# (sink is always inside loop for waw/war)
if dep.memory_region == d.memory_region and not (d.sink_line in loops_start_lines):
return False
for t, d in [(t, d) for s, t, d in in_data_dependencies[v.id] if d.dtype == DepType.RAW]:
# If there is a reverse raw dependency for var, then var is written in loop
# (source is always inside loop for reverse raw)
if dep.memory_region == d.memory_region and not (d.source_line in loops_start_lines):
return False
return True

def get_parent_function(self, node: Node) -> FunctionNode:
"""Finds the parent of a node
Expand Down
27 changes: 22 additions & 5 deletions discopop_explorer/pattern_detectors/reduction_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,18 @@ def __detect_reduction(pet: PEGraphX, root: LoopNode) -> bool:
parent_function_lineid = pet.get_parent_function(root).start_position()
called_functions_lineids = __get_called_functions(pet, root)

# get in and out data dependencies for CUs in loop
in_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]] = dict()
out_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]] = dict()
for rc_cu in root_children_cus:
in_data_dependencies[rc_cu.id] = pet.in_edges(rc_cu.id, EdgeType.DATA)
out_data_dependencies[rc_cu.id] = pet.out_edges(rc_cu.id, EdgeType.DATA)

# get variables which are defined inside the loop
defined_inside_loop: List[Tuple[Variable, Set[MemoryRegion]]] = []
tmp_loop_variables = pet.get_variables(root_children_cus)
tmp_loop_variables = pet.get_variables_using_buffered_dependencies(
root_children_cus, in_data_dependencies, out_data_dependencies
)
for var in tmp_loop_variables:
if ":" in var.defLine:
file_id = int(var.defLine.split(":")[0])
Expand All @@ -181,6 +190,8 @@ def __detect_reduction(pet: PEGraphX, root: LoopNode) -> bool:
parent_loops,
parent_function_lineid,
called_functions_lineids,
in_data_dependencies,
out_data_dependencies,
):
return False

Expand All @@ -206,6 +217,8 @@ def __check_loop_dependencies(
parent_loops: List[LineID],
parent_function_lineid: LineID,
called_functions_lineids: List[LineID],
in_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
out_data_dependencies: Dict[NodeID, List[Tuple[NodeID, NodeID, Dependency]]],
) -> bool:
"""Returns True, if dependencies between the respective subgraphs chave been found.
Returns False otherwise, which results in the potential suggestion of a Reduction pattern."""
Expand All @@ -215,8 +228,8 @@ def __check_loop_dependencies(
# get dependency edges between children nodes
deps = set()
for n in loop_children_ids:
deps.update([(s, t, d) for s, t, d in pet.in_edges(n, EdgeType.DATA) if s in loop_children_ids])
deps.update([(s, t, d) for s, t, d in pet.out_edges(n, EdgeType.DATA) if t in loop_children_ids])
deps.update([(s, t, d) for s, t, d in in_data_dependencies[n] if s in loop_children_ids])
deps.update([(s, t, d) for s, t, d in out_data_dependencies[n] if t in loop_children_ids])

# get memory regions which are defined inside the loop
memory_regions_defined_in_loop = set()
Expand All @@ -225,18 +238,22 @@ def __check_loop_dependencies(

for source, target, dep in deps:
# check if targeted variable is readonly inside loop
if pet.is_readonly_inside_loop_body(
if pet.is_readonly_inside_loop_body_using_buffered_dependencies(
dep,
root_loop,
root_children_cus,
root_children_loops,
in_data_dependencies,
out_data_dependencies,
loops_start_lines=loop_start_lines,
):
# variable is readonly -> no problem
continue

# check if targeted variable is loop index
if pet.is_loop_index(dep.var_name, loop_start_lines, root_children_cus):
if pet.is_loop_index_using_buffered_dependencies(
dep.var_name, loop_start_lines, root_children_cus, out_data_dependencies
):
continue

# ignore dependencies where either source or sink do not lie within root_loop
Expand Down

0 comments on commit 00bc581

Please sign in to comment.