예제 #1
0
    def apply(self, node, inline=True):
        '''Checks that the node is of the correct type (a Kernel) then marks
        the Kernel to be inlined, or not, depending on the value of
        the inline argument. If the inline argument is not passed the
        Kernel is marked to be inlined.'''
        # check node is a kernel
        from psyGen import Kern
        if not isinstance(node, Kern):
            raise TransformationError(
                "Error in KernelModuleInline transformation. The node is not "
                "a Kernel")

        schedule = node.root

        # create a memento of the schedule and the proposed transformation
        from undoredo import Memento
        keep = Memento(schedule, self, [node])

        # set kernel's inline status
        if node.module_inline == inline:
            # issue a warning here when we implement logging
            # print "Warning, Kernel inline is already set to "+str(inline)
            pass
        else:
            node.module_inline = inline

        return schedule, keep
예제 #2
0
    def apply(self, node, location, position="before"):
        '''Move the node represented by :py:obj:`node` before location
        :py:obj:`location` (which as also a node) by default and after
        if the optional `position` argument is set to 'after'. An
        exception is raised if the move is invalid '''

        self._validate(node, location, position)

        schedule = node.root

        # create a memento of the schedule and the proposed transformation
        from undoredo import Memento
        keep = Memento(schedule, self, [node, location])

        parent = node.parent

        my_node = parent.children.pop(node.position)

        location_index = location.position
        if position == "before":
            schedule.children.insert(location_index, my_node)
        else:
            schedule.children.insert(location_index + 1, my_node)

        return schedule, keep
예제 #3
0
    def apply(self, node, const_bounds=True):

        # Check node is a Schedule
        from gocean1p0 import GOSchedule
        if not isinstance(node, GOSchedule):
            raise TransformationError("Error in GOConstLoopBoundsTrans: "
                                      "node is not a GOSchedule")

        from undoredo import Memento
        keep = Memento(node, self)

        node.const_loop_bounds = const_bounds

        return node, keep
예제 #4
0
    def apply(self, node):
        '''Converts the Loop represented by :py:obj:`node` into a
        nested loop where the outer loop is over colours and the inner
        loop is over cells of that colour.
        '''
        schedule = node.root

        # create a memento of the schedule and the proposed transformation
        from undoredo import Memento
        keep = Memento(schedule, self, [node])

        node_parent = node.parent
        node_position = node.position

        # create a colours loop. This loops over colours and must be run
        # sequentially
        colours_loop = node.__class__(parent=node_parent, loop_type="colours")
        colours_loop.field_space = node.field_space
        colours_loop.iteration_space = node.iteration_space
        colours_loop.set_lower_bound("start")
        colours_loop.set_upper_bound("ncolours")
        # Add this loop as a child of the original node's parent
        node_parent.addchild(colours_loop, index=node_position)

        # create a colour loop. This loops over a particular colour and
        # can be run in parallel
        colour_loop = node.__class__(parent=colours_loop, loop_type="colour")
        colour_loop.field_space = node.field_space
        colour_loop.iteration_space = node.iteration_space
        colour_loop.set_lower_bound("start")
        colour_loop.set_upper_bound("ncolour")
        # Add this loop as a child of our loop over colours
        colours_loop.addchild(colour_loop)

        # add contents of node to colour loop
        colour_loop.children.extend(node.children)

        # change the parent of the node's contents to the colour loop
        for child in node.children:
            child.parent = colour_loop

        # remove original loop
        node_parent.children.remove(node)

        return schedule, keep
예제 #5
0
    def apply(self, node):
        ''' Apply an OMPParallelLoop Transformation to the supplied node
        (which must be a Loop). In the generated code this corresponds to
        wrapping the Loop with directives:

        .. code-block:: fortran

          !$OMP PARALLEL DO ...
          do ...
            ...
          end do
          !$OMP END PARALLEL DO

        '''

        self._validate(node)

        schedule = node.root
        # create a memento of the schedule and the proposed transformation
        from undoredo import Memento
        keep = Memento(schedule, self, [node])

        # keep a reference to the node's original parent and its index as these
        # are required and will change when we change the node's location
        node_parent = node.parent
        node_position = node.position

        # add our OpenMP loop directive setting its parent to the node's
        # parent and its children to the node
        from psyGen import OMPParallelDoDirective
        directive = OMPParallelDoDirective(parent=node_parent,
                                           children=[node],
                                           omp_schedule=self.omp_schedule)

        # add the OpenMP loop directive as a child of the node's parent
        node_parent.addchild(directive, index=node_position)

        # change the node's parent to be the loop directive
        node.parent = directive

        # remove the original loop
        node_parent.children.remove(node)

        return schedule, keep
예제 #6
0
    def apply(self, node1, node2):
        ''' Fuse the loops represented by :py:obj:`node1` and
        :py:obj:`node2` '''

        self._validate(node1, node2)

        schedule = node1.root

        # create a memento of the schedule and the proposed transformation
        from undoredo import Memento
        keep = Memento(schedule, self, [node1, node2])

        # add loop contents of node2 to node1
        node1.children.extend(node2.children)

        # change the parent of the loop contents of node2 to node1
        for child in node2.children:
            child.parent = node1

        # remove node2
        node2.parent.children.remove(node2)

        return schedule, keep
