def __init__(self, name, yc_soln, local_grids=None): """ Write out a YASK kernel, build it using YASK's Makefiles, import the corresponding SWIG-generated Python module, and finally create a YASK kernel solution object. :param name: Unique name of this YaskKernel. :param yc_soln: YaskCompiler solution. :param local_grids: A local grid is necessary to run the YaskKernel, but its final content can be ditched. Indeed, local grids are hidden to users -- for example, they could represent temporary arrays introduced by the DSE. This parameter tells which of the ``yc_soln``'s grids are local. """ self.name = name # Shared object name self.soname = "%s.%s.%s" % (name, yc_soln.get_name(), configuration['platform']) # It's necessary to `clean` the YASK kernel directory *before* # writing out the first `yask_stencil_code.hpp` make(namespace['path'], ['-C', namespace['kernel-path'], 'clean']) # Write out the stencil file if not os.path.exists(namespace['kernel-path-gen']): os.makedirs(namespace['kernel-path-gen']) yc_soln.format(configuration['isa'], ofac.new_file_output(namespace['kernel-output'])) # JIT-compile it try: compiler = configuration.yask['compiler'] opt_level = 1 if configuration.yask['develop-mode'] else 3 make( namespace['path'], [ '-j3', 'YK_CXX=%s' % compiler.cc, 'YK_CXXOPT=-O%d' % opt_level, 'mpi=0', # Disable MPI for now # "EXTRA_MACROS=TRACE", 'YK_BASE=%s' % str(name), 'stencil=%s' % yc_soln.get_name(), 'arch=%s' % configuration['platform'], '-C', namespace['kernel-path'], 'api' ]) except CompilationError: exit("Kernel solution compilation") # Import the corresponding Python (SWIG-generated) module try: yk = getattr(__import__('yask', fromlist=[name]), name) except ImportError: exit("Python YASK kernel bindings") try: yk = reload(yk) except NameError: # Python 3.5 compatibility yk = importlib.reload(yk) # Create the YASK solution object kfac = yk.yk_factory() self.env = kfac.new_env() self.soln = kfac.new_solution(self.env) # MPI setup: simple rank configuration in 1st dim only. # TODO: in production runs, the ranks would be distributed along all # domain dimensions. self.soln.set_num_ranks(self.space_dimensions[0], self.env.get_num_ranks()) # Redirect stdout/strerr to a string or file if configuration.yask['dump']: filename = 'yk_dump.%s.%s.%s.txt' % ( self.name, configuration['platform'], configuration['isa']) filename = os.path.join(configuration.yask['dump'], filename) self.output = yk.yask_output_factory().new_file_output(filename) else: self.output = yk.yask_output_factory().new_string_output() self.soln.set_debug_output(self.output) # Users may want to run the same Operator (same domain etc.) with # different grids. self.grids = {i.get_name(): i for i in self.soln.get_grids()} self.local_grids = { i.name: self.grids[i.name] for i in (local_grids or []) }
def __init__(self, name, yc_soln, domain): """ Write out a YASK kernel, build it using YASK's Makefiles, import the corresponding SWIG-generated Python module, and finally create a YASK kernel solution object. :param name: Unique name of this YaskKernel. :param yc_soln: YaskCompiler solution. :param domain: A mapper from space dimensions to their domain size. """ self.name = name # Shared object name self.soname = "%s.%s.%s" % (name, yc_soln.get_name(), yask_configuration['arch']) # It's necessary to `clean` the YASK kernel directory *before* # writing out the first `yask_stencil_code.hpp` make(namespace['path'], ['-C', namespace['kernel-path'], 'clean']) # Write out the stencil file if not os.path.exists(namespace['kernel-path-gen']): os.makedirs(namespace['kernel-path-gen']) yc_soln.format(yask_configuration['isa'], ofac.new_file_output(namespace['kernel-output'])) # JIT-compile it try: opt_level = 1 if yask_configuration['develop-mode'] else 3 make( os.environ['YASK_HOME'], [ '-j', 'YK_CXXOPT=-O%d' % opt_level, # "EXTRA_MACROS=TRACE", 'YK_BASE=%s' % str(name), 'stencil=%s' % yc_soln.get_name(), 'arch=%s' % yask_configuration['arch'], '-C', namespace['kernel-path'], 'api' ]) except CompilationError: exit("Kernel solution compilation") # Import the corresponding Python (SWIG-generated) module try: yk = importlib.import_module(name) except ImportError: exit("Python YASK kernel bindings") try: yk = reload(yk) except NameError: # Python 3.5 compatibility yk = importlib.reload(yk) # Create the YASK solution object kfac = yk.yk_factory() self.env = kfac.new_env() self.soln = kfac.new_solution(self.env) # MPI setup: simple rank configuration in 1st dim only. # TODO: in production runs, the ranks would be distributed along all # domain dimensions. self.soln.set_num_ranks(self.soln.get_domain_dim_names()[0], self.env.get_num_ranks()) # Redirect stdout/strerr to a string self.output = yk.yask_output_factory().new_string_output() self.soln.set_debug_output(self.output) # Set up the solution domain size for k, v in domain.items(): self.soln.set_rank_domain_size(k, v)
def __init__(self, name, yc_soln, local_grids=None): """ Write out a YASK kernel, build it using YASK's Makefiles, import the corresponding SWIG-generated Python module, and finally create a YASK kernel solution object. :param name: Unique name of this YaskKernel. :param yc_soln: YaskCompiler solution. :param local_grids: A local grid is necessary to run the YaskKernel, but its final content can be ditched. Indeed, local grids are hidden to users -- for example, they could represent temporary arrays introduced by the DSE. This parameter tells which of the ``yc_soln``'s grids are local. """ self.name = name # Shared object name self.soname = "%s.devito.%s" % (name, configuration['platform']) if os.path.exists(os.path.join(namespace['yask-pylib'], '%s.py' % name)): # Nothing to do -- the YASK solution was compiled in a previous session yk = import_module(name) log("cache hit, `%s` imported w/o jitting" % name) else: # We create and JIT compile a fresh YASK solution # The lock manager prevents race conditions # `lock_m` is used only to keep the lock manager alive with warnings.catch_warnings(): cleanup_m = CleanupManager() lock_m = CacheLockManager(cleanup_m, namespace['yask-output-dir']) # noqa # The directory in which the YASK-generated code (.hpp) will be placed yk_codegen = namespace['yask-codegen'](name, 'devito', configuration['platform']) if not os.path.exists(yk_codegen): os.makedirs(yk_codegen) # Write out the stencil file yk_codegen_file = os.path.join(yk_codegen, namespace['yask-codegen-file']) yc_soln.format(configuration['isa'], ofac.new_file_output(yk_codegen_file)) # JIT-compile it compiler = configuration.yask['compiler'] if configuration['develop-mode']: if yc_soln.get_num_equations() == 0: # YASK will compile more quickly, and no price has to be paid # in terms of performance, as this is a void kernel opt_level = 0 else: opt_level = 1 else: opt_level = 3 args = [ '-j', 'YK_CXX=%s' % compiler.cc, 'YK_CXXOPT=-O%d' % opt_level, # No MPI support at the moment 'mpi=0', # To locate the YASK compiler 'YC_EXEC=%s' % os.path.join(namespace['path'], 'bin'), # Error out if a grid not explicitly defined in the compiler is created 'allow_new_grid_types=0', # To give a unique name to the generated Python modules, rather # than creating `yask_kernel.py` 'YK_BASE=%s' % name, # `stencil` and `arch` should always be provided 'stencil=%s' % 'devito', 'arch=%s' % configuration['platform'], # The root directory of generated code files, shared libs, Python modules 'YASK_OUTPUT_DIR=%s' % namespace['yask-output-dir'], # Pick the YASK kernel Makefile, i.e. the one under `yask/src/kernel` '-C', namespace['kernel-path'], 'api' ] # Other potentially useful args: # - "EXTRA_MACROS=TRACE", -- debugging option make(namespace['path'], args) # Now we must be able to import the SWIG-generated Python module invalidate_caches() yk = import_module(name) # Release the lock manager cleanup_m.clean_up() # Create the YASK solution object kfac = yk.yk_factory() self.env = kfac.new_env() self.soln = kfac.new_solution(self.env) # Apply any user-provided options, if any. # These are applied here instead of just before prepare_solution() # so that applicable options will apply to all API calls. self.soln.apply_command_line_options(configuration.yask['options'] or '') # MPI setup: simple rank configuration in 1st dim only. # TODO: in production runs, the ranks would be distributed along all # domain dimensions. self.soln.set_num_ranks(self.space_dimensions[0], self.env.get_num_ranks()) # Redirect stdout to a string or file if configuration.yask['dump']: filename = 'yk_dump.%s.%s.%s.txt' % ( name, configuration['platform'], configuration['isa']) filename = os.path.join(configuration.yask['dump'], filename) self.output = yk.yask_output_factory().new_file_output(filename) else: self.output = yk.yask_output_factory().new_string_output() self.soln.set_debug_output(self.output) # Users may want to run the same Operator (same domain etc.) with # different grids. self.grids = {i.get_name(): i for i in self.soln.get_grids()} self.local_grids = { i.name: self.grids[i.name] for i in (local_grids or []) }
def __init__(self, name, yc_soln, local_grids=None): """ Write out a YASK kernel, compile it using the YASK's Makefiles, import the corresponding SWIG-generated Python module, and finally create a YASK kernel solution object. Parameters ---------- name : str Unique name of this YaskKernel. yc_soln The YaskCompiler solution. local_grids : list of Array, optional A local grid is necessary to run the YaskKernel, but it can be deallocated upon returning to Python-land. For example, local grids could be used to implement the temporary arrays introduced by the DSE. This parameter tells which of the ``yc_soln``'s grids are local. """ self.name = name # Shared object name self.soname = "%s.devito.%s" % (name, configuration['platform']) if os.path.exists(os.path.join(namespace['yask-pylib'], '%s.py' % name)): # Nothing to do -- the YASK solution was compiled in a previous session yk = import_module(name) debug("cache hit, `%s` imported w/o jitting" % name) else: # We create and JIT compile a fresh YASK solution # The lock manager prevents race conditions # `lock_m` is used only to keep the lock manager alive with warnings.catch_warnings(): cleanup_m = CleanupManager() lock_m = CacheLockManager(cleanup_m, namespace['yask-output-dir']) # noqa # The directory in which the YASK-generated code (.hpp) will be placed yk_codegen = namespace['yask-codegen'](name, 'devito', configuration['platform']) if not os.path.exists(yk_codegen): os.makedirs(yk_codegen) # Write out the stencil file yk_codegen_file = os.path.join(yk_codegen, namespace['yask-codegen-file']) yc_soln.format(configuration['platform'].isa, ofac.new_file_output(yk_codegen_file)) # JIT-compile it compiler = configuration.yask['compiler'] if configuration['develop-mode']: if yc_soln.get_num_equations() == 0: # YASK will compile more quickly, and no price has to be paid # in terms of performance, as this is a void kernel opt_level = 0 else: opt_level = 1 else: opt_level = 3 args = [ '-j', 'YK_CXX=%s' % compiler.cc, 'YK_CXXOPT=-O%d' % opt_level, # No MPI support at the moment 'mpi=0', # To locate the YASK compiler 'YC_EXEC=%s' % os.path.join(namespace['path'], 'bin'), # Error out if a grid not explicitly defined in the compiler is created 'allow_new_grid_types=0', # To give a unique name to the generated Python modules, rather # than creating `yask_kernel.py` 'YK_BASE=%s' % name, # `stencil` and `arch` should always be provided 'stencil=%s' % 'devito', 'arch=%s' % configuration['platform'], # The root directory of generated code files, shared libs, Python modules 'YASK_OUTPUT_DIR=%s' % namespace['yask-output-dir'], # Pick the YASK kernel Makefile, i.e. the one under `yask/src/kernel` '-C', namespace['kernel-path'], # Make target 'api' ] if configuration['develop-mode']: args.append('check=1') # Activate internal YASK asserts args.append('trace=1') # Print out verbose progress msgs w/-trace knob args.append('trace_mem=0') # Print out verbose mem-access msgs make(namespace['path'], args) # Now we must be able to import the SWIG-generated Python module invalidate_caches() yk = import_module(name) # Release the lock manager cleanup_m.clean_up() # Create the YASK solution object kfac = yk.yk_factory() self.env = kfac.new_env() self.soln = kfac.new_solution(self.env) # Allow step indices to wrap-around self.soln.set_step_wrap(True) # Apply any user-provided options, if any. # These are applied here instead of just before prepare_solution() # so that applicable options will apply to all API calls self.soln.apply_command_line_options(configuration.yask['options'] or '') # MPI setup: simple rank configuration in 1st dim only. # TODO: in production runs, the ranks would be distributed along all # domain dimensions self.soln.set_num_ranks(self.space_dimensions[0], self.env.get_num_ranks()) # Redirect stdout to a string or file if configuration.yask['dump']: filename = 'yk_dump.%s.%s.%s.txt' % (name, configuration['platform'], configuration['platform'].isa) filename = os.path.join(configuration.yask['dump'], filename) self.output = yk.yask_output_factory().new_file_output(filename) else: self.output = yk.yask_output_factory().new_string_output() self.soln.set_debug_output(self.output) # Users may want to run the same Operator (same domain etc.) with # different grids self.grids = {i.get_name(): i for i in self.soln.get_grids()} self.local_grids = {i.name: self.grids[i.name] for i in (local_grids or [])}