From a35b1ae056b261093ae1f5346d96bfff22cdf0dc Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 29 Apr 2021 00:51:50 +0100 Subject: [PATCH 1/8] Remove confusing options from configuration The reasoning is that things like CFLAGS have no obvious default so make little sense here. --- pyop2/configuration.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyop2/configuration.py b/pyop2/configuration.py index fe5a2c4c5..4749924a7 100644 --- a/pyop2/configuration.py +++ b/pyop2/configuration.py @@ -77,11 +77,8 @@ class Configuration(dict): """ # name, env variable, type, default, write once DEFAULTS = { - "compiler": ("PYOP2_BACKEND_COMPILER", str, "gcc"), "simd_width": ("PYOP2_SIMD_WIDTH", int, 4), "debug": ("PYOP2_DEBUG", bool, False), - "cflags": ("PYOP2_CFLAGS", str, ""), - "ldflags": ("PYOP2_LDFLAGS", str, ""), "compute_kernel_flops": ("PYOP2_COMPUTE_KERNEL_FLOPS", bool, False), "use_safe_cflags": ("PYOP2_USE_SAFE_CFLAGS", bool, True), "type_check": ("PYOP2_TYPE_CHECK", bool, True), From 99cd50d5fa9d494a1141346631428a9b7070b342 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 29 Apr 2021 00:54:58 +0100 Subject: [PATCH 2/8] WIP: Refactor compilers --- pyop2/compilation.py | 227 +++++++++++++++++++++++++++---------------- 1 file changed, 145 insertions(+), 82 deletions(-) diff --git a/pyop2/compilation.py b/pyop2/compilation.py index e5a9fefdd..0c44a02a9 100644 --- a/pyop2/compilation.py +++ b/pyop2/compilation.py @@ -32,6 +32,7 @@ # OF THE POSSIBILITY OF SUCH DAMAGE. +from abc import ABC, abstractmethod import os import subprocess import sys @@ -153,7 +154,7 @@ def compilation_comm(comm): return retcomm -class Compiler(object): +class Compiler(ABC): compiler_versions = {} @@ -173,18 +174,90 @@ class Compiler(object): :kwarg comm: Optional communicator to compile the code on (defaults to COMM_WORLD). """ - def __init__(self, cc, ld=None, cppargs=[], ldargs=[], - cpp=False, comm=None): - ccenv = 'CXX' if cpp else 'CC' + def __init__(self, extra_cppflags=None, extra_ldflags=None, cpp=False, comm=None): + self._extra_cppflags = extra_cppflags or [] + self._extra_ldflags = extra_ldflags or [] + + self._cpp = cpp + self._debug = configuration["debug"] + # Ensure that this is an internal communicator. comm = dup_comm(comm or COMM_WORLD) self.comm = compilation_comm(comm) - self._cc = os.environ.get(ccenv, cc) - self._ld = os.environ.get('LDSHARED', ld) - self._cppargs = cppargs + configuration['cflags'].split() - if configuration["use_safe_cflags"]: - self._cppargs += self.workaround_cflags - self._ldargs = ldargs + configuration['ldflags'].split() + + @property + def cc(self): + return os.environ.get("PYOP2_CC", self._cxx if self._cpp else self._cc) + + @property + def ld(self): + return os.environ.get("PYOP2_LD", None) + + @property + def cppflags(self): + try: + return os.environ["PYOP2_CPPFLAGS"] + except KeyError: + cppflags = self._cppflags + self._extra_cppflags + + if self._debug: + cppflags += self._debugflags + else: + cppflags += self._optflags + + if self._cpp: + cppflags += self._cxxflags + else: + cppflags += self._cflags + + return cppflags + + @property + def ldflags(self): + try: + return os.environ["PYOP2_LDFLAGS"] + except KeyError: + return self._ldflags + self._extra_ldflags + + @property + def bugfix_cflags(self): + return [] + + @property + @abstractmethod + def _cc(self): + ... + + @property + @abstractmethod + def _cxx(self): + ... + + @property + @abstractmethod + def _optflags(self): + ... + + @property + def _debugflags(self): + return ["-O0", "-g"] + + @property + @abstractmethod + def _cppflags(self): + ... + + @property + def _cflags(self): + return ["-std=c99"] + + @property + def _cxxflags(self): + return [] + + @property + def _ldflags(self): + return [] @property def compiler_version(self): @@ -354,86 +427,76 @@ def get_so(self, jitmodule, extension): return ctypes.CDLL(soname) -class MacCompiler(Compiler): - """A compiler for building a shared library on mac systems. +class MacClangCompiler(Compiler): + """A compiler for building a shared library on mac systems.""" - :arg cppargs: A list of arguments to pass to the C compiler - (optional). - :arg ldargs: A list of arguments to pass to the linker (optional). + @property + def _cc(self): + return "clang" - :arg cpp: Are we actually using the C++ compiler? + @property + def _cxx(self): + return "clang++" - :kwarg comm: Optional communicator to compile the code on (only - rank 0 compiles code) (defaults to COMM_WORLD). - """ + @property + def _cppflags(self): + return ["-fPIC", "-Wall", "-framework", "Accelerate"] - def __init__(self, cppargs=[], ldargs=[], cpp=False, comm=None): - opt_flags = ['-march=native', '-O3', '-ffast-math'] - if configuration['debug']: - opt_flags = ['-O0', '-g'] - cc = "mpicc" - stdargs = ["-std=c99"] - if cpp: - cc = "mpicxx" - stdargs = [] - cppargs = stdargs + ['-fPIC', '-Wall', '-framework', 'Accelerate'] + \ - opt_flags + cppargs - ldargs = ['-dynamiclib'] + ldargs - super(MacCompiler, self).__init__(cc, - cppargs=cppargs, - ldargs=ldargs, - cpp=cpp, - comm=comm) - - -class LinuxCompiler(Compiler): - """A compiler for building a shared library on linux systems. - - :arg cppargs: A list of arguments to pass to the C compiler - (optional). - :arg ldargs: A list of arguments to pass to the linker (optional). - :arg cpp: Are we actually using the C++ compiler? - :kwarg comm: Optional communicator to compile the code on (only - rank 0 compiles code) (defaults to COMM_WORLD).""" - def __init__(self, cppargs=[], ldargs=[], cpp=False, comm=None): - opt_flags = ['-march=native', '-O3', '-ffast-math'] - if configuration['debug']: - opt_flags = ['-O0', '-g'] - cc = "mpicc" - stdargs = ["-std=c99"] - if cpp: - cc = "mpicxx" - stdargs = [] - cppargs = stdargs + ['-fPIC', '-Wall'] + opt_flags + cppargs - ldargs = ['-shared'] + ldargs - - super(LinuxCompiler, self).__init__(cc, cppargs=cppargs, ldargs=ldargs, - cpp=cpp, comm=comm) + @property + def _optflags(self): + return ["-march=native", "-O3", "-ffast-math"] + + @property + def _ldflags(self): + return ["-dynamiclib"] + + +class LinuxGnuCompiler(Compiler): + """A compiler for building a shared library on Linux systems.""" + + @property + def _cc(self): + return "gcc" + + @property + def _cxx(self): + return "g++" + + @property + def _cppflags(self): + return ["-fPIC", "-Wall"] + + @property + def _ldargs(self): + return ["-shared"] + + @property + def _optflags(self): + return ["-march=native", "-O3", "-ffast-math"] class LinuxIntelCompiler(Compiler): - """The intel compiler for building a shared library on linux systems. + """The Intel compiler for building a shared library on Linux systems.""" - :arg cppargs: A list of arguments to pass to the C compiler - (optional). - :arg ldargs: A list of arguments to pass to the linker (optional). - :arg cpp: Are we actually using the C++ compiler? - :kwarg comm: Optional communicator to compile the code on (only - rank 0 compiles code) (defaults to COMM_WORLD). - """ - def __init__(self, cppargs=[], ldargs=[], cpp=False, comm=None): - opt_flags = ['-Ofast', '-xHost'] - if configuration['debug']: - opt_flags = ['-O0', '-g'] - cc = "mpicc" - stdargs = ["-std=c99"] - if cpp: - cc = "mpicxx" - stdargs = [] - cppargs = stdargs + ['-fPIC', '-no-multibyte-chars'] + opt_flags + cppargs - ldargs = ['-shared'] + ldargs - super(LinuxIntelCompiler, self).__init__(cc, cppargs=cppargs, ldargs=ldargs, - cpp=cpp, comm=comm) + @property + def _cc(self): + return "icc" + + @property + def _cxx(self): + return "icpc" + + @property + def _cppflags(self): + return ["-fPIC", "-no-multibyte-chars"] + + @property + def _optflags(self): + return ["-Ofast", "-xHost"] + + @property + def _ldflags(self): + return ["-shared"] @collective From 5635b846b478f65241472372b0c1c0cb4134d943 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 29 Apr 2021 01:16:56 +0100 Subject: [PATCH 3/8] WIP: Refactor compilers - more compact --- pyop2/compilation.py | 141 ++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 90 deletions(-) diff --git a/pyop2/compilation.py b/pyop2/compilation.py index 0c44a02a9..cac410ee9 100644 --- a/pyop2/compilation.py +++ b/pyop2/compilation.py @@ -156,8 +156,26 @@ def compilation_comm(comm): class Compiler(ABC): + @abstractmethod + def compile(self, jitmodule, extension): + ... + + +class ForkingCompiler(Compiler, ABC): + compiler_versions = {} + _cc = None + _cxx = None + _cppflags = None + _ldflags = None + + _cflags = None + _cxxflags = None + + _optflags = None + _debugflags = None + """A compiler for shared libraries. :arg cc: C compiler executable (can be overriden by exporting the @@ -200,7 +218,7 @@ def cppflags(self): except KeyError: cppflags = self._cppflags + self._extra_cppflags - if self._debug: + if not self._debug: cppflags += self._debugflags else: cppflags += self._optflags @@ -223,42 +241,6 @@ def ldflags(self): def bugfix_cflags(self): return [] - @property - @abstractmethod - def _cc(self): - ... - - @property - @abstractmethod - def _cxx(self): - ... - - @property - @abstractmethod - def _optflags(self): - ... - - @property - def _debugflags(self): - return ["-O0", "-g"] - - @property - @abstractmethod - def _cppflags(self): - ... - - @property - def _cflags(self): - return ["-std=c99"] - - @property - def _cxxflags(self): - return [] - - @property - def _ldflags(self): - return [] - @property def compiler_version(self): key = (id(self.comm), self._cc) @@ -427,76 +409,55 @@ def get_so(self, jitmodule, extension): return ctypes.CDLL(soname) -class MacClangCompiler(Compiler): +class MacClangCompiler(ForkingCompiler): """A compiler for building a shared library on mac systems.""" + + _cc = "clang" + _cxx = "clang++" + _cppflags = ["-fPIC", "-Wall", "-framework", "Accelerate"] + _ldflags = ["-dynamiclib"] - @property - def _cc(self): - return "clang" - - @property - def _cxx(self): - return "clang++" - - @property - def _cppflags(self): - return ["-fPIC", "-Wall", "-framework", "Accelerate"] + _cflags = ["-std=c99"] + _cxxflags = [] - @property - def _optflags(self): - return ["-march=native", "-O3", "-ffast-math"] - - @property - def _ldflags(self): - return ["-dynamiclib"] + _optflags = ["-march=native", "-O3", "-ffast-math"] + _debugflags = ["-O0", "-g"] -class LinuxGnuCompiler(Compiler): +class LinuxGnuCompiler(ForkingCompiler): """A compiler for building a shared library on Linux systems.""" - @property - def _cc(self): - return "gcc" - - @property - def _cxx(self): - return "g++" + _cc = "gcc" + _cxx = "g++" + _cppflags = ["-fPIC", "-Wall"] + _ldflags = ["-shared"] - @property - def _cppflags(self): - return ["-fPIC", "-Wall"] + _cflags = ["-std=c99"] + _cxxflags = [] - @property - def _ldargs(self): - return ["-shared"] - - @property - def _optflags(self): - return ["-march=native", "-O3", "-ffast-math"] + _optflags = ["-march=native", "-O3", "-ffast-math"] + _debugflags = ["-O0", "-g"] -class LinuxIntelCompiler(Compiler): +class LinuxIntelCompiler(ForkingCompiler): """The Intel compiler for building a shared library on Linux systems.""" - @property - def _cc(self): - return "icc" + _cc = "icc" + _cxx = "icpc" + _cppflags = ["-fPIC", "-no-multibyte-chars"] + _ldflags = ["-shared"] - @property - def _cxx(self): - return "icpc" + _cflags = ["-std=c99"] + _cxxflags = [] - @property - def _cppflags(self): - return ["-fPIC", "-no-multibyte-chars"] + _optflags = ["-Ofast", "-xHost"] + _debugflags = ["-O0", "-g"] - @property - def _optflags(self): - return ["-Ofast", "-xHost"] - @property - def _ldflags(self): - return ["-shared"] +class NonForkingCompiler(Compiler): + def compile(self, jitmodule, _): + ... + @collective From 3d0215b2d1f4d95b175468b064a0fad7638b9983 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 29 Apr 2021 10:29:19 +0100 Subject: [PATCH 4/8] Always use mpicc and begin writing non-forking cc --- pyop2/compilation.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/pyop2/compilation.py b/pyop2/compilation.py index cac410ee9..cb09151c0 100644 --- a/pyop2/compilation.py +++ b/pyop2/compilation.py @@ -165,8 +165,9 @@ class ForkingCompiler(Compiler, ABC): compiler_versions = {} - _cc = None - _cxx = None + _cc = "mpicc" + _cxx = "mpicxx" + _cppflags = None _ldflags = None @@ -412,8 +413,6 @@ def get_so(self, jitmodule, extension): class MacClangCompiler(ForkingCompiler): """A compiler for building a shared library on mac systems.""" - _cc = "clang" - _cxx = "clang++" _cppflags = ["-fPIC", "-Wall", "-framework", "Accelerate"] _ldflags = ["-dynamiclib"] @@ -427,8 +426,6 @@ class MacClangCompiler(ForkingCompiler): class LinuxGnuCompiler(ForkingCompiler): """A compiler for building a shared library on Linux systems.""" - _cc = "gcc" - _cxx = "g++" _cppflags = ["-fPIC", "-Wall"] _ldflags = ["-shared"] @@ -442,8 +439,6 @@ class LinuxGnuCompiler(ForkingCompiler): class LinuxIntelCompiler(ForkingCompiler): """The Intel compiler for building a shared library on Linux systems.""" - _cc = "icc" - _cxx = "icpc" _cppflags = ["-fPIC", "-no-multibyte-chars"] _ldflags = ["-shared"] @@ -455,9 +450,22 @@ class LinuxIntelCompiler(ForkingCompiler): class NonForkingCompiler(Compiler): + ... + + +class DragonFFICompiler(NonForkingCompiler): def compile(self, jitmodule, _): - ... + try: + import pydffi + except ImportError: + raise ImportError("DragonFFI must be installed first") + ffi = pydffi.FFI() + ffi.compile(jitmodule.codetocompile) + +class TinyCCompiler(NonForkingCompiler): + def compile(self, jitmodule, _): + ... @collective @@ -502,11 +510,11 @@ def __init__(self, code, argtypes): if compiler == 'icc': compiler = LinuxIntelCompiler(cppargs, ldargs, cpp=cpp, comm=comm) elif compiler == 'gcc': - compiler = LinuxCompiler(cppargs, ldargs, cpp=cpp, comm=comm) + compiler = LinuxGnuCompiler(cppargs, ldargs, cpp=cpp, comm=comm) else: raise CompilationError("Unrecognized compiler name '%s'" % compiler) elif platform.find('darwin') == 0: - compiler = MacCompiler(cppargs, ldargs, cpp=cpp, comm=comm) + compiler = MacClangCompiler(cppargs, ldargs, cpp=cpp, comm=comm) else: raise CompilationError("Don't know what compiler to use for platform '%s'" % platform) From 9764430e496f9478491b37059c42701433af5437 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 6 May 2021 11:27:03 +0100 Subject: [PATCH 5/8] WIP: Add cppyy and cffi as FFI backends --- pyop2/base.py | 132 +++++++++++++++++++++++++++++++------- pyop2/compilation.py | 147 +++++++++++++++++++++++-------------------- pyop2/petsc_base.py | 24 ++++++- pyop2/sequential.py | 25 ++++++-- 4 files changed, 226 insertions(+), 102 deletions(-) diff --git a/pyop2/base.py b/pyop2/base.py index 26c01d4cd..af3ed433d 100644 --- a/pyop2/base.py +++ b/pyop2/base.py @@ -170,8 +170,16 @@ def __init__(self, data=None, map=None, access=None, lgmaps=None, unroll_map=Fal "To set of %s doesn't match the set of %s." % (map, data)) @cached_property - def _kernel_args_(self): - return self.data._kernel_args_ + def ctypes_args(self): + return self.data.ctypes_args + + @property + def cffi_args(self): + return self.data.cffi_args + + @property + def cppyy_args(self): + return self.data.cppyy_args @cached_property def _argtypes_(self): @@ -403,7 +411,9 @@ class Set(object): _extruded = False - _kernel_args_ = () + ctypes_args = () + cffi_args = () + cppyy_args = () _argtypes_ = () @cached_property @@ -531,7 +541,9 @@ class GlobalSet(Set): """A proxy set allowing a :class:`Global` to be used in place of a :class:`Dat` where appropriate.""" - _kernel_args_ = () + ctypes_args = () + cffi_args = () + cppyy_args = () _argtypes_ = () def __init__(self, comm=None): @@ -642,8 +654,16 @@ def __init__(self, parent, layers): self._extruded = True @cached_property - def _kernel_args_(self): - return (self.layers_array.ctypes.data, ) + def ctypes_args(self): + return (self.layers_array.ctypes.data,) + + @property + def cffi_args(self): + return NotImplemented + + @property + def cppyy_args(self): + return (self._layers,) @cached_property def _argtypes_(self): @@ -723,8 +743,16 @@ def __init__(self, superset, indices): self._extruded = superset._extruded @cached_property - def _kernel_args_(self): - return self._superset._kernel_args_ + (self._indices.ctypes.data, ) + def ctypes_args(self): + return self._superset.ctypes_args + (self._indices.ctypes.data,) + + @property + def cffi_args(self): + return NotImplemented + + @property + def cppyy_args(self): + return self._superset.cppyy_args + (self._indices,) @cached_property def _argtypes_(self): @@ -800,8 +828,16 @@ def __init__(self, sets): self.comm = reduce(lambda a, b: a or b, map(lambda s: s if s is None else s.comm, sets)) self._initialized = True - @cached_property - def _kernel_args_(self): + @property + def ctypes_args(self): + raise NotImplementedError + + @property + def cffi_args(self): + raise NotImplementedError + + @property + def cppyy_args(self): raise NotImplementedError @cached_property @@ -1370,9 +1406,17 @@ def __init__(self, dataset, data=None, dtype=None, name=None): self.halo_valid = True self._name = name or "dat_#x%x" % id(self) - @cached_property - def _kernel_args_(self): - return (self._data.ctypes.data, ) + @property + def ctypes_args(self): + return (self._data.ctypes.data,) + + @property + def cffi_args(self): + raise NotImplementedError + + @property + def cppyy_args(self): + return (self._data,) @cached_property def _argtypes_(self): @@ -1923,9 +1967,17 @@ def __init__(self, dat, index): name="view[%s](%s)" % (index, dat.name)) self._parent = dat - @cached_property - def _kernel_args_(self): - return self._parent._kernel_args_ + @property + def ctypes_args(self): + return self._parent.ctypes_args + + @property + def cffi_args(self): + raise NotImplementedError + + @property + def cppyy_args(self): + return self._parent.cppyy_args @cached_property def _argtypes_(self): @@ -2005,8 +2057,16 @@ def what(x): self.comm = self._dats[0].comm @cached_property - def _kernel_args_(self): - return tuple(itertools.chain(*(d._kernel_args_ for d in self))) + def ctypes_args(self): + return tuple(itertools.chain(*(d.ctypes_args for d in self))) + + @cached_property + def cffi_args(self): + raise NotImplementedError + + @cached_property + def cppyy_args(self): + return tuple(itertools.chain(*(d.cppyy_args for d in self))) @cached_property def _argtypes_(self): @@ -2290,8 +2350,16 @@ def __init__(self, dim, data=None, dtype=None, name=None, comm=None): self.comm = comm @cached_property - def _kernel_args_(self): - return (self._data.ctypes.data, ) + def ctypes_args(self): + return (self._data.ctypes.data,) + + @cached_property + def cffi_args(self): + raise NotImplementedError + + @cached_property + def cppyy_args(self): + return (self._data,) @cached_property def _argtypes_(self): @@ -2528,8 +2596,16 @@ def __init__(self, iterset, toset, arity, values=None, name=None, offset=None): self._cache = {} @cached_property - def _kernel_args_(self): - return (self._values.ctypes.data, ) + def ctypes_args(self): + return (self._values.ctypes.data,) + + @cached_property + def cffi_args(self): + raise NotImplementedError + + @cached_property + def cppyy_args(self): + return (self._values,) @cached_property def _argtypes_(self): @@ -2654,8 +2730,16 @@ def _cache_key(cls, maps): return maps @cached_property - def _kernel_args_(self): - return tuple(itertools.chain(*(m._kernel_args_ for m in self if m is not None))) + def ctypes_args(self): + return tuple(itertools.chain(*(m.ctypes_args for m in self if m is not None))) + + @cached_property + def cffi_args(self): + raise NotImplementedError + + @cached_property + def cppyy_args(self): + return tuple(itertools.chain(*(m.cppyy_args for m in self if m is not None))) @cached_property def _argtypes_(self): diff --git a/pyop2/compilation.py b/pyop2/compilation.py index cb09151c0..f4d398f80 100644 --- a/pyop2/compilation.py +++ b/pyop2/compilation.py @@ -33,14 +33,15 @@ from abc import ABC, abstractmethod +import collections +import ctypes +from distutils import version +from enum import IntEnum, auto +from hashlib import md5 +from itertools import chain import os import subprocess import sys -import ctypes -import collections -from hashlib import md5 -from distutils import version - from pyop2.mpi import MPI, collective, COMM_WORLD from pyop2.mpi import dup_comm, get_compilation_comm, set_compilation_comm @@ -50,6 +51,12 @@ from pyop2.base import JITModule +class FFIBackend(IntEnum): + CTYPES = auto() + CFFI = auto() + CPPYY = auto() + + def _check_hashes(x, y, datatype): """MPI reduction op to check if code hashes differ across ranks.""" if x == y: @@ -156,26 +163,19 @@ def compilation_comm(comm): class Compiler(ABC): - @abstractmethod - def compile(self, jitmodule, extension): - ... - - -class ForkingCompiler(Compiler, ABC): - - compiler_versions = {} + compiler_versions = {} # TODO: what to do with this? - _cc = "mpicc" - _cxx = "mpicxx" + default_cc = "mpicc" + default_cxx = "mpicxx" - _cppflags = None - _ldflags = None + default_cppflags = None + default_ldflags = None - _cflags = None - _cxxflags = None + default_cflags = None + default_cxxflags = None - _optflags = None - _debugflags = None + default_optflags = None + default_debugflags = None """A compiler for shared libraries. @@ -282,7 +282,8 @@ def workaround_cflags(self): return [] @collective - def get_so(self, jitmodule, extension): + @classmethod + def compile(cls, jitmodule, extension): """Build a shared library and load it :arg jitmodule: The JIT Module which can generate the code to compile. @@ -294,8 +295,8 @@ def get_so(self, jitmodule, extension): # Determine cache key hsh = md5(str(jitmodule.cache_key[1:]).encode()) hsh.update(self._cc.encode()) - if self._ld: - hsh.update(self._ld.encode()) + if self.ld: + hsh.update(self.ld.encode()) hsh.update("".join(self._cppargs).encode()) hsh.update("".join(self._ldargs).encode()) @@ -340,7 +341,7 @@ def get_so(self, jitmodule, extension): with open(cname, "w") as f: f.write(jitmodule.code_to_compile) # Compiler also links - if self._ld is None: + if self.ld is None: cc = [self._cc] + self._cppargs + \ ['-o', tmpname, cname] + self._ldargs debug('Compilation command: %s', ' '.join(cc)) @@ -368,7 +369,7 @@ def get_so(self, jitmodule, extension): else: cc = [self._cc] + self._cppargs + \ ['-c', '-o', oname, cname] - ld = self._ld.split() + ['-o', tmpname, oname] + self._ldargs + ld = self.ld.split() + ['-o', tmpname, oname] + self.ldargs debug('Compilation command: %s', ' '.join(cc)) debug('Link command: %s', ' '.join(ld)) with open(logfile, "w") as log: @@ -410,7 +411,7 @@ def get_so(self, jitmodule, extension): return ctypes.CDLL(soname) -class MacClangCompiler(ForkingCompiler): +class MacClangCompiler(Compiler): """A compiler for building a shared library on mac systems.""" _cppflags = ["-fPIC", "-Wall", "-framework", "Accelerate"] @@ -423,7 +424,7 @@ class MacClangCompiler(ForkingCompiler): _debugflags = ["-O0", "-g"] -class LinuxGnuCompiler(ForkingCompiler): +class LinuxGnuCompiler(Compiler): """A compiler for building a shared library on Linux systems.""" _cppflags = ["-fPIC", "-Wall"] @@ -436,7 +437,7 @@ class LinuxGnuCompiler(ForkingCompiler): _debugflags = ["-O0", "-g"] -class LinuxIntelCompiler(ForkingCompiler): +class LinuxIntelCompiler(Compiler): """The Intel compiler for building a shared library on Linux systems.""" _cppflags = ["-fPIC", "-no-multibyte-chars"] @@ -449,28 +450,9 @@ class LinuxIntelCompiler(ForkingCompiler): _debugflags = ["-O0", "-g"] -class NonForkingCompiler(Compiler): - ... - - -class DragonFFICompiler(NonForkingCompiler): - def compile(self, jitmodule, _): - try: - import pydffi - except ImportError: - raise ImportError("DragonFFI must be installed first") - ffi = pydffi.FFI() - ffi.compile(jitmodule.codetocompile) - - -class TinyCCompiler(NonForkingCompiler): - def compile(self, jitmodule, _): - ... - - @collective def load(jitmodule, extension, fn_name, cppargs=[], ldargs=[], - argtypes=None, restype=None, compiler=None, comm=None): + argtypes=None, restype=None, compiler=None, comm=None, ffi_backend=FFIBackend.CTYPES): """Build a shared library and return a function pointer from it. :arg jitmodule: The JIT Module which can generate the code to compile, or @@ -502,28 +484,55 @@ def __init__(self, code, argtypes): else: raise ValueError("Don't know how to compile code of type %r" % type(jitmodule)) - platform = sys.platform - cpp = extension == "cpp" - if not compiler: - compiler = configuration["compiler"] - if platform.find('linux') == 0: - if compiler == 'icc': - compiler = LinuxIntelCompiler(cppargs, ldargs, cpp=cpp, comm=comm) - elif compiler == 'gcc': - compiler = LinuxGnuCompiler(cppargs, ldargs, cpp=cpp, comm=comm) + # testing + ffi_backend = FFIBackend.CPPYY + if ffi_backend == FFIBackend.CTYPES: + platform = sys.platform + cpp = extension == "cpp" + if not compiler: + compiler = configuration["compiler"] + if platform.find('linux') == 0: + if compiler == 'icc': + compiler = LinuxIntelCompiler(cppargs, ldargs, cpp=cpp, comm=comm) + elif compiler == 'gcc': + compiler = LinuxGnuCompiler(cppargs, ldargs, cpp=cpp, comm=comm) + else: + raise CompilationError("Unrecognized compiler name '%s'" % compiler) + elif platform.find('darwin') == 0: + compiler = MacClangCompiler(cppargs, ldargs, cpp=cpp, comm=comm) else: - raise CompilationError("Unrecognized compiler name '%s'" % compiler) - elif platform.find('darwin') == 0: - compiler = MacClangCompiler(cppargs, ldargs, cpp=cpp, comm=comm) + raise CompilationError("Don't know what compiler to use for platform '%s'" % + platform) + dll = compiler.compile(code, extension) + + fn = getattr(dll, fn_name) + fn.argtypes = code.argtypes + fn.restype = restype + return fn + elif ffi_backend == FFIBackend.CFFI: + raise NotImplementedError + elif ffi_backend == FFIBackend.CPPYY: + return _load_cppyy(code, fn_name, cppargs, ldargs) else: - raise CompilationError("Don't know what compiler to use for platform '%s'" % - platform) - dll = compiler.get_so(code, extension) - - fn = getattr(dll, fn_name) - fn.argtypes = code.argtypes - fn.restype = restype - return fn + raise AssertionError + + +def _load_cppyy(code, fn_name, cppargs, ldargs): + import cppyy + + print(code.code_to_compile) + exit() + + for flag in chain(cppargs, ldargs): + if flag.startswith("-I"): + cppyy.add_include_path(flag.strip("-I")) + elif flag.startswith("-L"): + cppyy.add_library_path(flag.strip("-L")) + elif flag.startswith("-l"): + cppyy.load_library(flag.strip("-l")) + + cppyy.cppdef(code.code_to_compile) + return getattr(cppyy.gbl, fn_name, ) def clear_cache(prompt=False): diff --git a/pyop2/petsc_base.py b/pyop2/petsc_base.py index 16ecdcefe..293207c15 100644 --- a/pyop2/petsc_base.py +++ b/pyop2/petsc_base.py @@ -508,8 +508,17 @@ def __init__(self, parent, i, j): self.local_to_global_maps = self.handle.getLGMap() @utils.cached_property - def _kernel_args_(self): - return (self.handle.handle, ) + def ctypes_args(self): + return (self.handle.handle,) + + @utils.cached_property + def cffi_args(self): + raise NotImplementedError + + @utils.cached_property + def cppyy_args(self): + import cppyy + return cppyy.bind_object(self.handle.handle, "PetscMat") @utils.cached_property def _wrapper_cache_key_(self): @@ -607,9 +616,18 @@ def __init__(self, *args, **kwargs): local_to_global_maps = (None, None) @utils.cached_property - def _kernel_args_(self): + def ctypes_args(self): return tuple(a.handle.handle for a in self) + @utils.cached_property + def cffi_args(self): + raise NotImplementedError + + @utils.cached_property + def cppyy_args(self): + import cppyy + return tuple(cppyy.bind_object(a.handle.handle, "PetscMat") for a in self) + @collective def _init(self): if not self.dtype == PETSc.ScalarType: diff --git a/pyop2/sequential.py b/pyop2/sequential.py index 1dbab1c18..c226783c3 100644 --- a/pyop2/sequential.py +++ b/pyop2/sequential.py @@ -50,6 +50,7 @@ from pyop2.base import DatView # noqa: F401 from pyop2.base import Kernel # noqa: F401 from pyop2.base import Arg # noqa: F401 +from pyop2.compilation import FFIBackend from pyop2.petsc_base import DataSet, MixedDataSet # noqa: F401 from pyop2.petsc_base import Global, GlobalDataSet # noqa: F401 from pyop2.petsc_base import Dat, MixedDat, Mat # noqa: F401 @@ -139,7 +140,8 @@ def compile(self): from pyop2.configuration import configuration - compiler = configuration["compiler"] + # compiler = configuration["compiler"] + compiler = "gcc" extension = "cpp" if self._kernel._cpp else "c" cppargs = self._cppargs cppargs += ["-I%s/include" % d for d in get_petsc_dir()] + \ @@ -174,7 +176,7 @@ def argtypes(self): for arg in self._args: maps = arg.map_tuple for map_ in maps: - for k, t in zip(map_._kernel_args_, map_._argtypes_): + for k, t in zip(map_.ctypes_args, map_._argtypes_): if k in seen: continue argtypes += (t,) @@ -184,17 +186,28 @@ def argtypes(self): class ParLoop(petsc_base.ParLoop): - def prepare_arglist(self, iterset, *args): - arglist = iterset._kernel_args_ + def prepare_arglist(self, iterset, *args, ffi_backend=FFIBackend.CTYPES): + def get_args(obj): + """Return the appropriate arguments to pass into the wrapper.""" + if ffi_backend == FFIBackend.CTYPES: + return obj.ctypes_args + elif ffi_backend == FFIBackend.CFFI: + return obj.cffi_args + elif ffi_backend == FFIBackend.CPPYY: + return obj.cppyy_args + else: + raise AssertionError + + arglist = get_args(iterset) for arg in args: - arglist += arg._kernel_args_ + arglist += get_args(arg) seen = set() for arg in args: maps = arg.map_tuple for map_ in maps: if map_ is None: continue - for k in map_._kernel_args_: + for k in get_args(map_): if k in seen: continue arglist += (k,) From 71f76c5d71bf5afe4b7af991b42598054f3b285f Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 6 May 2021 12:55:54 +0100 Subject: [PATCH 6/8] WIP: test_dats now passing --- pyop2/base.py | 9 +++++---- pyop2/compilation.py | 6 +++--- pyop2/sequential.py | 3 +++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pyop2/base.py b/pyop2/base.py index af3ed433d..cdab6201e 100644 --- a/pyop2/base.py +++ b/pyop2/base.py @@ -810,8 +810,8 @@ def layers_array(self): class SetPartition(object): def __init__(self, set, offset, size): self.set = set - self.offset = offset - self.size = size + self.offset = int(offset) + self.size = int(size) class MixedSet(Set, ObjectCached): @@ -1631,6 +1631,7 @@ def _copy_parloop(self, other, subset=None): if not hasattr(self, '_copy_kernel'): import islpy as isl import pymbolic.primitives as p + name = f"copy_{id(self)}" inames = isl.make_zero_and_vars(["i"]) domain = (inames[0].le_set(inames["i"])) & (inames["i"].lt_set(inames[0] + self.cdim)) _other = p.Variable("other") @@ -1639,9 +1640,9 @@ def _copy_parloop(self, other, subset=None): insn = loopy.Assignment(_other.index(i), _self.index(i), within_inames=frozenset(["i"])) data = [loopy.GlobalArg("self", dtype=self.dtype, shape=(self.cdim,)), loopy.GlobalArg("other", dtype=other.dtype, shape=(other.cdim,))] - knl = loopy.make_function([domain], [insn], data, name="copy") + knl = loopy.make_function([domain], [insn], data, name=name) - self._copy_kernel = _make_object('Kernel', knl, 'copy') + self._copy_kernel = _make_object('Kernel', knl, name) return _make_object('ParLoop', self._copy_kernel, subset or self.dataset.set, self(READ), other(WRITE)) diff --git a/pyop2/compilation.py b/pyop2/compilation.py index f4d398f80..5f45898d8 100644 --- a/pyop2/compilation.py +++ b/pyop2/compilation.py @@ -520,8 +520,8 @@ def __init__(self, code, argtypes): def _load_cppyy(code, fn_name, cppargs, ldargs): import cppyy - print(code.code_to_compile) - exit() + # print(code.code_to_compile) + # exit() for flag in chain(cppargs, ldargs): if flag.startswith("-I"): @@ -532,7 +532,7 @@ def _load_cppyy(code, fn_name, cppargs, ldargs): cppyy.load_library(flag.strip("-l")) cppyy.cppdef(code.code_to_compile) - return getattr(cppyy.gbl, fn_name, ) + return getattr(cppyy.gbl, fn_name) def clear_cache(prompt=False): diff --git a/pyop2/sequential.py b/pyop2/sequential.py index c226783c3..b146f1e1c 100644 --- a/pyop2/sequential.py +++ b/pyop2/sequential.py @@ -104,6 +104,7 @@ def __init__(self, kernel, iterset, *args, **kwargs): @collective def __call__(self, *args): + # import pdb; pdb.set_trace() return self._fun(*args) @cached_property @@ -187,6 +188,7 @@ def argtypes(self): class ParLoop(petsc_base.ParLoop): def prepare_arglist(self, iterset, *args, ffi_backend=FFIBackend.CTYPES): + ffi_backend = FFIBackend.CPPYY # testing def get_args(obj): """Return the appropriate arguments to pass into the wrapper.""" if ffi_backend == FFIBackend.CTYPES: @@ -228,6 +230,7 @@ def _compute_event(self): def _compute(self, part, fun, *arglist): with self._compute_event: self.log_flops(part.size * self.num_flops) + # import pdb; pdb.set_trace() fun(part.offset, part.offset + part.size, *arglist) From 2f1c60269f1dff5e630cd7d09afcb645c7a879a8 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 6 May 2021 13:02:21 +0100 Subject: [PATCH 7/8] Fix bug where numpy arrays are not hashable --- pyop2/sequential.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pyop2/sequential.py b/pyop2/sequential.py index b146f1e1c..bcc9e7d0f 100644 --- a/pyop2/sequential.py +++ b/pyop2/sequential.py @@ -33,10 +33,11 @@ """OP2 sequential backend.""" -import os from copy import deepcopy as dcopy - import ctypes +import os + +import numpy as np from pyop2.datatypes import IntType, as_ctypes from pyop2 import base @@ -210,10 +211,11 @@ def get_args(obj): if map_ is None: continue for k in get_args(map_): - if k in seen: + key = k.ctypes.data if isinstance(k, np.ndarray) else k + if key in seen: continue arglist += (k,) - seen.add(k) + seen.add(key) return arglist @cached_property From 7e980a2a71bfa5ca795c91c9bfd1badacfffa146 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Thu, 6 May 2021 13:26:30 +0100 Subject: [PATCH 8/8] Correctly cast Mats --- pyop2/compilation.py | 7 ++++++- pyop2/petsc_base.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pyop2/compilation.py b/pyop2/compilation.py index 5f45898d8..048b21dd9 100644 --- a/pyop2/compilation.py +++ b/pyop2/compilation.py @@ -531,7 +531,12 @@ def _load_cppyy(code, fn_name, cppargs, ldargs): elif flag.startswith("-l"): cppyy.load_library(flag.strip("-l")) - cppyy.cppdef(code.code_to_compile) + # debug + try: + cppyy.cppdef(code.code_to_compile) + except: + print(code.code_to_compile) + raise Exception return getattr(cppyy.gbl, fn_name) diff --git a/pyop2/petsc_base.py b/pyop2/petsc_base.py index 293207c15..e170a932a 100644 --- a/pyop2/petsc_base.py +++ b/pyop2/petsc_base.py @@ -518,7 +518,11 @@ def cffi_args(self): @utils.cached_property def cppyy_args(self): import cppyy - return cppyy.bind_object(self.handle.handle, "PetscMat") + import cppyy.ll + for dir_ in utils.get_petsc_dir(): + cppyy.add_include_path(f"{dir_}/include") + cppyy.include("petsc.h") + return cppyy.ll.cast["Mat"](self.handle.handle) @utils.cached_property def _wrapper_cache_key_(self): @@ -626,7 +630,11 @@ def cffi_args(self): @utils.cached_property def cppyy_args(self): import cppyy - return tuple(cppyy.bind_object(a.handle.handle, "PetscMat") for a in self) + import cppyy.ll + for dir_ in utils.get_petsc_dir(): + cppyy.add_include_path(f"{dir_}/include") + cppyy.include("petsc.h") + return tuple(cppyy.ll.cast["Mat"](a.handle.handle) for a in self) @collective def _init(self):