kerncraft/kernel.py
def compile_global_offsets(self, iteration=0, spacing=0):
        """
        Return load and store offsets on a virtual address space.

        :param iteration: controls the inner index counter
        :param spacing: sets a spacing between the arrays, default is 0

        All array variables (non scalars) are laid out linearly starting from 0. An optional
        spacing can be set. The accesses are based on this layout.

        The iteration 0 is the first iteration. All loops are mapped to this linear iteration
        space.

        Accesses to scalars are ignored.

        Returned are load and store byte-offset pairs for each iteration.
        """
        global_load_offsets = []
        global_store_offsets = []

        if isinstance(iteration, range):
            iteration = numpy.arange(iteration.start, iteration.stop, iteration.step, dtype='O')
        else:
            if not isinstance(iteration, collections.Sequence):
                iteration = [iteration]
            iteration = numpy.array(iteration, dtype='O')

        # loop indices based on iteration
        # unwind global iteration count into loop counters:
        base_loop_counters = self.global_iterator_to_indices()
        total_length = self.iteration_length()

        assert iteration.max() < self.subs_consts(total_length), \
            "Iterations go beyond what is possible in the original code ({} vs {}). " \
            "One common reason, is that the iteration length are unrealistically small.".format(
                iteration.max(), self.subs_consts(total_length))

        # Get sizes of arrays and base offsets for each array
        var_sizes = self.array_sizes(in_bytes=True, subs_consts=True)
        base_offsets = {}
        base = 0
        # Always arrange arrays in alphabetical order in memory, for reproducibility
        for var_name, var_size in sorted(var_sizes.items(), key=lambda v: v[0]):
            base_offsets[var_name] = base
            array_total_size = self.subs_consts(var_size + spacing)
            # Add bytes to align by 64 byte (typical cacheline size):
            array_total_size = ((int(array_total_size) + 63) & ~63)
            base += array_total_size

        # Gather all read and write accesses to the array:
        for var_name, var_size in var_sizes.items():
            element_size = self.datatypes_size[self.variables[var_name][0]]
            for r in self.sources.get(var_name, []):
                offset_expr = self.access_to_sympy(var_name, r)
                # Ignore accesses that always go to the same location (constant offsets)
                if not any([s in base_loop_counters.keys() for s in offset_expr.free_symbols]):
                    continue
                offset = force_iterable(sympy.lambdify(
                    base_loop_counters.keys(),
                    self.subs_consts(
                        offset_expr*element_size
                        + base_offsets[var_name]), numpy))
                # TODO possibly differentiate between index order
                global_load_offsets.append(offset)
            for w in self.destinations.get(var_name, []):
                offset_expr = self.access_to_sympy(var_name, w)
                # Ignore accesses that always go to the same location (constant offsets)
                if not any([s in base_loop_counters.keys() for s in offset_expr.free_symbols]):
                    continue
                offset = force_iterable(sympy.lambdify(
                    base_loop_counters.keys(),
                    self.subs_consts(
                        offset_expr*element_size
                        + base_offsets[var_name]), numpy))
                # TODO possibly differentiate between index order
                global_store_offsets.append(offset)
                # TODO take element sizes into account, return in bytes

        # Generate numpy.array for each counter
        counter_per_it = [v(iteration) for v in base_loop_counters.values()]

        # Old and slow - left for reference
        ## Data access as they appear with iteration order
        #return zip_longest(zip(*[o(*counter_per_it) for o in global_load_offsets]),
        #                   zip(*[o(*counter_per_it) for o in global_store_offsets]),
        #                   fillvalue=None)

        # Data access as they appear with iteration order
        load_offsets = []
        for o in global_load_offsets:
            load_offsets.append(o(*counter_per_it))
        # Convert to numpy ndarray and transpose to get offsets per iterations
        load_offsets = numpy.asarray(load_offsets).T

        store_offsets = []
        for o in global_store_offsets:
            store_offsets.append(o(*counter_per_it))
        store_offsets = numpy.asarray(store_offsets).T

        # Combine loads and stores
        store_width = store_offsets.shape[1] if len(store_offsets.shape) > 1 else 0
        dtype = [('load', load_offsets.dtype, (load_offsets.shape[1],)),
                 ('store', store_offsets.dtype, (store_width,))]
        offsets = numpy.empty(max(load_offsets.shape[0], store_offsets.shape[0]), dtype=dtype)
        offsets['load'] = load_offsets
        offsets['store'] = store_offsets

        return offsets
