提交 c23da129 authored 作者: Olivier Breuleux's avatar Olivier Breuleux

added documentation for almost everything

上级 9bc72d95
...@@ -103,7 +103,7 @@ class Tool(Feature): ...@@ -103,7 +103,7 @@ class Tool(Feature):
def uniq_features(_features, *_rest): def uniq_features(_features, *_rest):
"""Return a list such that no element is a subclass of another""" """Return a list such that no element is a subclass of another"""
# used in Env.__init__ to # used in Env.__init__
features = [x for x in _features] features = [x for x in _features]
for other in _rest: for other in _rest:
features += [x for x in other] features += [x for x in other]
......
# from features import Tool
from utils import AbstractFunctionError from utils import AbstractFunctionError
import utils import utils
...@@ -10,8 +8,16 @@ import traceback ...@@ -10,8 +8,16 @@ import traceback
__excepthook = sys.excepthook __excepthook = sys.excepthook
def thunk_hook(type, value, trace): def thunk_hook(type, value, trace):
"""
This function is meant to replace excepthook and do some
special work if the exception value has a __thunk_trace__
field. In that case, it retrieves the field, which should
contain a trace as returned by traceback.extract_stack,
and prints it out on stderr.
The normal excepthook is then called.
"""
if hasattr(value, '__thunk_trace__'): if hasattr(value, '__thunk_trace__'):
# such a hack :(
trace2 = value.__thunk_trace__ trace2 = value.__thunk_trace__
if trace2 is None: if trace2 is None:
print>>sys.stderr, "Could not find where this Op was defined." print>>sys.stderr, "Could not find where this Op was defined."
...@@ -24,22 +30,6 @@ def thunk_hook(type, value, trace): ...@@ -24,22 +30,6 @@ def thunk_hook(type, value, trace):
__excepthook(type, value, trace) __excepthook(type, value, trace)
sys.excepthook = thunk_hook sys.excepthook = thunk_hook
# __excepthook = sys.excepthook
# def thunk_hook(type, value, trace):
# if len(value.args) > 0 and hasattr(value[0], '__thunk_trace__'):
# # such a hack :(
# trace2 = value[0].__thunk_trace__ #.exc_info
# if trace2 is None:
# print>>sys.stderr, "Could not find where this Op was defined."
# print>>sys.stderr, " * You might have instantiated this Op directly instead of using a constructor."
# print>>sys.stderr, " * The Op you constructed might have been optimized. Try turning off optimizations."
# elif trace2:
# print>>sys.stderr, "Definition in: "
# for line in traceback.format_list(trace2):
# print>>sys.stderr, line,
# __excepthook(type, value, trace)
# sys.excepthook = thunk_hook
class Linker: class Linker:
...@@ -108,6 +98,10 @@ class Linker: ...@@ -108,6 +98,10 @@ class Linker:
class PerformLinker(Linker): class PerformLinker(Linker):
"""
Basic Linker subclass that calls the perform method on each op in
the env in the order given by env.toposort.
"""
def make_thunk(self, inplace = False): def make_thunk(self, inplace = False):
if inplace: if inplace:
...@@ -133,127 +127,64 @@ class PerformLinker(Linker): ...@@ -133,127 +127,64 @@ class PerformLinker(Linker):
return f, env.inputs, env.outputs return f, env.inputs, env.outputs
class ProfilePerformLinker(Linker):
def compile(self):
order = self.env.toposort()
thunks = [op.perform for op in order]
self.n_calls = 0
self.n_thunks = 0
self.times = [0.0 for op in self.order]
def f():
for thunk in thunks:
thunk()
self.thunk = f
self.order = order
self.thunks = thunks
def slow_call(self):
"""Run the program, timing each thunk."""
for i, thunk in enumerate(self.thunks):
start_time = time.time()
thunk()
self.times[i] += time.time() - start_time
self.n_thunks += 1
self.n_calls += 1
def fast_call(self):
"""Run the program, but only time the entire loop."""
start_time = time.time()
for thunk in self.thunks:
thunk()
self.n_thunks += len(self.thunks)
self.n_calls += 1
self.times[0] += time.time() - start_time
__call__ = slow_call
def dump(self, proportion=True):
"""Print statistics accumulated so far."""
total_time = sum(self.times)
print self.n_calls, 'calls took', total_time, 'seconds to evaluate',
print self.n_thunks, 'thunks'
if 0:
print 'Proportion of CPU per op'
for op, t in zip(self.order, self.times):
s_op = str(op).split()[0][1:]
print " %-35s %4.5f"% (s_op, t/total_time)
print 'Proportion of CPU per op class'
dct = {}
for op, t in zip(self.order, self.times):
s_op = str(op).split()[0][1:]
dct[s_op] = dct.get(s_op, 0.0) + t
for t, s_op in reversed(sorted([(t,op) for op, t in dct.items()])):
if proportion:
print " %-35s %4.5f"% (s_op, t/total_time)
else:
print " %-35s %4.5f"% (s_op, t)
### PROFILEPERFORMLINKER USES COMPLETELY OUTDATED INTERFACE - FIX ###
# class Linker(Tool): # class ProfilePerformLinker(Linker):
# def compile(self): # def compile(self):
# raise AbstractFunctionError() # order = self.env.toposort()
# thunks = [op.perform for op in order]
# def run(self): # self.n_calls = 0
# raise AbstractFunctionError() # self.n_thunks = 0
# self.times = [0.0 for op in self.order]
# def f():
# for thunk in thunks:
# thunk()
# self.thunk = f
# self.order = order
# self.thunks = thunks
# def slow_call(self):
# """Run the program, timing each thunk."""
# for i, thunk in enumerate(self.thunks):
# start_time = time.time()
# def perform_linker(env, target = None):
# order = env.toposort()
# thunks = [op.perform for op in order]
# def ret():
# for thunk in thunks:
# thunk() # thunk()
# if not target: # self.times[i] += time.time() - start_time
# return ret # self.n_thunks += 1
# else: # self.n_calls += 1
# raise NotImplementedError("Cannot write thunk representation to a file.")
# def fast_call(self):
# """Run the program, but only time the entire loop."""
# def perform_linker_nochecks(env, target = None): # start_time = time.time()
# order = env.toposort() # for thunk in self.thunks:
# thunks = [op._perform for op in order]
# def ret():
# for thunk in thunks:
# thunk() # thunk()
# if not target: # self.n_thunks += len(self.thunks)
# return ret # self.n_calls += 1
# else: # self.times[0] += time.time() - start_time
# raise NotImplementedError("Cannot write thunk representation to a file.")
# __call__ = slow_call
# def cthunk_linker(env): # def dump(self, proportion=True):
# order = env.toposort() # """Print statistics accumulated so far."""
# thunks = [] # total_time = sum(self.times)
# cstreak = [] # print self.n_calls, 'calls took', total_time, 'seconds to evaluate',
# print self.n_thunks, 'thunks'
# def append_cstreak():
# if cstreak: # if 0:
# thunks.append(cutils.create_cthunk_loop(*cstreak)) # print 'Proportion of CPU per op'
# cstreak = [] # for op, t in zip(self.order, self.times):
# def ret(): # s_op = str(op).split()[0][1:]
# for thunk in thunks: # print " %-35s %4.5f"% (s_op, t/total_time)
# thunk()
# print 'Proportion of CPU per op class'
# for op in order: # dct = {}
# if hasattr(op, 'cthunk'): # for op, t in zip(self.order, self.times):
# cstreak.append(op.cthunk()) # s_op = str(op).split()[0][1:]
# else: # dct[s_op] = dct.get(s_op, 0.0) + t
# append_cstreak() # for t, s_op in reversed(sorted([(t,op) for op, t in dct.items()])):
# thunks.append(op.perform) # if proportion:
# print " %-35s %4.5f"% (s_op, t/total_time)
# if len(thunks) == 1: # else:
# return thunks[0] # print " %-35s %4.5f"% (s_op, t)
# else:
# return ret
### CODE CAN BE SIMPLIFIED IF WE ONLY KEEP BUILD MODE ###
import utils import utils
import traceback import traceback
from op import Op from op import Op
...@@ -50,7 +52,11 @@ def add_modal_members(cls, *members): ...@@ -50,7 +52,11 @@ def add_modal_members(cls, *members):
def attach_trace(op): def attach_trace(op):
stack = traceback.extract_stack()[:-3] """
Extracts the stack trace at the point of construction and
puts it in the op's trace field.
"""
stack = traceback.extract_stack()[:-3] # we discard 3 levels
op.trace = stack op.trace = stack
def build_mode(op): def build_mode(op):
......
...@@ -4,7 +4,6 @@ Contains the Op class, which is the base interface for all operations ...@@ -4,7 +4,6 @@ Contains the Op class, which is the base interface for all operations
compatible with gof's graph manipulation routines. compatible with gof's graph manipulation routines.
""" """
# from result import BrokenLinkError
from utils import ClsInit, all_bases, all_bases_collect, AbstractFunctionError from utils import ClsInit, all_bases, all_bases_collect, AbstractFunctionError
import graph import graph
...@@ -25,9 +24,6 @@ class Op(object): ...@@ -25,9 +24,6 @@ class Op(object):
list of at most one Op, its owner. It is the responsibility of the list of at most one Op, its owner. It is the responsibility of the
Op to ensure that it owns its outputs and it is encouraged (though Op to ensure that it owns its outputs and it is encouraged (though
not required) that it creates them. not required) that it creates them.
After construction, self.inputs and self.outputs should only be
modified through the set_input and set_output methods.
""" """
__slots__ = ['_inputs', '_outputs'] __slots__ = ['_inputs', '_outputs']
...@@ -42,13 +38,13 @@ class Op(object): ...@@ -42,13 +38,13 @@ class Op(object):
raise AttributeError("Op does not have a default output.") raise AttributeError("Op does not have a default output.")
out = property(default_output, out = property(default_output,
doc = "Same as self.outputs[0] if this Op's has_default_output field is True.") doc = "Same as self.outputs[0] if this Op's has_default_output field is True.")
def __init__(self, *inputs): def __init__(self, *inputs):
# this might be a bit brainless
raise AbstractFunctionError("Op is an abstract class. Its constructor does nothing, you must override it.") raise AbstractFunctionError("Op is an abstract class. Its constructor does nothing, you must override it.")
def get_input(self, i): def get_input(self, i):
return self._inputs[i] return self._inputs[i]
def set_input(self, i, new): def set_input(self, i, new):
...@@ -121,9 +117,8 @@ class Op(object): ...@@ -121,9 +117,8 @@ class Op(object):
def perform(self): def perform(self):
""" """
(abstract) Performs the computation associated to this Op, Performs the computation associated to this Op and places the
places the result(s) in the output Results and gives them result(s) in the output Results.
the Computed status.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -142,11 +137,12 @@ class Op(object): ...@@ -142,11 +137,12 @@ class Op(object):
def c_validate_update(self): def c_validate_update(self):
""" """
Returns C code that checks that the inputs to this function Returns templated C code that checks that the inputs to this
can be worked on. If a failure occurs, set an Exception function can be worked on. If a failure occurs, set an
and insert "%(fail)s". Exception and insert "%(fail)s".
You may use the variable names defined by c_var_names() You may use the variable names defined by c_var_names() in
the template.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -158,11 +154,12 @@ class Op(object): ...@@ -158,11 +154,12 @@ class Op(object):
def c_code(self): def c_code(self):
""" """
Returns C code that does the computation associated to this Returns templated C code that does the computation associated
Op. You may assume that input validation and output allocation to this Op. You may assume that input validation and output
have already been done. allocation have already been done.
You may use the variable names defined by c_var_names() You may use the variable names defined by c_var_names() in
the templates.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -193,7 +190,8 @@ class Op(object): ...@@ -193,7 +190,8 @@ class Op(object):
def c_support_code(self): def c_support_code(self):
""" """
Return utility code for use by this Op. Return utility code for use by this Op. It may refer to support code
defined for its input Results.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
......
...@@ -17,6 +17,8 @@ __all__ = ['ResultBase', ...@@ -17,6 +17,8 @@ __all__ = ['ResultBase',
] ]
### CLEANUP - DO WE REALLY EVEN THE STATE ANYMORE? ###
class StateError(Exception): class StateError(Exception):
"""The state of the Result is a problem""" """The state of the Result is a problem"""
...@@ -46,14 +48,8 @@ class ResultBase(object): ...@@ -46,14 +48,8 @@ class ResultBase(object):
index - (ro) index - (ro)
data - (rw) : calls data_filter when setting data - (rw) : calls data_filter when setting
Methods:
alloc() - create storage in data, suitable for use by C ops.
(calls data_alloc)
Abstract Methods: Abstract Methods:
data_filter data_filter
data_alloc
""" """
__slots__ = ['_role', '_data', 'state', '_name'] __slots__ = ['_role', '_data', 'state', '_name']
...@@ -95,7 +91,7 @@ class ResultBase(object): ...@@ -95,7 +91,7 @@ class ResultBase(object):
return self._role[0] return self._role[0]
owner = property(__get_owner, owner = property(__get_owner,
doc = "Op of which this Result is an output, or None if role is None") doc = "Op of which this Result is an output, or None if role is None")
# #
# index # index
...@@ -106,7 +102,7 @@ class ResultBase(object): ...@@ -106,7 +102,7 @@ class ResultBase(object):
return self._role[1] return self._role[1]
index = property(__get_index, index = property(__get_index,
doc = "position of self in owner's outputs, or None if role is None") doc = "position of self in owner's outputs, or None if role is None")
# #
...@@ -117,6 +113,9 @@ class ResultBase(object): ...@@ -117,6 +113,9 @@ class ResultBase(object):
return self._data[0] return self._data[0]
def __set_data(self, data): def __set_data(self, data):
"""
Filters the data provided and sets the result in the storage.
"""
if data is self._data[0]: if data is self._data[0]:
return return
if data is None: if data is None:
...@@ -134,12 +133,12 @@ class ResultBase(object): ...@@ -134,12 +133,12 @@ class ResultBase(object):
doc = "The storage associated with this result") doc = "The storage associated with this result")
def filter(self, data): def filter(self, data):
"""(abstract) Raise an exception if the data is not of an """
acceptable type. Raise an exception if the data is not of an acceptable type.
If a subclass overrides this function, __set_data will use If a subclass overrides this function, __set_data will use it
it to check that the argument can be used properly. This gives to check that the argument can be used properly. This gives a
a subclass the opportunity to ensure that the contents of subclass the opportunity to ensure that the contents of
self._data remain sensible. self._data remain sensible.
Returns data or an appropriately wrapped data. Returns data or an appropriately wrapped data.
...@@ -152,6 +151,11 @@ class ResultBase(object): ...@@ -152,6 +151,11 @@ class ResultBase(object):
# #
def c_is_simple(self): def c_is_simple(self):
"""
A hint to tell the compiler that this type is a builtin C
type or a small struct and that its memory footprint is
negligible.
"""
return False return False
def c_declare(self): def c_declare(self):
...@@ -167,8 +171,8 @@ class ResultBase(object): ...@@ -167,8 +171,8 @@ class ResultBase(object):
call this Result. The Python object self.data is in a call this Result. The Python object self.data is in a
variable called "py_%(name)s" and this code must set the variable called "py_%(name)s" and this code must set the
variables declared by c_declare to something representative variables declared by c_declare to something representative
of py_%(name)s. If the data is improper, set an appropriate error of py_%(name)s. If the data is improper, set an appropriate
message and insert "%(fail)s". exception and insert "%(fail)s".
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -177,8 +181,6 @@ class ResultBase(object): ...@@ -177,8 +181,6 @@ class ResultBase(object):
This returns C code that should deallocate whatever This returns C code that should deallocate whatever
c_data_extract allocated or decrease the reference counts. Do c_data_extract allocated or decrease the reference counts. Do
not decrease py_%(name)s's reference count. not decrease py_%(name)s's reference count.
Note: EITHER c_cleanup OR c_sync will be called.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -188,7 +190,7 @@ class ResultBase(object): ...@@ -188,7 +190,7 @@ class ResultBase(object):
representing the name that the caller wants to call this Result. representing the name that the caller wants to call this Result.
The returned code may set "py_%(name)s" to a PyObject* and that PyObject* The returned code may set "py_%(name)s" to a PyObject* and that PyObject*
will be accessible from Python via result.data. Do not forget to adjust will be accessible from Python via result.data. Do not forget to adjust
reference counts if "py_%(name)s" is changed from its original value! reference counts if "py_%(name)s" is changed from its original value.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -273,6 +275,10 @@ class ResultBase(object): ...@@ -273,6 +275,10 @@ class ResultBase(object):
class PythonResult(ResultBase): class PythonResult(ResultBase):
"""
Represents a generic Python object. The object is available
through %(name)s.
"""
def c_declare(self): def c_declare(self):
return """ return """
...@@ -296,6 +302,7 @@ class PythonResult(ResultBase): ...@@ -296,6 +302,7 @@ class PythonResult(ResultBase):
py_%(name)s = %(name)s; py_%(name)s = %(name)s;
Py_XINCREF(py_%(name)s); Py_XINCREF(py_%(name)s);
""" """
def same_properties(self, other): def same_properties(self, other):
return False return False
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论