Skip to content

Commit

Permalink
Merge branch 'master' into dask2
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Lange authored Oct 27, 2017
2 parents bdc3428 + 0644ec1 commit a962b0d
Show file tree
Hide file tree
Showing 72 changed files with 1,952 additions and 1,589 deletions.
29 changes: 26 additions & 3 deletions devito/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from sympy.core import cache

from devito.base import * # noqa
from devito.backends import init_backend
from devito.finite_difference import * # noqa
from devito.dimension import * # noqa
from devito.grid import * # noqa
Expand All @@ -15,6 +14,9 @@
from devito.tools import * # noqa
from devito.dse import * # noqa

from devito.compiler import compiler_registry
from devito.backends import backends_registry, init_backend


def clear_cache():
cache.clear_cache()
Expand All @@ -29,7 +31,28 @@ def clear_cache():
__version__ = get_versions()['version']
del get_versions

# First add the compiler configuration option...
configuration.add('compiler', 'custom', list(compiler_registry),
lambda i: compiler_registry[i]())
configuration.add('openmp', 0, [0, 1], lambda i: bool(i))
configuration.add('debug_compiler', 0, [0, 1], lambda i: bool(i))

# ... then the backend configuration. The order is important since the
# backend might depend on the compiler configuration.
configuration.add('backend', 'core', list(backends_registry),
callback=init_backend)

# Set the Instruction Set Architecture (ISA)
ISAs = [None, 'cpp', 'avx', 'avx2', 'avx512', 'knc']
configuration.add('isa', None, ISAs)

# Set the CPU architecture (only codename)
PLATFORMs = [None, 'intel64', 'sandybridge', 'ivybridge', 'haswell',
'broadwell', 'skylake', 'knc', 'knl']
# TODO: switch arch to actual architecture names; use the mapper in /YASK/
configuration.add('platform', None, PLATFORMs)


# Initialize the Devito backend
# Initialize the configuration, either from the environment or
# defaults. This will also trigger the backend initialization
init_configuration()
init_backend(configuration['backend'])
104 changes: 70 additions & 34 deletions devito/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import numpy as np
from cached_property import cached_property
from collections import namedtuple

from devito.exceptions import InvalidArgument
from devito.logger import debug
Expand Down Expand Up @@ -34,7 +35,7 @@ class Argument(object):
def __init__(self, name, provider, default_value=None):
self.name = name
self.provider = provider
self._value = self._default_value = default_value
self._value = self.default_value = default_value

@property
def value(self):
Expand All @@ -48,38 +49,39 @@ def value(self):
except AttributeError:
return self._value

@property
def ready(self):
return self._value is not None

@property
def dtype(self):
return self.provider.dtype

def reset(self):
self._value = self._default_value
self._value = self.default_value

@abc.abstractproperty
def verify(self, kwargs):
return

def __repr__(self):
return self.name


class ScalarArgument(Argument):

""" Class representing scalar arguments that a kernel might expect.
Most commonly used to pass dimension sizes
enforce determines whether any reduction will be performed or not.
i.e. if it is a user-provided value, use it directly.
"""

is_ScalarArgument = True

def __init__(self, name, provider, reducer, default_value=None):
def __init__(self, name, provider, reducer=lambda old, new: new, default_value=None):
super(ScalarArgument, self).__init__(name, provider, default_value)
self.reducer = reducer

def verify(self, value):
def verify(self, value, enforce=False):
# Assuming self._value was initialised as appropriate for the reducer
if value is not None:
if self._value is not None:
if self._value is not None and not enforce:
self._value = self.reducer(self._value, value)
else:
self._value = value
Expand All @@ -101,10 +103,10 @@ def verify(self, value):
if value is None:
value = self._value

verify = self.provider.shape == value.shape
verify = len(self.provider.shape) == len(value.shape)

verify = verify and all(d.verify(v) for d, v in zip(self.provider.indices,
value.shape))
verify = verify and all(d.verify(v) for d, v in
zip(self.provider.indices, value.shape))
if verify:
self._value = value

Expand Down Expand Up @@ -157,14 +159,18 @@ class DimensionArgProvider(ArgumentProvider):

def __init__(self, *args, **kwargs):
super(DimensionArgProvider, self).__init__(*args, **kwargs)
self._value = self._default_value