def compile_global_offsets(self, iteration=0, spacing=0):
        """
        Return load and store offsets on a virtual address space.

        :param iteration: controls the inner index counter
        :param spacing: sets a spacing between the arrays, default is 0

        All array variables (non scalars) are laid out linearly starting from 0. An optional
        spacing can be set. The accesses are based on this layout.

        The iteration 0 is the first iteration. All loops are mapped to this linear iteration
        space.

        Accesses to scalars are ignored.

        Returned are load and store byte-offset pairs for each iteration.
        """
        global_load_offsets = []
        global_store_offsets = []

        if isinstance(iteration, range):
            iteration = numpy.arange(iteration.start, iteration.stop, iteration.step, dtype='O')
        else:
            if not isinstance(iteration, collections.Sequence):
                iteration = [iteration]
            iteration = numpy.array(iteration, dtype='O')

        # loop indices based on iteration
        # unwind global iteration count into loop counters:
        base_loop_counters = self.global_iterator_to_indices()
        total_length = self.iteration_length()

        assert iteration.max() < self.subs_consts(total_length), \
            "Iterations go beyond what is possible in the original code ({} vs {}). " \
            "One common reason, is that the iteration length are unrealistically small.".format(
                iteration.max(), self.subs_consts(total_length))

        # Get sizes of arrays and base offsets for each array
        var_sizes = self.array_sizes(in_bytes=True, subs_consts=True)
        base_offsets = {}
        base = 0
        # Always arrange arrays in alphabetical order in memory, for reproducibility
        for var_name, var_size in sorted(var_sizes.items(), key=lambda v: v[0]):
            base_offsets[var_name] = base
            array_total_size = self.subs_consts(var_size + spacing)
            # Add bytes to align by 64 byte (typical cacheline size):
            array_total_size = ((int(array_total_size) + 63) & ~63)
            base += array_total_size

        # Gather all read and write accesses to the array:
        for var_name, var_size in var_sizes.items():
            element_size = self.datatypes_size[self.variables[var_name][0]]
            for r in self.sources.get(var_name, []):
                offset_expr = self.access_to_sympy(var_name, r)
                # Ignore accesses that always go to the same location (constant offsets)
                if not any([s in base_loop_counters.keys() for s in offset_expr.free_symbols]):
                    continue
                offset = force_iterable(sympy.lambdify(
                    base_loop_counters.keys(),
                    self.subs_consts(
                        offset_expr*element_size
                        + base_offsets[var_name]), numpy))
                # TODO possibly differentiate between index order
                global_load_offsets.append(offset)
            for w in self.destinations.get(var_name, []):
                offset_expr = self.access_to_sympy(var_name, w)
                # Ignore accesses that always go to the same location (constant offsets)
                if not any([s in base_loop_counters.keys() for s in offset_expr.free_symbols]):
                    continue
                offset = force_iterable(sympy.lambdify(
                    base_loop_counters.keys(),
                    self.subs_consts(
                        offset_expr*element_size
                        + base_offsets[var_name]), numpy))
                # TODO possibly differentiate between index order
                global_store_offsets.append(offset)
                # TODO take element sizes into account, return in bytes

        # Generate numpy.array for each counter
        counter_per_it = [v(iteration) for v in base_loop_counters.values()]

        # Old and slow - left for reference
        ## Data access as they appear with iteration order
        #return zip_longest(zip(*[o(*counter_per_it) for o in global_load_offsets]),
        #                   zip(*[o(*counter_per_it) for o in global_store_offsets]),
        #                   fillvalue=None)

        # Data access as they appear with iteration order
        load_offsets = []
        for o in global_load_offsets:
            load_offsets.append(o(*counter_per_it))
        # Convert to numpy ndarray and transpose to get offsets per iterations
        load_offsets = numpy.asarray(load_offsets).T

        store_offsets = []
        for o in global_store_offsets:
            store_offsets.append(o(*counter_per_it))
        store_offsets = numpy.asarray(store_offsets).T

        # Combine loads and stores
        store_width = store_offsets.shape[1] if len(store_offsets.shape) > 1 else 0
        dtype = [('load', load_offsets.dtype, (load_offsets.shape[1],)),
                 ('store', store_offsets.dtype, (store_width,))]
        offsets = numpy.empty(max(load_offsets.shape[0], store_offsets.shape[0]), dtype=dtype)
        offsets['load'] = load_offsets
        offsets['store'] = store_offsets

        return offsets
