def analyze_iterations(nodes): """ Attach :class:`IterationProperty` to :class:`Iteration` objects within ``nodes``. The recognized IterationProperty decorators are listed in ``nodes.IterationProperty._KNOWN``. """ sections = FindSections().visit(nodes) # Local analysis: detect Iteration properties, inspecting trees in isolation mapper = OrderedDict() for tree, exprs in sections.items(): deps_graph = compute_dependency_graph(exprs) mapper = detect_fully_parallel(tree, deps_graph, mapper) mapper = detect_outermost_parallel(tree, deps_graph, mapper) mapper = detect_outermost_sequential_inner_parallel( tree, deps_graph, mapper) mapper = detect_innermost_unitstride(tree, deps_graph, mapper) mapper = detect_wrappable_iterations(tree, deps_graph, mapper) # Global analysis for k, v in list(mapper.items()): args = k.args # SEQUENTIAL kills PARALLEL properties = [i for i in v if i != PARALLEL] if SEQUENTIAL in v else v properties = as_tuple(args.pop('properties')) + as_tuple(properties) mapper[k] = Iteration(properties=properties, **args) # Store the discovered properties in the Iteration/Expression tree processed = NestedTransformer(mapper).visit(nodes) return processed
def retrieve_iteration_tree(node, mode='normal'): """Return a list of all :class:`Iteration` sub-trees rooted in ``node``. For example, given the Iteration tree: .. code-block:: c Iteration i expr0 Iteration j Iteraion k expr1 Iteration p expr2 Return the list: :: [(Iteration i, Iteration j, Iteration k), (Iteration i, Iteration p)] :param node: The searched Iteration/Expression tree. :param mode: Accepted values are 'normal' (default) and 'superset', in which case iteration trees that are subset of larger iteration trees are dropped. """ assert mode in ('normal', 'superset') trees = [i for i in FindSections().visit(node) if i] if mode == 'normal': return trees else: match = [] for i in trees: if any(set(i).issubset(set(j)) for j in trees if i != j): continue match.append(i) return match
def test_find_sections(exprs, block1, block2, block3): finder = FindSections() sections = finder.visit(block1) assert len(sections) == 1 sections = finder.visit(block2) assert len(sections) == 2 found = list(sections.values()) assert len(found[0]) == 1 assert len(found[1]) == 1 sections = finder.visit(block3) assert len(sections) == 3 found = list(sections.values()) assert len(found[0]) == 1 assert len(found[1]) == 2 assert len(found[2]) == 1
def retrieve_iteration_tree(node, mode='normal'): """ A list with all :class:`Iteration` sub-trees within an IET. Examples -------- Given the Iteration tree: .. code-block:: c Iteration i expr0 Iteration j Iteraion k expr1 Iteration p expr2 Return the list: :: [(Iteration i, Iteration j, Iteration k), (Iteration i, Iteration p)] Parameters ---------- iet : Node The searched Iteration/Expression tree. mode : str, optional - ``normal`` - ``superset``: Iteration trees that are subset of larger iteration trees are dropped. """ assert mode in ('normal', 'superset') trees = [IterationTree(i) for i in FindSections().visit(node) if i] if mode == 'normal': return trees else: match = [] for i in trees: if any(set(i).issubset(set(j)) for j in trees if i != j): continue match.append(i) return IterationTree(match)
def create_profile(node): """ Create a :class:`Profiler` for the Iteration/Expression tree ``node``. The following code sections are profiled: :: * The whole ``node``; * A sequence of perfectly nested loops that have common :class:`Iteration` dimensions, but possibly different extent. For example: :: for x = 0 to N .. for x = 1 to N-1 .. Both Iterations have dimension ``x``, and will be profiled as a single section, though their extent is different. * Any perfectly nested loops. """ profiler = Profiler() # Group by root Iteration mapper = OrderedDict() for itspace in FindSections().visit(node): mapper.setdefault(itspace[0], []).append(itspace) # Group sections if their iteration spaces overlap key = lambda itspace: set([i.dim for i in itspace]) found = [] for v in mapper.values(): queue = list(v) handle = [] while queue: item = queue.pop(0) if not handle or key(item) == key(handle[0]): handle.append(item) else: # Found a timing section found.append(tuple(handle)) handle = [item] if handle: found.append(tuple(handle)) # Create and track C-level timers mapper = OrderedDict() for i, group in enumerate(found): name = 'section_%d' % i # We time at the single timestep level for i in zip(*group): root = i[0] remainder = tuple(j for j in i if j is not root) if not (root.dim.is_Time or root.dim.is_Stepping): break # Prepare to transform the Iteration/Expression tree body = (root, ) + remainder mapper[root] = TimedList(gname=profiler.varname, lname=name, body=body) mapper.update(OrderedDict([(j, None) for j in remainder])) # Estimate computational properties of the profiled section expressions = FindNodes(Expression).visit(body) ops = estimate_cost([e.expr for e in expressions]) memory = estimate_memory([e.expr for e in expressions]) # Keep track of the new profiled section profiler.add(name, group[0], ops, memory) # Transform the Iteration/Expression tree introducing the C-level timers processed = Transformer(mapper).visit(node) return processed, profiler