예제 #7
0
    def apply(self, nodes):
        '''Apply this transformation to a subset of the nodes within a
            schedule - i.e. enclose the specified Loops in the
            schedule within a single OpenMP region. :py:obj:`nodes`
            can be a single Node or a list of Nodes.

        '''
        from psyGen import OMPDirective, OMPParallelDirective

        # Check whether we've been passed a list of nodes or just a
        # single node. If the latter then we create ourselves a
        # list containing just that node.
        from psyGen import Node
        if isinstance(nodes, list) and isinstance(nodes[0], Node):
            node_list = nodes
        elif isinstance(nodes, Node):
            node_list = [nodes]
        else:
            arg_type = str(type(nodes))
            raise TransformationError("Error in OMPParallel transformation. "
                                      "Argument must be a single Node in a "
                                      "schedule or a list of Nodes in a "
                                      "schedule but have been passed an "
                                      "object of type: {0}".format(arg_type))

        # temporary dynamo0.3-specific test for haloexchange calls
        # existing within a parallel region. As we are going to
        # support this in the future, see #526, it does not warrant
        # making a separate dynamo-specific class.
        from psyGen import HaloExchange
        for node in node_list:
            if isinstance(node, HaloExchange):
                raise TransformationError(
                    "A halo exchange within a parallel region is not "
                    "supported")

        # Keep a reference to the parent of the nodes that are to be
        # enclosed within a parallel region. Also keep the index of
        # the first child to be enclosed as that will become the
        # position of the new !$omp parallel directive.
        node_parent = node_list[0].parent
        node_position = node_list[0].position

        if node_list[0].ancestor(OMPDirective):
            raise TransformationError("Error in OMPParallel transformation:" +
                                      " cannot create an OpenMP PARALLEL " +
                                      "region within another OpenMP region.")
        for child in node_list:
            if child.parent is not node_parent:
                raise TransformationError(
                    "Error in OMPParallel transformation: supplied nodes "
                    "are not children of the same Schedule/parent.")

        # create a memento of the schedule and the proposed
        # transformation
        schedule = node_list[0].root

        from undoredo import Memento
        keep = Memento(schedule, self)

        # Create the OpenMP parallel directive as a child of the
        # parent of the nodes being enclosed and with those nodes
        # as its children.
        # We slice the nodes list in order to get a new list object
        # (although the actual items in the list are still those in the
        # original). If we don't do this then we get an infinite
        # recursion in the new schedule.
        directive = OMPParallelDirective(parent=node_parent,
                                         children=node_list[:])

        # Change all of the affected children so that they have
        # the OpenMP directive as their parent. Use a slice
        # of the list of nodes so that we're looping over a local
        # copy of the list. Otherwise things get confused when
        # we remove children from the list.
        for child in node_list[:]:
            # Remove child from the parent's list of children
            node_parent.children.remove(child)
            child.parent = directive

        # Add the OpenMP region directive as a child of the parent
        # of the nodes being enclosed and at the original location
        # of the first of these nodes
        node_parent.addchild(directive, index=node_position)

        return schedule, keep
예제 #8
0
    def apply(self, node, reprod=None):
        '''Apply the OMPLoopTrans transformation to the specified node in a
        Schedule. This node must be a Loop since this transformation
        corresponds to wrapping the generated code with directives like so:

        .. code-block:: fortran

          !$OMP DO
          do ...
             ...
          end do
          !$OMP END DO

        At code-generation time (when
        :py:meth:`OMPLoopTrans.gen_code` is called), this node must be
        within (i.e. a child of) an OpenMP PARALLEL region.

        The optional reprod argument will cause a reproducible
        reduction to be generated if it is set to True, otherwise the
        default, non-reproducible OpenMP reduction will used. Note,
        reproducible in this case means obtaining the same results
        with the same number of OpenMP threads, not for different
        numbers of OpenMP threads.

        '''

        if reprod is None:
            import config
            reprod = config.REPRODUCIBLE_REDUCTIONS

        # Check that the supplied node is a Loop
        from psyGen import Loop
        if not isinstance(node, Loop):
            raise TransformationError("Cannot apply an OpenMP Loop "
                                      "directive to something that is "
                                      "not a loop")
        # Check we are not a sequential loop
        if node.loop_type == 'colours':
            raise TransformationError("Error in " + self.name +
                                      " transformation. "
                                      "The target loop is over colours and "
                                      "must be computed serially.")

        schedule = node.root

        # create a memento of the schedule and the proposed
        # transformation
        from undoredo import Memento
        keep = Memento(schedule, self, [node])

        # keep a reference to the node's original parent and its index as these
        # are required and will change when we change the node's location
        node_parent = node.parent
        node_position = node.position

        # add our orphan OpenMP loop directive setting its parent to
        # the node's parent and its children to the node
        from psyGen import OMPDoDirective
        directive = OMPDoDirective(parent=node_parent,
                                   children=[node],
                                   omp_schedule=self.omp_schedule,
                                   reprod=reprod)

        # add the OpenMP loop directive as a child of the node's parent
        node_parent.addchild(directive, index=node_position)

        # change the node's parent to be the loop directive
        node.parent = directive

        # remove the original loop
        node_parent.children.remove(node)

        return schedule, keep