コード例 #1
0
def test_intersects_symbolic():
    N, M = dace.symbol('N', positive=True), dace.symbol('M', positive=True)
    rng1 = subsets.Range([(0, N - 1, 1), (0, M - 1, 1)])
    rng2 = subsets.Range([(0, 0, 1), (0, 0, 1)])
    rng3_1 = subsets.Range([(N, N, 1), (0, 1, 1)])
    rng3_2 = subsets.Range([(0, 1, 1), (M, M, 1)])
    rng4 = subsets.Range([(N, N, 1), (M, M, 1)])
    rng5 = subsets.Range([(0, 0, 1), (M, M, 1)])
    rng6 = subsets.Range([(0, N, 1), (0, M, 1)])
    rng7 = subsets.Range([(0, N - 1, 1), (N - 1, N, 1)])
    ind1 = subsets.Indices([0, 1])

    assert subsets.intersects(rng1, rng2) is True
    assert subsets.intersects(rng1, rng3_1) is False
    assert subsets.intersects(rng1, rng3_2) is False
    assert subsets.intersects(rng1, rng4) is False
    assert subsets.intersects(rng1, rng5) is False
    assert subsets.intersects(rng6, rng1) is True
    assert subsets.intersects(rng1, rng7) is None
    assert subsets.intersects(rng7, rng1) is None
    assert subsets.intersects(rng1, ind1) is None
    assert subsets.intersects(ind1, rng1) is None
コード例 #2
0
    def memlets_intersect(graph_a: SDFGState, group_a: List[nodes.AccessNode],
                          inputs_a: bool, graph_b: SDFGState,
                          group_b: List[nodes.AccessNode],
                          inputs_b: bool) -> bool:
        """
        Performs an all-pairs check for subset intersection on two
        groups of nodes. If group intersects or result is indeterminate,
        returns True as a precaution.
        :param graph_a: The graph in which the first set of nodes reside.
        :param group_a: The first set of nodes to check.
        :param inputs_a: If True, checks inputs of the first group.
        :param graph_b: The graph in which the second set of nodes reside.
        :param group_b: The second set of nodes to check.
        :param inputs_b: If True, checks inputs of the second group.
        :returns True if subsets intersect or result is indeterminate.
        """
        # Set traversal functions
        src_subset = lambda e: (e.data.src_subset if e.data.src_subset is
                                not None else e.data.dst_subset)
        dst_subset = lambda e: (e.data.dst_subset if e.data.dst_subset is
                                not None else e.data.src_subset)
        if inputs_a:
            edges_a = [e for n in group_a for e in graph_a.out_edges(n)]
            subset_a = src_subset
        else:
            edges_a = [e for n in group_a for e in graph_a.in_edges(n)]
            subset_a = dst_subset
        if inputs_b:
            edges_b = [e for n in group_b for e in graph_b.out_edges(n)]
            subset_b = src_subset
        else:
            edges_b = [e for n in group_b for e in graph_b.in_edges(n)]
            subset_b = dst_subset

        # Simple all-pairs check
        for ea in edges_a:
            for eb in edges_b:
                result = subsets.intersects(subset_a(ea), subset_b(eb))
                if result is True or result is None:
                    return True
        return False
コード例 #3
0
def test_intersects_constant():
    rng1 = subsets.Range([(0, 4, 1)])
    rng2 = subsets.Range([(3, 4, 1)])
    rng3 = subsets.Range([(1, 5, 1)])
    rng4 = subsets.Range([(5, 7, 1)])
    ind1 = subsets.Indices([0])
    ind2 = subsets.Indices([1])
    ind3 = subsets.Indices([5])

    assert subsets.intersects(rng1, rng2) is True
    assert subsets.intersects(rng1, rng3) is True
    assert subsets.intersects(rng1, rng4) is False
    assert subsets.intersects(ind1, rng1) is True
    assert subsets.intersects(rng1, ind2) is True
    assert subsets.intersects(rng1, ind3) is False