def reset(self):
self._value = self._default_value
for i in self.rtargs:
i.reset()

@property
def value(self):
return self._value
""" Returns a tuple (same order as rtargs) with the current value of each rtarg
If any of the rtargs has value None, the return value here is None.
"""
child_values = tuple([i.value for i in self.rtargs])
return child_values if all(i is not None for i in child_values) else None

@property
def dtype(self):
Expand All @@ -173,11 +179,42 @@ def dtype(self):

@cached_property
def rtargs(self):
size = ScalarArgument("%s_size" % self.name, self, max)
return [size]
size = ScalarArgument(self.size_name, self, max)
start = ScalarArgument(self.start_name, self, max, 0)
end = ScalarArgument(self.end_name, self, max)
return namedtuple("RuntimeArguments", ["size", "start", "end"])(size, start, end)

def _promote(self, value):
""" Strictly, a dimension's value is a 3-tuple consisting of the
values of each of its rtargs - currently size, start and end. However, for
convenience, we may accept partial representations of the value, e.g. scalars
and 2-tuples and interpret them in a certain way while assuming defaults for
missing information. If value is:
3-tuple: it contains explicit values for all 3 rtargs and hence will be used
directly
2-tuple: We assume we are being provided the (start, end) values. This will be
promoted to a 3-tuple assuming size to be the same as end.
scalar: We assume we are being provided the value of size. Promote to 3-tuple
by assuming this scalar is the size and the end of the dimension. start will
default to 0.
"""

if not isinstance(value, tuple):
# scalar
size, start, end = self.rtargs
value = (value, start.default_value, value)
else:
if len(value) == 2:
# 2-tuple
# Assume we've been passed a (start, end) tuple
start, end = value
value = (end, start, end)
elif len(value) != 3:
raise InvalidArgument("Expected either a scalar value or a tuple(2/3)")
return value

# TODO: Can we do without a verify on a dimension?
def verify(self, value):
def verify(self, value, enforce=False):
verify = True
if value is None:
if self.value is not None:
Expand All @@ -189,37 +226,36 @@ def verify(self, value):
return False
except AttributeError:
return False

try:
# Make sure we're dealing with a 3-tuple. See docstring of _promote for more
value = self._promote(value)
if hasattr(self, 'parent'):
parent_value = self.parent.value
if parent_value is not None:
value = self.reducer(value, parent_value)
if parent_value is not None and not enforce:
parent_value = self._promote(parent_value)
value = tuple([self.reducer(i1, i2) for i1, i2 in zip(value,
parent_value)])
verify = verify and self.parent.verify(value)
except AttributeError:
pass

if value == self.value:
return True

# Derived dimensions could be linked through constraints
# At this point, a constraint needs to be added that enforces
# dim_e - dim_s < SOME_MAX
# Also need a default constraint that dim_e > dim_s (or vice-versa)
verify = verify and all([a.verify(v) for a, v in zip(self.rtargs, (value,))])
if verify:
self._value = value
verify = verify and all([a.verify(v, enforce=enforce) for a, v in
zip(self.rtargs, value)])
return verify


class ConstantArgProvider(ArgumentProvider):

""" Class used to decorate Constat Data objects with behaviour required for runtime
""" Class used to decorate Constant Data objects with behaviour required for runtime
arguments.
"""

@cached_property
def rtargs(self):
return [ScalarArgument(self.name, self, lambda old, new: new, self.data)]
return (ScalarArgument(self.name, self, default_value=self.data),)


class TensorFunctionArgProvider(ArgumentProvider):
Expand All @@ -230,7 +266,7 @@ class TensorFunctionArgProvider(ArgumentProvider):

@cached_property
def rtargs(self):
return [TensorArgument(self.name, self)]
return (TensorArgument(self.name, self),)


class ScalarArgProvider(ArgumentProvider):
Expand All @@ -241,7 +277,7 @@ class ScalarArgProvider(ArgumentProvider):

@cached_property
def rtargs(self):
return [ScalarArgument(self.name, self, self.dtype)]
return (ScalarArgument(self.name, self, self.dtype),)


class ArrayArgProvider(ArgumentProvider):
Expand All @@ -252,7 +288,7 @@ class ArrayArgProvider(ArgumentProvider):

@cached_property
def rtargs(self):
return [TensorArgument(self.name, self)]
return (TensorArgument(self.name, self),)


class ObjectArgProvider(ArgumentProvider):
Expand All @@ -262,7 +298,7 @@ class ObjectArgProvider(ArgumentProvider):

@cached_property
def rtargs(self):
return [PtrArgument(self.name, self)]
return (PtrArgument(self.name, self),)


def log_args(arguments):
Expand Down
37 changes: 15 additions & 22 deletions devito/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

from devito.exceptions import DevitoError
from devito.logger import warning
from devito.parameters import configuration

backends = {}

Expand Down Expand Up @@ -112,18 +111,16 @@ def set_backend(backend):
"""
global _BackendSelector
if _BackendSelector._backend != void:
raise RuntimeError("The backend can only be set once!")

