linker, tests

上级 754f6f27
# from features import Tool # from features import Tool
# from utils import AbstractFunctionError
from utils import AbstractFunctionError
class Linker:
def __init__(self, env):
self.env = env
self.thunk = None
def compile(self):
raise AbstractFunctionError()
def run(self):
self.thunk()
def __call__(self):
self.thunk()
class PerformLinker(Linker):
def compile(self):
order = self.env.toposort()
thunks = [op.perform for op in order]
def f():
for thunk in thunks:
thunk()
self.thunk = f
self.order = order
self.thunks = thunks
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)
# class Linker(Tool): # class Linker(Tool):
......
...@@ -4,19 +4,40 @@ from env import Env ...@@ -4,19 +4,40 @@ from env import Env
from utils import AbstractFunctionError from utils import AbstractFunctionError
class Linker: class Prog:
def __init__(self, env): def __init__(self, inputs, outputs, optimizer, linker_class, features = []):
self.env = env self.inputs = inputs
self.thunk = None if isinstance(outputs, dict):
for name, output in outputs.items():
setattr(self, name, output)
self.outputs = outputs.values()
else:
self.outputs = outputs
self.optimizer = optimizer
self.env = Env(self.inputs, self.outputs, features, False)
self.env.add_feature(EquivTool)
self.linker = linker_class(self.env)
def compile(self): def build(self):
raise AbstractFunctionError() self.optimizer.optimize(self.env)
def run(self): def equiv(self, r):
self.thunk() return self.env.equiv(r)
def __getitem__(self, r):
if isinstance(r, str):
return getattr(self, r)
else:
return self.equiv(r)
def __setitem__(self, r, value):
if isinstance(r, tuple):
for a, b in zip(r, value):
self.__setitem__(a, b)
else:
self[r].data = value
......
...@@ -145,38 +145,37 @@ class ResultBase(object): ...@@ -145,38 +145,37 @@ class ResultBase(object):
raise AbstractFunctionError() raise AbstractFunctionError()
# # #
# alloc # # alloc
# # #
def alloc(self): # def alloc(self):
"""Create self.data from data_alloc, and set state to Allocated # """Create self.data from data_alloc, and set state to Allocated
Graph routines like the linker will ask Ops to allocate outputs. The # Graph routines like the linker will ask Ops to allocate outputs. The
Ops, in turn, usually call this function. Results that are involved in # Ops, in turn, usually call this function. Results that are involved in
destroy maps and view maps are exceptions to the usual case. # destroy maps and view maps are exceptions to the usual case.
""" # """
self.data = self.data_alloc() #might raise exception # self.data = self.data_alloc() #might raise exception
self.state = Allocated # self.state = Allocated
def data_alloc(self): # def data_alloc(self):
"""(abstract) Return an appropriate _data based on self. # """(abstract) Return an appropriate _data based on self.
If a subclass overrides this function, then that overriding # If a subclass overrides this function, then that overriding
implementation will be used in alloc() to produce a data object. # implementation will be used in alloc() to produce a data object.
""" # """
raise AbstractFunctionError() # raise AbstractFunctionError()
# #
# C code generators # C code generators
# #
def c_type(self): def c_declare(self):
""" """
Return a string naming the C type that Ops must use to manipulate Declares variables that will be instantiated by c_data_extract.
this Result.
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
...@@ -185,23 +184,39 @@ class ResultBase(object): ...@@ -185,23 +184,39 @@ class ResultBase(object):
PyObject* py_%(name)s = PyList_GET_ITEM(%(name)s_storage, 0); PyObject* py_%(name)s = PyList_GET_ITEM(%(name)s_storage, 0);
Py_XINCREF(py_%(name)s); Py_XINCREF(py_%(name)s);
""" """
return self.c_data_extract() + get_from_list return get_from_list + self.c_data_extract()
def c_data_extract(self): def c_data_extract(self):
""" """
The code returned from this function must be templated using # The code returned from this function must be templated using
"%(name)s", representing the name that the caller wants to # "%(name)s", representing the name that the caller wants to
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 declare a # variable called "py_%(name)s" and this code must declare a
variable named "%(name)s" of type "%(type)s" where "%(type)s" # variable named "%(name)s" of type "%(type)s" where "%(type)s"
will be replaced by the return value of # will be replaced by the return value of
self.c_type(). Additional variables and typedefs can be # self.c_type(). Additional variables and typedefs may not be
produced. If the data is improper, set an appropriate error # produced. If the data is improper, set an appropriate error
message and insert "%(fail)s". # message and insert "%(fail)s".
""" """
raise AbstractFunctionError() raise AbstractFunctionError()
def c_sync(self, var_name): def c_cleanup(self):
decref = """
Py_XDECREF(py_%(name)s);
"""
return self.c_data_cleanup() + decref
def c_data_cleanup(self):
"""
This returns C code that should deallocate whatever
c_data_extract allocated or decrease the reference counts. Do
not decrease py_%(name)s's reference count.
Note: EITHER c_cleanup OR c_sync will be called.
"""
raise AbstractFunctionError()
def c_sync(self):
set_in_list = """ set_in_list = """
PyList_SET_ITEM(%(name)s_storage, 0, py_%(name)s); PyList_SET_ITEM(%(name)s_storage, 0, py_%(name)s);
Py_XDECREF(py_%(name)s); Py_XDECREF(py_%(name)s);
...@@ -231,6 +246,13 @@ class ResultBase(object): ...@@ -231,6 +246,13 @@ class ResultBase(object):
""" """
return [] return []
def c_support(self):
"""
Return utility code for use by this Result or Ops manipulating this
Result.
"""
raise AbstractFunctionError()
# #
# name # name
# #
...@@ -277,7 +299,8 @@ class ResultBase(object): ...@@ -277,7 +299,8 @@ class ResultBase(object):
def same_properties(self, other): def same_properties(self, other):
raise AbstractFunction() raise AbstractFunction()
################# #################
# NumpyR Compatibility # NumpyR Compatibility
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论