提交 e552cf70 authored 作者: Frederic Bastien's avatar Frederic Bastien

When compiling, take the compile lock when we compile the first c code.

So if tall c code is already compiled, we don't take it!
上级 1cde57d4
...@@ -33,6 +33,7 @@ from env import Env ...@@ -33,6 +33,7 @@ from env import Env
import graph import graph
import link import link
import utils import utils
import op
from compilelock import get_lock, release_lock from compilelock import get_lock, release_lock
...@@ -696,7 +697,7 @@ class CLinker(link.Linker): ...@@ -696,7 +697,7 @@ class CLinker(link.Linker):
except utils.MethodNotDefined: pass except utils.MethodNotDefined: pass
return list(set(ret)) return list(set(ret))
def __compile__(self, input_storage = None, output_storage = None): def __compile__(self, input_storage = None, output_storage = None, keep_lock=False):
"""WRITEME """WRITEME
Compiles this linker's env. Compiles this linker's env.
...@@ -724,7 +725,8 @@ class CLinker(link.Linker): ...@@ -724,7 +725,8 @@ class CLinker(link.Linker):
output_storage = tuple(output_storage) output_storage = tuple(output_storage)
thunk = self.cthunk_factory(error_storage, thunk = self.cthunk_factory(error_storage,
input_storage, input_storage,
output_storage) output_storage,
keep_lock=keep_lock)
return thunk, \ return thunk, \
[link.Container(input, storage) for input, storage in zip(self.env.inputs, input_storage)], \ [link.Container(input, storage) for input, storage in zip(self.env.inputs, input_storage)], \
[link.Container(output, storage, True) for output, storage in zip(self.env.outputs, output_storage)], \ [link.Container(output, storage, True) for output, storage in zip(self.env.outputs, output_storage)], \
...@@ -751,7 +753,7 @@ class CLinker(link.Linker): ...@@ -751,7 +753,7 @@ class CLinker(link.Linker):
id += 1 id += 1
return init_tasks, tasks return init_tasks, tasks
def make_thunk(self, input_storage = None, output_storage = None): def make_thunk(self, input_storage = None, output_storage = None, keep_lock=False):
"""WRITEME """WRITEME
Compiles this linker's env and returns a function to perform the Compiles this linker's env and returns a function to perform the
computations, as well as lists of storage cells for both the computations, as well as lists of storage cells for both the
...@@ -775,7 +777,8 @@ class CLinker(link.Linker): ...@@ -775,7 +777,8 @@ class CLinker(link.Linker):
first_output = ostor[0].data first_output = ostor[0].data
""" """
init_tasks, tasks = self.get_init_tasks() init_tasks, tasks = self.get_init_tasks()
cthunk, in_storage, out_storage, error_storage = self.__compile__(input_storage, output_storage) cthunk, in_storage, out_storage, error_storage = self.__compile__(input_storage, output_storage,
keep_lock=keep_lock)
res = _execute(cthunk, init_tasks, tasks, error_storage), in_storage, out_storage res = _execute(cthunk, init_tasks, tasks, error_storage), in_storage, out_storage
return res return res
...@@ -1021,7 +1024,7 @@ class CLinker(link.Linker): ...@@ -1021,7 +1024,7 @@ class CLinker(link.Linker):
return mod return mod
def cthunk_factory(self, error_storage, in_storage, out_storage): def cthunk_factory(self, error_storage, in_storage, out_storage, keep_lock=False):
"""WRITEME """WRITEME
error_storage -> list of length 3 error_storage -> list of length 3
in_storage -> list of lists of length 1, one per input in_storage -> list of lists of length 1, one per input
...@@ -1041,7 +1044,7 @@ class CLinker(link.Linker): ...@@ -1041,7 +1044,7 @@ class CLinker(link.Linker):
#if we can't get a key, then forget the cache mechanism #if we can't get a key, then forget the cache mechanism
module = self.compile_cmodule() module = self.compile_cmodule()
else: else:
module = get_module_cache().module_from_key(key=key, fn=self.compile_cmodule) module = get_module_cache().module_from_key(key=key, fn=self.compile_cmodule, keep_lock=keep_lock)
vars = self.inputs + self.outputs + self.orphans vars = self.inputs + self.outputs + self.orphans
# List of indices that should be ignored when passing the arguments # List of indices that should be ignored when passing the arguments
...@@ -1147,9 +1150,12 @@ class OpWiseCLinker(link.LocalLinker): ...@@ -1147,9 +1150,12 @@ class OpWiseCLinker(link.LocalLinker):
def make_all(self, profiler = None, input_storage = None, output_storage = None): def make_all(self, profiler = None, input_storage = None, output_storage = None):
# Acquire lock on compilation directory, and # The lock will be acquired when we compile the first
# hold it throughout the compilation of all internal nodes. # C code. We will keep the lock untill all the function
get_lock() # compilation will be finished. This allow to don't
# require the lock when all c code are already compiled!
keep_lock=True
orig_n_lock = get_lock.n_lock
try: try:
env = self.env env = self.env
...@@ -1168,6 +1174,11 @@ class OpWiseCLinker(link.LocalLinker): ...@@ -1168,6 +1174,11 @@ class OpWiseCLinker(link.LocalLinker):
node_input_storage = [storage_map[r] for r in node.inputs] node_input_storage = [storage_map[r] for r in node.inputs]
node_output_storage = [storage_map[r] for r in node.outputs] node_output_storage = [storage_map[r] for r in node.outputs]
debug('Compiling node %i of graph' % node_idx) debug('Compiling node %i of graph' % node_idx)
thunk = None
# If the op don't override the c_code function, we don't try
# to generate a cthunk! Otherwise we won't find it in the compilation cache
# and try to compile it. This will get the lock even if we don't need it!
if node.op.c_code.im_func is not op.Op.c_code.im_func:
try: try:
e = Env(*graph.clone(node.inputs, node.outputs)) e = Env(*graph.clone(node.inputs, node.outputs))
if self.allow_gc: if self.allow_gc:
...@@ -1182,12 +1193,20 @@ class OpWiseCLinker(link.LocalLinker): ...@@ -1182,12 +1193,20 @@ class OpWiseCLinker(link.LocalLinker):
debug('Trying CLinker.make_thunk') debug('Trying CLinker.make_thunk')
thunk, node_input_filters, node_output_filters = cl.make_thunk( thunk, node_input_filters, node_output_filters = cl.make_thunk(
input_storage = node_input_storage, input_storage = node_input_storage,
output_storage = node_output_storage) output_storage = node_output_storage,
keep_lock=keep_lock)
assert callable(thunk) assert callable(thunk)
thunk.inputs = node_input_storage thunk.inputs = node_input_storage
thunk.outputs = node_output_storage thunk.outputs = node_output_storage
thunks.append(thunk) thunks.append(thunk)
if keep_lock and get_lock.n_lock > orig_n_lock:
keep_lock=False
do_python_thunk = False
except (NotImplementedError, utils.MethodNotDefined): except (NotImplementedError, utils.MethodNotDefined):
thunk = None
if thunk is None:
if self.fallback_on_perform: if self.fallback_on_perform:
debug('Falling back on perform') debug('Falling back on perform')
p = node.op.perform p = node.op.perform
...@@ -1200,7 +1219,7 @@ class OpWiseCLinker(link.LocalLinker): ...@@ -1200,7 +1219,7 @@ class OpWiseCLinker(link.LocalLinker):
thunk.perform = p thunk.perform = p
thunks.append(thunk) thunks.append(thunk)
else: else:
raise raise NotImplementedError("We where not able to use c_code and perform code for this node", node)
if self.allow_gc: if self.allow_gc:
post_thunk_old_storage.append([storage_map[input] post_thunk_old_storage.append([storage_map[input]
...@@ -1222,7 +1241,9 @@ class OpWiseCLinker(link.LocalLinker): ...@@ -1222,7 +1241,9 @@ class OpWiseCLinker(link.LocalLinker):
finally: finally:
# Release lock on compilation directory. # Release lock on compilation directory.
if get_lock.n_lock > orig_n_lock:
release_lock() release_lock()
assert get_lock.n_lock == orig_n_lock
return f, [link.Container(input, storage) for input, storage in zip(env.inputs, input_storage)], \ return f, [link.Container(input, storage) for input, storage in zip(env.inputs, input_storage)], \
[link.Container(output, storage, True) for output, storage in zip(env.outputs, output_storage)], \ [link.Container(output, storage, True) for output, storage in zip(env.outputs, output_storage)], \
......
...@@ -393,7 +393,7 @@ class ModuleCache(object): ...@@ -393,7 +393,7 @@ class ModuleCache(object):
return too_old_to_use return too_old_to_use
def module_from_key(self, key, fn=None): def module_from_key(self, key, fn=None, keep_lock=False):
""" """
:param fn: a callable object that will return a module for the key (it is called only if the key isn't in :param fn: a callable object that will return a module for the key (it is called only if the key isn't in
the cache). This function will be called with a single keyword argument "location" the cache). This function will be called with a single keyword argument "location"
...@@ -430,12 +430,14 @@ class ModuleCache(object): ...@@ -430,12 +430,14 @@ class ModuleCache(object):
module = fn(location=location) # WILL FAIL FOR BAD C CODE module = fn(location=location) # WILL FAIL FOR BAD C CODE
except Exception, e: except Exception, e:
_rmtree(location) _rmtree(location)
if not keep_lock:
compilelock.release_lock() compilelock.release_lock()
#try: #try:
#except Exception, ee: #except Exception, ee:
#error('failed to cleanup location', location, ee) #error('failed to cleanup location', location, ee)
raise raise
if not keep_lock:
compilelock.release_lock() compilelock.release_lock()
name = module.__file__ name = module.__file__
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论