def compile_global_offsets ( self , iteration = 0 , spacing = 0 ) : global_load_offsets = [ ] global_store_offsets = [ ] if isinstance ( iteration , range ) : iteration = numpy . arange ( iteration . start , iteration . stop , iteration . step , dtype = 'O' ) else : if not isinstance ( iteration , collections . Sequence ) : iteration = [ iteration ] iteration = numpy . array ( iteration , dtype = 'O' ) # loop indices based on iteration # unwind global iteration count into loop counters: base_loop_counters = self . global_iterator_to_indices ( ) total_length = self . iteration_length ( ) assert iteration . max ( ) < self . subs_consts ( total_length ) , "Iterations go beyond what is possible in the original code ({} vs {}). " "One common reason, is that the iteration length are unrealistically small." . format ( iteration . max ( ) , self . subs_consts ( total_length ) ) # Get sizes of arrays and base offsets for each array var_sizes = self . array_sizes ( in_bytes = True , subs_consts = True ) base_offsets = { } base = 0 # Always arrange arrays in alphabetical order in memory, for reproducibility for var_name , var_size in sorted ( var_sizes . items ( ) , key = lambda v : v [ 0 ] ) : base_offsets [ var_name ] = base array_total_size = self . subs_consts ( var_size + spacing ) # Add bytes to align by 64 byte (typical cacheline size): array_total_size = ( ( int ( array_total_size ) + 63 ) & ~ 63 ) base += array_total_size # Gather all read and write accesses to the array: for var_name , var_size in var_sizes . items ( ) : element_size = self . datatypes_size [ self . variables [ var_name ] [ 0 ] ] for r in self . sources . get ( var_name , [ ] ) : offset_expr = self . access_to_sympy ( var_name , r ) # Ignore accesses that always go to the same location (constant offsets) if not any ( [ s in base_loop_counters . keys ( ) for s in offset_expr . free_symbols ] ) : continue offset = force_iterable ( sympy . lambdify ( base_loop_counters . keys ( ) , self . subs_consts ( offset_expr * element_size + base_offsets [ var_name ] ) , numpy ) ) # TODO possibly differentiate between index order global_load_offsets . append ( offset ) for w in self . destinations . get ( var_name , [ ] ) : offset_expr = self . access_to_sympy ( var_name , w ) # Ignore accesses that always go to the same location (constant offsets) if not any ( [ s in base_loop_counters . keys ( ) for s in offset_expr . free_symbols ] ) : continue offset = force_iterable ( sympy . lambdify ( base_loop_counters . keys ( ) , self . subs_consts ( offset_expr * element_size + base_offsets [ var_name ] ) , numpy ) ) # TODO possibly differentiate between index order global_store_offsets . append ( offset ) # TODO take element sizes into account, return in bytes # Generate numpy.array for each counter counter_per_it = [ v ( iteration ) for v in base_loop_counters . values ( ) ] # Old and slow - left for reference ## Data access as they appear with iteration order #return zip_longest(zip(*[o(*counter_per_it) for o in global_load_offsets]), #                   zip(*[o(*counter_per_it) for o in global_store_offsets]), #                   fillvalue=None) # Data access as they appear with iteration order load_offsets = [ ] for o in global_load_offsets : load_offsets . append ( o ( * counter_per_it ) ) # Convert to numpy ndarray and transpose to get offsets per iterations load_offsets = numpy . asarray ( load_offsets ) . T store_offsets = [ ] for o in global_store_offsets : store_offsets . append ( o ( * counter_per_it ) ) store_offsets = numpy . asarray ( store_offsets ) . T # Combine loads and stores store_width = store_offsets . shape [ 1 ] if len ( store_offsets . shape ) > 1 else 0 dtype = [ ( 'load' , load_offsets . dtype , ( load_offsets . shape [ 1 ] , ) ) , ( 'store' , store_offsets . dtype , ( store_width , ) ) ] offsets = numpy . empty ( max ( load_offsets . shape [ 0 ] , store_offsets . shape [ 0 ] ) , dtype = dtype ) offsets [ 'load' ] = load_offsets offsets [ 'store' ] = store_offsets return offsets
Return load and store offsets on a virtual address space.

        :param iteration: controls the inner index counter
        :param spacing: sets a spacing between the arrays, default is 0

        All array variables (non scalars) are laid out linearly starting from 0. An optional
        spacing can be set. The accesses are based on this layout.

        The iteration 0 is the first iteration. All loops are mapped to this linear iteration
        space.

        Accesses to scalars are ignored.

        Returned are load and store byte-offset pairs for each iteration.
Return load and store offsets on a virtual address space .