コード例 #4
0
ファイル: loop_to_map.py プロジェクト: thobauma/dace
    def can_be_applied(self, graph, candidate, expr_index, sdfg, strict=False):
        # Is this even a loop
        if not DetectLoop.can_be_applied(graph, candidate, expr_index, sdfg,
                                         strict):
            return False

        guard = graph.node(candidate[DetectLoop._loop_guard])
        begin = graph.node(candidate[DetectLoop._loop_begin])

        # Guard state should not contain any dataflow
        if len(guard.nodes()) != 0:
            return False

        # If loop cannot be detected, fail
        found = find_for_loop(graph, guard, begin,
                              itervar=self.itervar)
        if not found:
            return False

        itervar, (start, end, step), (_, body_end) = found

        # We cannot handle symbols read from data containers unless they are
        # scalar
        for expr in (start, end, step):
            if symbolic.contains_sympy_functions(expr):
                return False

        # Find all loop-body states
        states = set([body_end])
        to_visit = [begin]
        while to_visit:
            state = to_visit.pop(0)
            if state is body_end:
                continue
            for _, dst, _ in graph.out_edges(state):
                if dst not in states:
                    to_visit.append(dst)
            states.add(state)

        write_set = set()
        for state in states:
            _, wset = state.read_and_write_sets()
            write_set |= wset

        # Get access nodes from other states to isolate local loop variables
        other_access_nodes = set()
        for state in sdfg.nodes():
            if state in states:
                continue
            other_access_nodes |= set(n.data for n in state.data_nodes()
                                      if sdfg.arrays[n.data].transient)
        # Add non-transient nodes from loop state
        for state in states:
            other_access_nodes |= set(n.data for n in state.data_nodes()
                                      if not sdfg.arrays[n.data].transient)

        write_memlets = defaultdict(list)

        itersym = symbolic.pystr_to_symbolic(itervar)
        a = sp.Wild('a', exclude=[itersym])
        b = sp.Wild('b', exclude=[itersym])

        for state in states:
            for dn in state.data_nodes():
                if dn.data not in other_access_nodes:
                    continue
                # Take all writes that are not conflicted into consideration
                if dn.data in write_set:
                    for e in state.in_edges(dn):
                        if e.data.dynamic and e.data.wcr is None:
                            # If pointers are involved, give up
                            return False
                        # To be sure that the value is only written at unique
                        # indices per loop iteration, we want to match symbols
                        # of the form "a*i+b" where a >= 1, and i is the iteration
                        # variable. The iteration variable must be used.
                        if e.data.wcr is None:
                            dst_subset = e.data.get_dst_subset(e, state)
                            if not _check_range(dst_subset, a, itersym, b, step):
                                return False
                        # End of check

                        write_memlets[dn.data].append(e.data)

        # After looping over relevant writes, consider reads that may overlap
        for state in states:
            for dn in state.data_nodes():
                if dn.data not in other_access_nodes:
                    continue
                data = dn.data
                if data in write_memlets:
                    # Import as necessary
                    from dace.sdfg.propagation import propagate_subset

                    for e in state.out_edges(dn):
                        # If the same container is both read and written, only match if
                        # it read and written at locations that will not create data races
                        if e.data.dynamic and e.data.src_subset.num_elements() != 1:
                            # If pointers are involved, give up
                            return False
                        src_subset = e.data.get_src_subset(e, state)
                        if not _check_range(src_subset, a, itersym, b, step):
                            return False

                        pread = propagate_subset([e.data], sdfg.arrays[data],
                                                [itervar],
                                                subsets.Range([(start, end, step)
                                                                ]))
                        for candidate in write_memlets[data]:
                            # Simple case: read and write are in the same subset
                            if e.data.subset == candidate.subset:
                                break
                            # Propagated read does not overlap with propagated write
                            pwrite = propagate_subset([candidate],
                                                    sdfg.arrays[data], [itervar],
                                                    subsets.Range([(start, end,
                                                                    step)]))
                            if subsets.intersects(pread.subset,
                                                pwrite.subset) is False:
                                break
                            return False

        # Check that the iteration variable is not used on other edges or states
        # before it is reassigned
        prior_states = True
        for state in cfg.stateorder_topological_sort(sdfg):
            # Skip all states up to guard
            if prior_states:
                if state is begin:
                    prior_states = False
                continue
            # We do not need to check the loop-body states
            if state in states:
                continue
            if itervar in state.free_symbols:
                return False
            # Don't continue in this direction, as the variable has
            # now been reassigned
            # TODO: Handle case of subset of out_edges
            if all(itervar in e.data.assignments
                   for e in sdfg.out_edges(state)):
                break

        return True
