diff --git a/hysop/backend/device/opencl/opencl_array_backend.py b/hysop/backend/device/opencl/opencl_array_backend.py index 6c7bbc479bc663c17f35f6a4e00b9a02cba26ee3..c2f74efb05d1c5266ef6901821a1b25ffd77b5ca 100644 --- a/hysop/backend/device/opencl/opencl_array_backend.py +++ b/hysop/backend/device/opencl/opencl_array_backend.py @@ -301,6 +301,24 @@ class OpenClArrayBackend(ArrayBackend): return not (self == other) def __hash__(self): return id(self._context) ^ id(self._default_queue) ^ id(self.allocator) ^ id(self.host_array_backend) + + + __backends = {} + + @classmethod + def get_or_create(cls, cl_env, queue, allocator, + host_array_backend=None): + from hysop.core.arrays.all import HostArrayBackend, \ + default_host_array_backend + host_array_backend = first_not_None(host_array_backend, default_host_array_backend) + key = (cl_env, queue, allocator, host_array_backend) + if key in cls.__backends: + return cls.__backends[key] + else: + obj = cls(cl_env=cl_env, queue=queue, allocator=allocator, + host_array_backend=host_array_backend) + cls.__backends[key] = obj + return obj def __init__(self, cl_env=None, queue=None, allocator=None, host_array_backend=None): diff --git a/hysop/backend/host/host_array_backend.py b/hysop/backend/host/host_array_backend.py index 98f349a0f53980ac9b88fd4f6111ba73ebf1d5c2..b15be25943177cf86e59a865f31df6e30e49cb3e 100644 --- a/hysop/backend/host/host_array_backend.py +++ b/hysop/backend/host/host_array_backend.py @@ -66,6 +66,20 @@ class HostArrayBackend(ArrayBackend): Host array backend. """ + __backends = {} + + @classmethod + def get_or_create(cls, allocator): + from hysop.backend.host.host_allocator import default_host_allocator + allocator = first_not_None(allocator, default_host_allocator) + key = (allocator,) + if key in cls.__backends: + return cls.__backends[key] + else: + obj = cls(allocator=allocator) + cls.__backends[key] = obj + return obj + def __init__(self, allocator, **kwds): check_instance(allocator, AllocatorBase) assert allocator.is_on_host(), 'allocator does not allocate buffers on host.' diff --git a/hysop/core/arrays/array_backend.py b/hysop/core/arrays/array_backend.py index 8f7cc823457ed8d60cc9b3731b2629bc85d3dc95..72c7491d9ac37b57230c7005eb8bf0e66bc4dad3 100644 --- a/hysop/core/arrays/array_backend.py +++ b/hysop/core/arrays/array_backend.py @@ -83,6 +83,10 @@ class ArrayBackend(TaggedObject): """ If set to true, prints all wrapped calls and arguments conversion. """ + + @classmethod + def get_or_create(cls, **kwds): + cls._not_implemented_yet('get_or_create') @staticmethod def get_alignment_and_size(shape, dtype, min_alignment=None): diff --git a/hysop/core/graph/computational_graph.py b/hysop/core/graph/computational_graph.py index c47877b82edec632927a0ec000a9b29b710a795a..9f33a49e846d820559a13764173496acfce7616a 100644 --- a/hysop/core/graph/computational_graph.py +++ b/hysop/core/graph/computational_graph.py @@ -65,47 +65,91 @@ class ComputationalGraph(ComputationalGraphNode): self._profiler += node._profiler def field_requirements_report(self, requirements): - inputs = {} - sinputs = '' + inputs, outputs = {}, {} + sinputs, soutputs = {}, {} def sorted_reqs(reqs): return sorted(reqs, key=lambda x: \ '{}::{}'.format(x.operator.name, x.field.name)) for field, mreqs in requirements.input_field_requirements.iteritems(): for td, reqs in mreqs.requirements.iteritems(): for req in reqs: - if self.__FORCE_REPORTS__ or __DEBUG__ or (__VERBOSE__ and not req.is_default()): - inputs.setdefault(td, {}).setdefault(field, []).append(req) + inputs.setdefault(td, {}).setdefault(field, []).append(req) for td, td_reqs in inputs.iteritems(): - sinputs += '\n {}'.format(td) + sin = sinputs.setdefault(td, []) for field, reqs in td_reqs.iteritems(): for req in sorted_reqs(reqs): - sinputs += '\n *{}'.format(req) - - outputs = {} - soutputs = '' + opname = getattr(req.operator, 'name', 'UnknownOperator') + fname = getattr(req.field, 'name', 'UnknownField') + min_ghosts=req.ghost_str(req.min_ghosts) + max_ghosts=req.ghost_str(req.max_ghosts+1) + ghosts = '{}<=ghosts<{}'.format(min_ghosts, max_ghosts) + can_split=req.can_split.view(npw.int8) + basis='{}'.format(','.join(str(basis)[:3] for basis in req.basis)) \ + if req.basis else 'ANY' + tstates='{}'.format(','.join(str(ts) for ts in req.tstates)) \ + if req.tstates else 'ANY' + sin.append( (opname, fname, ghosts, basis, tstates) ) for field, mreqs in requirements.output_field_requirements.iteritems(): for td, reqs in mreqs.requirements.iteritems(): for req in reqs: - if self.__FORCE_REPORTS__ or __DEBUG__ or (__VERBOSE__ and not req.is_default()): - outputs.setdefault(td, {}).setdefault(field, []).append(req) + outputs.setdefault(td, {}).setdefault(field, []).append(req) for td, td_reqs in outputs.iteritems(): - soutputs += '\n {}'.format(td) + sin = soutputs.setdefault(td, []) for field, reqs in td_reqs.iteritems(): for req in sorted_reqs(reqs): - soutputs += '\n *{}'.format(req) - - ss = '== ComputationalGraph {} field requirements report =='.format(self.name) + opname = getattr(req.operator, 'name', 'UnknownOperator') + fname = getattr(req.field, 'name', 'UnknownField') + min_ghosts=req.ghost_str(req.min_ghosts) + max_ghosts=req.ghost_str(req.max_ghosts+1) + ghosts = '{}<=ghosts<{}'.format(min_ghosts, max_ghosts) + can_split=req.can_split.view(npw.int8) + basis='{}'.format(','.join(str(basis)[:3] for basis in req.basis)) \ + if req.basis else 'ANY' + tstates='{}'.format(','.join(str(ts) for ts in req.tstates)) \ + if req.tstates else 'ANY' + sin.append( (opname, fname, ghosts, basis, tstates) ) + + titles = [[('OPERATOR', 'FIELD', 'GHOSTS', 'BASIS', 'TSTATES')]] + name_size = max(len(s[0]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) + field_size = max(len(s[1]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) + ghosts_size = max(len(s[2].decode('utf-8').replace(u'\u221e', u' ')) \ + for ss in sinputs.values()+soutputs.values()+titles for s in ss) + basis_size = max(len(s[3]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) + tstates_size = max(len(s[4]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) + + template = '\n {:<{name_size}} {:^{field_size}} {:^{ghosts_size}} {:^{basis_size}} {:^{tstates_size}}' + + ss= '>INPUTS:' if sinputs: - ss+= '\n>INPUTS:{}'.format(sinputs) + for (td, sreqs) in sinputs.iteritems(): + ss+='\n {}'.format(td) + ss+= template.format(*titles[0][0], + name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, + basis_size=basis_size, tstates_size=tstates_size) + for (opname, fname, ghosts, basis, tstates) in sreqs: + ss+=template.format( + opname, fname, ghosts, basis, tstates, + name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, + basis_size=basis_size, tstates_size=tstates_size) else: - ss+= '\n>INPUTS: None' + ss+=' None' + ss+= '\n>OUTPUTS:' if soutputs: - ss+= '\n>OUTPUTS:{}'.format(soutputs) + for (td, sreqs) in soutputs.iteritems(): + ss+='\n {}'.format(td) + ss+= template.format(*titles[0][0], + name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, + basis_size=basis_size, tstates_size=tstates_size) + for (opname, fname, ghosts, basis, tstates) in sreqs: + ss+=template.format( + opname, fname, ghosts, basis, tstates, + name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, + basis_size=basis_size, tstates_size=tstates_size) else: - ss+= '\n>OUTPUTS: None' - l = len('== ComputationalGraph {} field requirements report =='.format(self.name)) - ss += '\n'+l*'='+'\n' - return ss + ss+=' None' + + title = ' ComputationalGraph {} field requirements report '.format(self.name) + return '\n{}\n'.format(framed_str(title=title, msg=ss)) def domain_report(self): domains = self.get_domains() @@ -133,7 +177,7 @@ class ComputationalGraph(ComputationalGraphNode): ops.setdefault(domain, []).append( (op.name, inputs, outputs, type(op).__name__) ) if (None in domains): - opeators = domains[None] + operators = domains[None] for op in sorted(operators, key=lambda x: x.name): pinputs = ','.join( sorted([p for p in op.input_params])) poutputs =','.join( sorted([p for p in op.output_params])) diff --git a/hysop/core/memory/memory_request.py b/hysop/core/memory/memory_request.py index ae40fe091ccb09578dd7538da9b478ff6a5bb115..067711285eab618eed72110d060d8468026b1c1c 100644 --- a/hysop/core/memory/memory_request.py +++ b/hysop/core/memory/memory_request.py @@ -354,7 +354,7 @@ class MultipleOperatorMemoryRequests(object): def __str__(self): s='' - for backend, backend_requests in self._all_requests_per_backend.iteritems(): + for (backend, backend_requests) in self._all_requests_per_backend.iteritems(): kind = backend.kind if kind == Backend.OPENCL: precision = ' on device {}'.format(backend.device.name) diff --git a/hysop/domain/box.py b/hysop/domain/box.py index 83a1359b908cc9bc943b65afc61f4f5d355be2ea..d6b97d93742151d99f5bb7e37f33380ffeb3058c 100644 --- a/hysop/domain/box.py +++ b/hysop/domain/box.py @@ -76,8 +76,8 @@ class BoxView(DomainView): """ Return a short description of this Box as a string. """ - return 'Box(tag={}, O=[{}], L=[{}], BC=[{}], current_task={})'.format( - self.tag, + return '{} (O=[{}], L=[{}], BC=[{}], current_task={})'.format( + self.full_tag, ','.join(('{:1.1f}'.format(val) for val in self.origin)), ','.join(('{:1.1f}'.format(val) for val in self.length)), ','.join(('{}/{}'.format(str(lb)[:3],str(rb)[:3]) for (lb,rb) in \ diff --git a/hysop/fields/field_requirements.py b/hysop/fields/field_requirements.py index 07dd797dc7f8f466e2a42fba76db464e427051b1..2c1214bdd2a153a93e4fa9b650ba4b8f7e15f2b6 100644 --- a/hysop/fields/field_requirements.py +++ b/hysop/fields/field_requirements.py @@ -107,17 +107,18 @@ class DiscreteFieldRequirements(object): return id(self.operator) ^ id(self.variables) ^ id(self.field) ^ \ hash((to_tuple(self.min_ghosts), to_tuple(self.max_ghosts), self.basis, self.tstates)) + + def ghost_str(self, array): + inf = u'+\u221e' + vals = [u''+str(x) if np.isfinite(x) else inf for x in array] + return u'[{}]'.format(u','.join(vals)).encode('utf-8').strip() def __str__(self): - def arraystr(array): - inf = u'+\u221e' - vals = [u''+str(x) if np.isfinite(x) else inf for x in array] - return u'[{}]'.format(u','.join(vals)).encode('utf-8').strip() return '{:15s} {:>10s}<=ghosts<{:<10s} can_split={} basis={} tstates={}'.format( '{}::{}'.format(getattr(self.operator, 'name', 'UnknownOperator'), getattr(self.field, 'name', 'UnknownField')), - arraystr(self.min_ghosts), arraystr(self.max_ghosts+1), + self.ghost_str(self.min_ghosts), self.ghost_str(self.max_ghosts+1), self.can_split.view(np.int8), '[{}]'.format(','.join(str(basis)[:3] for basis in self.basis)) \ if self.basis else 'ANY', diff --git a/hysop/tools/string_utils.py b/hysop/tools/string_utils.py index 6aa9c2d9789857f7d02ce870905b71bfd3ba6f8d..e9c1214aefa45b8282a44e0d88a872243db6ba5f 100644 --- a/hysop/tools/string_utils.py +++ b/hysop/tools/string_utils.py @@ -33,7 +33,8 @@ def framed_str(title, msg, c='=', at_border=2): Format a message to fit between two separation lines containing a title. """ - length = max(len(m) for m in msg.split('\n')) + clean = lambda s: re.sub(r'[^\x00-\x7f]','',s) + length = max(len(clean(m)) for m in msg.split('\n')) title = c*at_border + title + c*at_border header = title + c*max(0, length-len(title)) footer = c*len(header) diff --git a/hysop/topology/cartesian_topology.py b/hysop/topology/cartesian_topology.py index e276861dcfbe751d57c0d9e2137aac64821e9c54..f18248f408efd79967d3be7c60431b5359374395 100644 --- a/hysop/topology/cartesian_topology.py +++ b/hysop/topology/cartesian_topology.py @@ -363,7 +363,9 @@ class CartesianTopologyView(TopologyView): s='CartesianTopology[tag={}, domain={}, task_id={}, pcoords={}, pshape={}, ' s+='distr.={}, periods={}, shape={}, ghosts={}]' s = s.format( - self.tag, self.domain.domain.full_tag, self.task_id, + self.tag, + self.domain.domain.full_tag, + self.task_id, self.proc_coords, self.proc_shape, '[{}]'.format(','.join('T' if per else 'F' for per in self.is_distributed)), '[{}]'.format(','.join('T' if per else 'F' for per in self.is_periodic)), diff --git a/hysop/topology/topology.py b/hysop/topology/topology.py index c69ebe237722528dc1828d1adb8c3e989882409b..9f34cacd762a971e1441aabec1c4d45d02115175 100644 --- a/hysop/topology/topology.py +++ b/hysop/topology/topology.py @@ -356,15 +356,13 @@ class Topology(RegisteredObject): assert (cl_env is None) assert (queue is None) from hysop.core.arrays.all import HostArrayBackend - from hysop.backend.host.host_allocator import default_host_allocator - allocator = allocator or default_host_allocator - backend = HostArrayBackend(allocator) + backend = HostArrayBackend.get_or_create(allocator) elif (backend == Backend.OPENCL): from hysop.backend.device.opencl.opencl_tools import get_or_create_opencl_env from hysop.core.arrays.all import OpenClArrayBackend cl_env = cl_env or get_or_create_opencl_env(mpi_params) assert cl_env.mpi_params == mpi_params - backend = OpenClArrayBackend(cl_env=cl_env, queue=queue, allocator=allocator) + backend = OpenClArrayBackend.get_or_create(cl_env=cl_env, queue=queue, allocator=allocator) assert backend.cl_env.mpi_params == mpi_params else: msg = 'Unsupported backend {}.'.format(backend)