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
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
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
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
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
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
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
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