コード例 #5
0
ファイル: redundant_array.py プロジェクト: thobauma/dace
    def can_be_applied(graph, candidate, expr_index, sdfg, strict=False):
        in_array = graph.nodes()[candidate[RedundantSecondArray._in_array]]
        out_array = graph.nodes()[candidate[RedundantSecondArray._out_array]]

        in_desc = in_array.desc(sdfg)
        out_desc = out_array.desc(sdfg)

        # Ensure in degree is one (only one source, which is in_array)
        if graph.in_degree(out_array) != 1:
            return False

        # Make sure that the candidate is a transient variable
        if not out_desc.transient:
            return False

        # 1. Get edge e1 and extract/validate subsets for arrays A and B
        e1 = graph.edges_between(in_array, out_array)[0]
        a_subset, b1_subset = _validate_subsets(e1, sdfg.arrays)

        if strict:
            # In strict mode, make sure the memlet covers the removed array
            if not b1_subset:
                return False
            subset = copy.deepcopy(b1_subset)
            subset.squeeze()
            shape = [sz for sz in out_desc.shape if sz != 1]
            if any(m != a for m, a in zip(subset.size(), shape)):
                return False

            # NOTE: Library node check
            # The transformation must not apply in strict mode if out_array is
            # not a view, is input to a library node, and an access or a view
            # of in_desc is also output to the same library node.
            # The reason is that the application of the transformation will lead
            # to in_desc being both input and output of the library node.
            # We do not know if this is safe.

            # First find the true in_desc (in case in_array is a view).
            true_in_desc = in_desc
            if isinstance(in_desc, data.View):
                e = sdutil.get_view_edge(graph, in_array)
                if not e:
                    return False
                true_in_desc = sdfg.arrays[e.dst.data]

            if not isinstance(out_desc, data.View):

                edges_to_check = []
                for a in graph.out_edges(out_array):
                    if isinstance(a.dst, nodes.LibraryNode):
                        edges_to_check.append(a)
                    elif (isinstance(a.dst, nodes.AccessNode)
                          and isinstance(sdfg.arrays[a.dst.data], data.View)):
                        for b in graph.out_edges(a.dst):
                            edges_to_check.append(graph.memlet_path(b)[-1])

                for a in edges_to_check:
                    if isinstance(a.dst, nodes.LibraryNode):
                        for b in graph.out_edges(a.dst):
                            if isinstance(b.dst, nodes.AccessNode):
                                desc = sdfg.arrays[b.dst.data]
                                if isinstance(desc, data.View):
                                    e = sdutil.get_view_edge(graph, b.dst)
                                    if not e:
                                        return False
                                    desc = sdfg.arrays[e.dst.data]
                                    if desc is true_in_desc:
                                        return False

            # In strict mode, check if the state has two or more access nodes
            # for in_array and at least one of them is a write access. There
            # might be a RW, WR, or WW dependency.
            accesses = [
                n for n in graph.nodes() if isinstance(n, nodes.AccessNode)
                and n.desc(sdfg) == in_desc and n is not in_array
            ]
            if len(accesses) > 0:
                if (graph.in_degree(in_array) > 0
                        or any(graph.in_degree(a) > 0 for a in accesses)):
                    # We need to ensure that a data race will not happen if we
                    # remove in_array.
                    # First, we simplify the graph
                    G = helpers.simplify_state(graph)
                    # Loop over the accesses
                    for a in accesses:
                        subsets_intersect = False
                        for e in graph.in_edges(a):
                            _, subset = _validate_subsets(e,
                                                          sdfg.arrays,
                                                          dst_name=a.data)
                            res = subsets.intersects(a_subset, subset)
                            if res == True or res is None:
                                subsets_intersect = True
                                break
                        if not subsets_intersect:
                            continue
                        try:
                            has_bward_path = nx.has_path(G, a, in_array)
                        except NodeNotFound:
                            has_bward_path = nx.has_path(graph.nx, a, in_array)
                        try:
                            has_fward_path = nx.has_path(G, in_array, a)
                        except NodeNotFound:
                            has_fward_path = nx.has_path(graph.nx, in_array, a)
                        # If there is no path between the access nodes
                        # (disconnected components), then it is definitely
                        # possible to have data races. Abort.
                        if not (has_bward_path or has_fward_path):
                            return False
                        # If there is a forward path then a must not be a direct
                        # successor of in_array.
                        if has_fward_path and a in G.successors(in_array):
                            for src, _ in G.in_edges(a):
                                if src is in_array:
                                    continue
                                if (nx.has_path(G, in_array, src)
                                        and src != out_array):
                                    continue
                                return False

        # Make sure that both arrays are using the same storage location
        # and are of the same type (e.g., Stream->Stream)
        if in_desc.storage != out_desc.storage:
            return False
        if in_desc.location != out_desc.location:
            return False
        if type(in_desc) != type(out_desc):
            if isinstance(in_desc, data.View):
                # Case View -> Access
                # If the View points to the Access (and has a different shape?)
                # then we should (probably) not remove the Access.
                e = sdutil.get_view_edge(graph, in_array)
                if e and e.dst is out_array and in_desc.shape != out_desc.shape:
                    return False
                # Check that the View's immediate ancestors are Accesses.
                # Otherwise, the application of the transformation will result
                # in an ambiguous View.
                view_ancestors_desc = [
                    e.src.desc(sdfg)
                    if isinstance(e.src, nodes.AccessNode) else None
                    for e in graph.in_edges(in_array)
                ]
                if any([
                        not desc or isinstance(desc, data.View)
                        for desc in view_ancestors_desc
                ]):
                    return False
            elif isinstance(out_desc, data.View):
                # Case Access -> View
                # If the View points to the Access and has the same shape,
                # it can be removed
                e = sdutil.get_view_edge(graph, out_array)
                if e and e.src is in_array and in_desc.shape == out_desc.shape:
                    return True
                return False
            else:
                # Something else, for example, Stream
                return False
        else:
            # Two views connected to each other
            if isinstance(in_desc, data.View):
                return False

        # Find occurrences in this and other states
        occurrences = []
        for state in sdfg.nodes():
            occurrences.extend([
                n for n in state.nodes()
                if isinstance(n, nodes.AccessNode) and n.desc(sdfg) == out_desc
            ])
        for isedge in sdfg.edges():
            if out_array.data in isedge.data.free_symbols:
                occurrences.append(isedge)

        if len(occurrences) > 1:
            return False

        # Check whether the data copied from the first datanode cover
        # the subsets of all the output edges of the second datanode.
        # We assume the following pattern: A -- e1 --> B -- e2 --> others

        # 2. Iterate over the e2 edges
        for e2 in graph.out_edges(out_array):
            # 2-a. Extract/validate subsets for array B and others
            try:
                b2_subset, _ = _validate_subsets(e2, sdfg.arrays)
            except NotImplementedError:
                return False
            # 2-b. Check where b1_subset covers b2_subset
            if not b1_subset.covers(b2_subset):
                return False
            # 2-c. Validate subsets in memlet tree
            # (should not be needed for valid SDGs)
            path = graph.memlet_tree(e2)
            for e3 in path:
                if e3 is not e2:
                    try:
                        _validate_subsets(e3,
                                          sdfg.arrays,
                                          src_name=out_array.data)
                    except NotImplementedError:
                        return False

        return True
