提交 8bd6e9a3 authored 作者: abergeron's avatar abergeron 提交者: GitHub

Merge pull request #6077 from notoraptor/debug-for-ccode

Add a DEBUG option for C code
...@@ -1074,6 +1074,12 @@ import theano and print the config variable, as in: ...@@ -1074,6 +1074,12 @@ import theano and print the config variable, as in:
reused by Theano. Automatic deletion of those c module 7 days reused by Theano. Automatic deletion of those c module 7 days
after that time. after that time.
.. attribute:: config.cmodule.debug
Bool value, default: ``False``
If True, define a DEBUG macro (if not exists) for any compiled C code.
.. attribute:: config.traceback.limit .. attribute:: config.traceback.limit
Int value, default: 8 Int value, default: 8
......
...@@ -1150,6 +1150,11 @@ AddConfigVar('cmodule.age_thresh_use', ...@@ -1150,6 +1150,11 @@ AddConfigVar('cmodule.age_thresh_use',
IntParam(60 * 60 * 24 * 24, allow_override=False), IntParam(60 * 60 * 24 * 24, allow_override=False),
in_c_key=False) in_c_key=False)
AddConfigVar('cmodule.debug',
"If True, define a DEBUG macro (if not exists) for any compiled C code.",
BoolParam(False),
in_c_key=True)
def default_blas_ldflags(): def default_blas_ldflags():
global numpy global numpy
......
...@@ -910,6 +910,12 @@ class CLinker(link.Linker): ...@@ -910,6 +910,12 @@ class CLinker(link.Linker):
The support code from Variables is added before the support code from Ops.This might contain duplicates. The support code from Variables is added before the support code from Ops.This might contain duplicates.
""" """
ret = [] ret = []
if config.cmodule.debug:
ret.append("""
#ifndef DEBUG
#define DEBUG
#endif
""")
# generic support code # generic support code
for x in [y.type for y in self.variables] + [ for x in [y.type for y in self.variables] + [
y.op for y in self.node_order]: y.op for y in self.node_order]:
......
...@@ -851,19 +851,27 @@ class EnumType(Type, dict): ...@@ -851,19 +851,27 @@ class EnumType(Type, dict):
int constant_3 = CONSTANT_3; // constant_3 == 0 int constant_3 = CONSTANT_3; // constant_3 == 0
int constant_4 = CONSTANT_4; // constant_4 == 1 int constant_4 = CONSTANT_4; // constant_4 == 1
You can also specify a C type for the op param if you want to pass one of these constant values at runtime. You can also specify a C type for the op param. Default C type is ``double``.
Default C type is ``double``.
.. code-block:: python .. code-block:: python
enum = EnumType(CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2, ctype='size_t') enum = EnumType(CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2, ctype='size_t')
op_param_value = enum.CONSTANT_1 # In C code, the Op param will then be a ``size_t``.
In C code: .. note::
.. code-block:: c You can also specify a C name (``cname``) or the current enumeration. This C name may be
used to name functions related to that specific enumeration, e.g. for debugging
purposes. Default C name is the C type (with any sequence of spaces replaced with
an underscore). If you want to debug and your C type is quite generic (e.g.
``int`` or ``double``), we recommend you specify a C name.
size_t value = op_param_value; // contains enum.CONSTANT_1, i.e 0 C name must be a valid C identifier.
.. code-block:: python
enum = EnumType(CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2,
ctype='size_t', cname='MyEnumName')
**Example with aliases** **Example with aliases**
...@@ -918,8 +926,14 @@ class EnumType(Type, dict): ...@@ -918,8 +926,14 @@ class EnumType(Type, dict):
raise TypeError('%s: invalid C type.' % type(self).__name__) raise TypeError('%s: invalid C type.' % type(self).__name__)
self.ctype = ' '.join(ctype_parts) self.ctype = ' '.join(ctype_parts)
def __init_cname(self, cname):
if not re.match('^[A-Za-z_][A-Za-z0-9_]*$', cname):
raise TypeError("%s: invalid C name." % type(self).__name__)
self.cname = cname
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.__init_ctype(kwargs.pop('ctype', 'double')) self.__init_ctype(kwargs.pop('ctype', 'double'))
self.__init_cname(kwargs.pop('cname', self.ctype.replace(' ', '_')))
self.aliases = dict() self.aliases = dict()
for k in kwargs: for k in kwargs:
if re.match('^[A-Z][A-Z0-9_]*$', k) is None: if re.match('^[A-Z][A-Z0-9_]*$', k) is None:
...@@ -1043,12 +1057,51 @@ class EnumType(Type, dict): ...@@ -1043,12 +1057,51 @@ class EnumType(Type, dict):
#endif #endif
""" """
def c_to_string(self):
"""
Return code for a C function that will convert an enumeration value
to a string representation. The function prototype is:
.. code-block:: c
int theano_enum_to_string_<cname>(<ctype> value, char* output_string);
Where ``ctype`` and ``cname`` are the C type and the C name of current Theano enumeration.
``output_string`` should be large enough to contain the longest name in this enumeration.
If given value is unknown, the C function sets a Python ValueError exception and returns a non-zero.
This C function may be useful to retrieve some runtime informations.
It is available in C code when theano flag ``config.cmodule.debug`` is set to ``True``.
"""
return """
#ifdef DEBUG
int theano_enum_to_string_%(cname)s(%(ctype)s in, char* out) {
int ret = 0;
switch(in) {
%(cases)s
default:
PyErr_SetString(PyExc_ValueError, "%(classname)s: unknown enum value.");
ret = -1;
break;
}
return ret;
}
#endif
""" % dict(cname=self.cname, ctype=self.ctype,
classname=type(self).__name__,
cases=''.join("""
case %(name)s: sprintf(out, "%(name)s"); break;
""" % dict(name=name) for name in self))
def c_support_code(self): def c_support_code(self):
return ( return (
self.pyint_compat_code + self.pyint_compat_code +
''.join(""" ''.join("""
#define %s %s #define %s %s
""" % (k, str(self[k])) for k in sorted(self.keys())) """ % (k, str(self[k])) for k in sorted(self.keys())) +
self.c_to_string()
) )
def c_declare(self, name, sub, check_input=True): def c_declare(self, name, sub, check_input=True):
...@@ -1073,7 +1126,7 @@ class EnumType(Type, dict): ...@@ -1073,7 +1126,7 @@ class EnumType(Type, dict):
""" % dict(ctype=self.ctype, name=name, fail=sub['fail']) """ % dict(ctype=self.ctype, name=name, fail=sub['fail'])
def c_code_cache_version(self): def c_code_cache_version(self):
return (1, 1) return (2, self.ctype, self.cname, tuple(self.items()))
class EnumList(EnumType): class EnumList(EnumType):
...@@ -1092,7 +1145,7 @@ class EnumList(EnumType): ...@@ -1092,7 +1145,7 @@ class EnumList(EnumType):
print (enum.CONSTANT_1, enum.CONSTANT_2, enum.CONSTANT_3, enum.CONSTANT_4, enum.CONSTANT_5) print (enum.CONSTANT_1, enum.CONSTANT_2, enum.CONSTANT_3, enum.CONSTANT_4, enum.CONSTANT_5)
# will print: 0 1 2 3 4 # will print: 0 1 2 3 4
Like :class:`EnumType`, you can also define the C type for the op param. Like :class:`EnumType`, you can also define the C type and a C name for the op param.
Default C type is ``int``:: Default C type is ``int``::
enum = EnumList('CONSTANT_1', 'CONSTANT_2', 'CONSTANT_3', 'CONSTANT_4', ctype='unsigned int') enum = EnumList('CONSTANT_1', 'CONSTANT_2', 'CONSTANT_3', 'CONSTANT_4', ctype='unsigned int')
...@@ -1110,9 +1163,10 @@ class EnumList(EnumType): ...@@ -1110,9 +1163,10 @@ class EnumList(EnumType):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
assert len(kwargs) == 0 or (len(kwargs) == 1 and 'ctype' in kwargs), \ assert len(kwargs) in (0, 1, 2), (type(self).__name__ +
type(self).__name__ + ': expected 0 or only 1 extra parameter "ctype".' ': expected 0 to 2 extra parameters ("ctype", "cname").')
ctype = kwargs.pop('ctype', 'int') ctype = kwargs.pop('ctype', 'int')
cname = kwargs.pop('cname', None)
for arg_rank, arg in enumerate(args): for arg_rank, arg in enumerate(args):
if isinstance(arg, (list, tuple)): if isinstance(arg, (list, tuple)):
...@@ -1135,6 +1189,8 @@ class EnumList(EnumType): ...@@ -1135,6 +1189,8 @@ class EnumList(EnumType):
kwargs[constant_name] = constant_value kwargs[constant_name] = constant_value
kwargs.update(ctype=ctype) kwargs.update(ctype=ctype)
if cname is not None:
kwargs.update(cname=cname)
super(EnumList, self).__init__(**kwargs) super(EnumList, self).__init__(**kwargs)
...@@ -1151,7 +1207,7 @@ class CEnumType(EnumList): ...@@ -1151,7 +1207,7 @@ class CEnumType(EnumList):
- In C code, the real values defined in C will be used. - In C code, the real values defined in C will be used.
They could be used either for choices or for its real values. They could be used either for choices or for its real values.
Like :class:`EnumList`, you can also define the C type for the op param. Like :class:`EnumList`, you can also define the C type and a C name for the op param.
Default C type is ``int``. Default C type is ``int``.
.. code-block:: python .. code-block:: python
...@@ -1170,7 +1226,7 @@ class CEnumType(EnumList): ...@@ -1170,7 +1226,7 @@ class CEnumType(EnumList):
""" """
def c_support_code(self): def c_support_code(self):
return self.pyint_compat_code return self.pyint_compat_code + self.c_to_string()
def c_extract(self, name, sub, check_input=True): def c_extract(self, name, sub, check_input=True):
swapped_dict = dict((v, k) for (k, v) in self.items()) swapped_dict = dict((v, k) for (k, v) in self.items())
...@@ -1191,6 +1247,4 @@ class CEnumType(EnumList): ...@@ -1191,6 +1247,4 @@ class CEnumType(EnumList):
fail=sub['fail']) fail=sub['fail'])
def c_code_cache_version(self): def c_code_cache_version(self):
# C code depends on (C constant name, Python value) associations (given by `self.items()`), return (1, super(CEnumType, self).c_code_cache_version())
# so we should better take them into account in C code version.
return (1, tuple(self.items()), super(CEnumType, self).c_code_cache_version())
...@@ -202,6 +202,15 @@ APPLY_SPECIFIC(conv_gw)(PyGpuArrayObject *input, PyGpuArrayObject *output, ...@@ -202,6 +202,15 @@ APPLY_SPECIFIC(conv_gw)(PyGpuArrayObject *input, PyGpuArrayObject *output,
prev_top_dims[i] = PyGpuArray_DIM(output, i); prev_top_dims[i] = PyGpuArray_DIM(output, i);
} }
} }
#ifdef DEBUG
char algorithm_name[128];
if (0 != theano_enum_to_string_cudnnConvolutionBwdFilterAlgo_t(algo, algorithm_name)) {
return 1;
};
// NB: This is printed only when algorithm is chosen at runtime.
fprintf(stderr, "(using %s) ", algorithm_name);
#endif
} }
// The FFT implementation does not support strides, 1x1 filters or inputs // The FFT implementation does not support strides, 1x1 filters or inputs
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论