mod = backends.get(backend)
if mod is None:
try:
# We need to pass a non-empty fromlist so that __import__
# returns the submodule (i.e. the backend) rather than the
# package.
mod = __import__('devito.%s' % backend, fromlist=['None'])
except ImportError as e:
warning('Unable to import backend %s' % backend)
raise e
warning("WARNING: Switching backend to %s" % backend)

try:
# We need to pass a non-empty fromlist so that __import__
# returns the submodule (i.e. the backend) rather than the
# package.
mod = __import__('devito.%s' % backend, fromlist=['None'])
except ImportError as e:
warning('Unable to import backend %s' % backend)
raise e
backends[backend] = mod
_BackendSelector._backend = mod

Expand All @@ -137,14 +134,10 @@ def init_backend(backend):
"""
Initialise Devito: select the backend and other configuration options.
"""
current_backend = get_backend()
if current_backend not in backends_registry:
if backend not in backends_registry:
raise RuntimeError("Calling init() for a different backend is illegal.")
if current_backend == 'void':
try:
set_backend(backend)
except (ImportError, RuntimeError):
raise DevitoError("Couldn't initialize Devito.")


configuration.add('backend', 'core', list(backends_registry))
try:
set_backend(backend)
except (ImportError, RuntimeError):
raise DevitoError("Couldn't initialize Devito.")
6 changes: 0 additions & 6 deletions devito/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,3 @@ def make(loc, args):
'intel-mic': IntelMICCompiler, 'mic': IntelMICCompiler,
'intel-knl': IntelKNLCompiler, 'knl': IntelKNLCompiler,
}


configuration.add('compiler', 'custom', list(compiler_registry),
lambda i: compiler_registry[i]())
configuration.add('openmp', 0, [0, 1], lambda i: bool(i))
configuration.add('debug_compiler', 0, [0, 1], lambda i: bool(i))
9 changes: 4 additions & 5 deletions devito/core/autotuning.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
from operator import mul
import resource

from psutil import cpu_count

from devito.logger import info, info_at
from devito.nodes import Iteration
from devito.parameters import configuration
Expand Down Expand Up @@ -40,12 +38,13 @@ def autotune(operator, arguments, tunable):
timesteps = 1
elif len(sequentials) == 1:
sequential = sequentials[0]
timesteps = sequential.extent(finish=options['at_squeezer'])
start = sequential.dim.rtargs.start.default_value
timesteps = sequential.extent(start=start, finish=options['at_squeezer'])
if timesteps < 0:
timesteps = options['at_squeezer'] - timesteps + 1
info_at("Adjusted auto-tuning timestep to %d" % timesteps)
at_arguments[sequential.dim.symbolic_size.name] = timesteps
if sequential.dim.is_Buffered:
if sequential.dim.is_Stepping:
at_arguments[sequential.dim.parent.symbolic_size.name] = timesteps
else:
info_at("Couldn't understand loop structure, giving up auto-tuning")
Expand Down Expand Up @@ -166,7 +165,7 @@ def more_heuristic_attempts(blocksizes):

options = {
'at_squeezer': 5,
'at_blocksize': sorted({8, 16, 24, 32, 40, 64, 128, cpu_count(logical=False)}),
'at_blocksize': sorted({8, 16, 24, 32, 40, 64, 128}),
'at_stack_limit': resource.getrlimit(resource.RLIMIT_STACK)[0] / 4
}
"""Autotuning options."""
Loading

0 comments on commit a962b0d

Please sign in to comment.