コード例 #6
0
ファイル: cpp.py プロジェクト: gibchikafa/dace
def is_write_conflicted_with_reason(dfg,
                                    edge,
                                    datanode=None,
                                    sdfg_schedule=None):
    """
    Detects whether a write-conflict-resolving edge can be emitted without
    using atomics or critical sections, returning the node or SDFG that caused
    the decision.
    :return: None if the conflict is nonatomic, otherwise returns the scope entry
             node or SDFG that caused the decision to be made.
    """

    if edge.data.wcr_nonatomic or edge.data.wcr is None:
        return None

    # If it's an entire SDFG, it's probably write-conflicted
    if isinstance(dfg, SDFG):
        if datanode is None:
            return dfg
        in_edges = find_incoming_edges(datanode, dfg)
        if len(in_edges) != 1:
            return dfg
        if (isinstance(in_edges[0].src, nodes.ExitNode)
                and in_edges[0].src.map.schedule
                == dtypes.ScheduleType.Sequential):
            return None
        return dfg
    elif isinstance(dfg, gr.SubgraphView):
        dfg = dfg.graph

    # Traverse memlet path to determine conflicts.
    # If no conflicts will occur, write without atomics
    # (e.g., if the array has been defined in a non-parallel schedule context)
    while edge is not None:
        path = dfg.memlet_path(edge)
        for e in path:
            if (isinstance(e.dst, nodes.ExitNode)
                    and e.dst.map.schedule != dtypes.ScheduleType.Sequential):
                if _check_map_conflicts(e.dst.map, e):
                    # This map is parallel w.r.t. WCR
                    # print('PAR: Continuing from map')
                    continue
                # print('SEQ: Map is conflicted')
                return dfg.entry_node(e.dst)
            # Should never happen (no such thing as write-conflicting reads)
            if (isinstance(e.src, nodes.EntryNode)
                    and e.src.map.schedule != dtypes.ScheduleType.Sequential):
                warnings.warn(
                    'Unexpected WCR path to have write-conflicting reads')
                return e.src

        sdfg = dfg.parent
        dst = path[-1].dst
        # Unexpected case
        if not isinstance(dst, nodes.AccessNode):
            warnings.warn('Unexpected WCR path to not end in access node')
            return dst

        if dfg.in_degree(dst) > 0:
            for x, y in itertools.combinations(dfg.in_edges(dst), 2):
                x, y = x.data.subset, y.data.subset
                if subsets.intersects(x, y):
                    return dst

        # If this is a nested SDFG and the access leads outside
        if not sdfg.arrays[dst.data].transient:
            if sdfg.parent_nsdfg_node is not None:
                dfg = sdfg.parent
                nsdfg = sdfg.parent_nsdfg_node
                edge = next(iter(dfg.out_edges_by_connector(nsdfg, dst.data)))
            else:
                break
        else:
            # Memlet path ends here, transient. We can thus safely write here
            edge = None
            # print('PAR: Reached transient')
            return None

    return None