cython-devel

changeset 2092:181618626844

Cython profiling
author Robert Bradshaw <robertwb@math.washington.edu>
date Mon Jul 20 15:39:57 2009 -0700 (4 years ago)
parents 835c9620f9ed
children f96b8b73accf
files Cython/Compiler/Code.py Cython/Compiler/ModuleNode.py Cython/Compiler/Naming.py Cython/Compiler/Nodes.py Cython/Compiler/Options.py
line diff
1.1 --- a/Cython/Compiler/Code.py Thu Jul 16 02:32:52 2009 -0700 1.2 +++ b/Cython/Compiler/Code.py Mon Jul 20 15:39:57 2009 -0700 1.3 @@ -943,6 +943,15 @@ 1.4 def put_finish_refcount_context(self): 1.5 self.putln("__Pyx_FinishRefcountContext();") 1.6 1.7 + def put_trace_call(self, name, pos): 1.8 + self.putln('__Pyx_TraceCall("%s", %s[%s], %s);' % (name, Naming.filetable_cname, self.lookup_filename(pos[0]), pos[1])); 1.9 + 1.10 + def put_trace_exception(self): 1.11 + self.putln("__Pyx_TraceException();") 1.12 + 1.13 + def put_trace_return(self, retvalue_cname): 1.14 + self.putln("__Pyx_TraceReturn(%s);" % retvalue_cname) 1.15 + 1.16 1.17 class PyrexCodeWriter(object): 1.18 # f file output file
2.1 --- a/Cython/Compiler/ModuleNode.py Thu Jul 16 02:32:52 2009 -0700 2.2 +++ b/Cython/Compiler/ModuleNode.py Mon Jul 20 15:39:57 2009 -0700 2.3 @@ -2480,6 +2480,7 @@ 2.4 #define __Pyx_XGOTREF(r) if((r) == NULL) ; else __Pyx_GOTREF(r) 2.5 """) 2.6 2.7 + 2.8 main_method = UtilityCode( 2.9 impl = """ 2.10 #if PY_MAJOR_VERSION < 3 || (!defined(WIN32) && !defined(MS_WINDOWS))
3.1 --- a/Cython/Compiler/Naming.py Thu Jul 16 02:32:52 2009 -0700 3.2 +++ b/Cython/Compiler/Naming.py Mon Jul 20 15:39:57 2009 -0700 3.3 @@ -84,6 +84,8 @@ 3.4 import_star_set = pyrex_prefix + "import_star_set" 3.5 cur_scope_cname = pyrex_prefix + "cur_scope" 3.6 enc_scope_cname = pyrex_prefix + "enc_scope" 3.7 +frame_cname = pyrex_prefix + "frame" 3.8 +frame_code_cname = pyrex_prefix + "frame_code" 3.9 3.10 line_c_macro = "__LINE__" 3.11
4.1 --- a/Cython/Compiler/Nodes.py Thu Jul 16 02:32:52 2009 -0700 4.2 +++ b/Cython/Compiler/Nodes.py Mon Jul 20 15:39:57 2009 -0700 4.3 @@ -1002,6 +1002,7 @@ 4.4 py_func = None 4.5 assmt = None 4.6 needs_closure = False 4.7 + modifiers = [] 4.8 4.9 def analyse_default_values(self, env): 4.10 genv = env.global_scope() 4.11 @@ -1054,6 +1055,15 @@ 4.12 4.13 is_getbuffer_slot = (self.entry.name == "__getbuffer__" and 4.14 self.entry.scope.is_c_class_scope) 4.15 + 4.16 + if code.globalstate.directives['profile'] is None: 4.17 + profile = 'inline' not in self.modifiers 4.18 + else: 4.19 + profile = code.globalstate.directives['profile'] 4.20 + if profile and lenv.nogil: 4.21 + error(self.pos, "Cannot profile nogil function.") 4.22 + if profile: 4.23 + code.globalstate.use_utility_code(trace_utility_code) 4.24 4.25 # Generate C code for header and body of function 4.26 code.enter_cfunc_scope() 4.27 @@ -1101,6 +1111,8 @@ 4.28 env.use_utility_code(force_init_threads_utility_code) 4.29 code.putln("PyGILState_STATE _save = PyGILState_Ensure();") 4.30 # ----- Automatic lead-ins for certain special functions 4.31 + if profile: 4.32 + code.put_trace_call(self.entry.name, self.pos) 4.33 if not lenv.nogil: 4.34 code.put_setup_refcount_context(self.entry.name) 4.35 if is_getbuffer_slot: 4.36 @@ -1165,6 +1177,9 @@ 4.37 err_val = self.error_value() 4.38 exc_check = self.caller_will_check_exceptions() 4.39 if err_val is not None or exc_check: 4.40 + # TODO: Fix exception tracing (though currently unused by cProfile). 4.41 + # code.globalstate.use_utility_code(get_exception_tuple_utility_code) 4.42 + # code.put_trace_exception() 4.43 code.putln('__Pyx_AddTraceback("%s");' % self.entry.qualified_name) 4.44 else: 4.45 warning(self.entry.pos, "Unraisable exception in function '%s'." \ 4.46 @@ -1194,9 +1209,6 @@ 4.47 4.48 4.49 # ----- Non-error return cleanup 4.50 - # If you add anything here, remember to add a condition to the 4.51 - # if-test above in the error block (so that it can jump past this 4.52 - # block). 4.53 code.put_label(code.return_label) 4.54 for entry in lenv.buffer_entries: 4.55 if entry.used: 4.56 @@ -1233,6 +1245,12 @@ 4.57 # We do as Python instances and coerce -1 into -2. 4.58 code.putln("if (unlikely(%s == -1) && !PyErr_Occurred()) %s = -2;" % (Naming.retval_cname, Naming.retval_cname)) 4.59 4.60 + if profile: 4.61 + if self.return_type.is_pyobject: 4.62 + code.put_trace_return(Naming.retval_cname) 4.63 + else: 4.64 + code.put_trace_return("Py_None") 4.65 + 4.66 if acquire_gil: 4.67 code.putln("PyGILState_Release(_save);") 4.68 4.69 @@ -5695,6 +5713,31 @@ 4.70 4.71 #------------------------------------------------------------------------------------ 4.72 4.73 +get_exception_tuple_utility_code = UtilityCode(proto=""" 4.74 +static PyObject *__Pyx_GetExceptionTuple(void); /*proto*/ 4.75 +""", 4.76 +impl = """ 4.77 +static PyObject *__Pyx_GetExceptionTuple(void) { 4.78 + PyObject *type = NULL, *value = NULL, *tb = NULL; 4.79 + if (__Pyx_GetException(&type, &value, &tb) == 0) { 4.80 + PyObject* exc_info = PyTuple_New(3); 4.81 + if (exc_info) { 4.82 + Py_INCREF(type); 4.83 + Py_INCREF(value); 4.84 + Py_INCREF(tb); 4.85 + PyTuple_SET_ITEM(exc_info, 0, type); 4.86 + PyTuple_SET_ITEM(exc_info, 1, value); 4.87 + PyTuple_SET_ITEM(exc_info, 2, tb); 4.88 + return exc_info; 4.89 + } 4.90 + } 4.91 + return NULL; 4.92 +} 4.93 +""", 4.94 +requires=[get_exception_utility_code]) 4.95 + 4.96 +#------------------------------------------------------------------------------------ 4.97 + 4.98 reset_exception_utility_code = UtilityCode( 4.99 proto = """ 4.100 static INLINE void __Pyx_ExceptionSave(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ 4.101 @@ -5740,3 +5783,138 @@ 4.102 """) 4.103 4.104 #------------------------------------------------------------------------------------ 4.105 + 4.106 +# Note that cPython ignores PyTrace_EXCEPTION, 4.107 +# but maybe some other profilers don't. 4.108 + 4.109 +trace_utility_code = UtilityCode(proto=""" 4.110 +#define CYTHON_TRACING 1 4.111 +#define CYTHON_TRACING_REUSE_FRAME 0 4.112 + 4.113 +#if CYTHON_TRACING 4.114 + 4.115 +#include "compile.h" 4.116 +#include "frameobject.h" 4.117 +#include "traceback.h" 4.118 + 4.119 +#if CYTHON_TRACING_REUSE_FRAME 4.120 +#define CYTHON_FRAME_MODIFIER static 4.121 +#define CYTHON_FRAME_DEL 4.122 +#else 4.123 +#define CYTHON_FRAME_MODIFIER 4.124 +#define CYTHON_FRAME_DEL Py_DECREF(%(FRAME)s) 4.125 +#endif 4.126 + 4.127 +#define __Pyx_TraceCall(funcname, srcfile, firstlineno) \\ 4.128 +static PyCodeObject *%(FRAME_CODE)s = NULL; \\ 4.129 +CYTHON_FRAME_MODIFIER PyFrameObject *%(FRAME)s = NULL; \\ 4.130 +int __Pyx_use_tracing = 0; \\ 4.131 +if (PyThreadState_GET()->use_tracing && PyThreadState_GET()->c_profilefunc) { \\ 4.132 + __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&%(FRAME_CODE)s, &%(FRAME)s, funcname, srcfile, firstlineno); \\ 4.133 +} 4.134 + 4.135 +#define __Pyx_TraceException() \\ 4.136 +if (__Pyx_use_tracing && PyThreadState_GET()->use_tracing && PyThreadState_GET()->c_profilefunc) { \\ 4.137 + PyObject *exc_info = __Pyx_GetExceptionTuple(); \\ 4.138 + if (exc_info) { \\ 4.139 + PyThreadState_GET()->c_profilefunc( \\ 4.140 + PyThreadState_GET()->c_profileobj, %(FRAME)s, PyTrace_EXCEPTION, exc_info); \\ 4.141 + Py_DECREF(exc_info); \\ 4.142 + } \\ 4.143 +} 4.144 + 4.145 +#define __Pyx_TraceReturn(result) \\ 4.146 +if (__Pyx_use_tracing && PyThreadState_GET()->use_tracing && PyThreadState_GET()->c_profilefunc) { \\ 4.147 + PyThreadState_GET()->c_profilefunc( \\ 4.148 + PyThreadState_GET()->c_profileobj, %(FRAME)s, PyTrace_RETURN, (PyObject*)result); \\ 4.149 + CYTHON_FRAME_DEL; \\ 4.150 +} 4.151 + 4.152 +static PyCodeObject *__Pyx_createFrameCodeObject(const char *funcname, const char *srcfile, int firstlineno); /*proto*/ 4.153 +static int __Pyx_TraceSetupAndCall(PyCodeObject** code, PyFrameObject** frame, const char *funcname, const char *srcfile, int firstlineno); /*proto*/ 4.154 + 4.155 +#else 4.156 +#define __Pyx_TraceCall(funcname, srcfile, firstlineno) 4.157 +#define __Pyx_TraceException() 4.158 +#define __Pyx_TraceReturn(result) 4.159 +#endif /* CYTHON_TRACING */ 4.160 +""" 4.161 +% { 4.162 + "FRAME": Naming.frame_cname, 4.163 + "FRAME_CODE": Naming.frame_code_cname, 4.164 +}, 4.165 +impl = """ 4.166 + 4.167 +#if CYTHON_TRACING 4.168 + 4.169 +static int __Pyx_TraceSetupAndCall(PyCodeObject** code, 4.170 + PyFrameObject** frame, 4.171 + const char *funcname, 4.172 + const char *srcfile, 4.173 + int firstlineno) { 4.174 + if (*frame == NULL || !CYTHON_TRACING_REUSE_FRAME) { 4.175 + if (*code == NULL) { 4.176 + *code = __Pyx_createFrameCodeObject(funcname, srcfile, firstlineno); 4.177 + if (*code == NULL) return 0; 4.178 + } 4.179 + *frame = PyFrame_New( 4.180 + PyThreadState_GET(), /*PyThreadState *tstate*/ 4.181 + *code, /*PyCodeObject *code*/ 4.182 + PyModule_GetDict(%(MODULE)s), /*PyObject *globals*/ 4.183 + 0 /*PyObject *locals*/ 4.184 + ); 4.185 + if (*frame == NULL) return 0; 4.186 + } 4.187 + else { 4.188 + (*frame)->f_tstate = PyThreadState_GET(); 4.189 + } 4.190 + return PyThreadState_GET()->c_profilefunc(PyThreadState_GET()->c_profileobj, *frame, PyTrace_CALL, NULL) == 0; 4.191 +} 4.192 + 4.193 +static PyCodeObject *__Pyx_createFrameCodeObject(const char *funcname, const char *srcfile, int firstlineno) { 4.194 + PyObject *py_srcfile = 0; 4.195 + PyObject *py_funcname = 0; 4.196 + PyCodeObject *py_code = 0; 4.197 + 4.198 + #if PY_MAJOR_VERSION < 3 4.199 + py_funcname = PyString_FromString(funcname); 4.200 + py_srcfile = PyString_FromString(srcfile); 4.201 + #else 4.202 + py_funcname = PyUnicode_FromString(funcname); 4.203 + py_srcfile = PyUnicode_FromString(srcfile); 4.204 + #endif 4.205 + if (!py_funcname | !py_srcfile) goto bad; 4.206 + 4.207 + py_code = PyCode_New( 4.208 + 0, /*int argcount,*/ 4.209 + #if PY_MAJOR_VERSION >= 3 4.210 + 0, /*int kwonlyargcount,*/ 4.211 + #endif 4.212 + 0, /*int nlocals,*/ 4.213 + 0, /*int stacksize,*/ 4.214 + 0, /*int flags,*/ 4.215 + %(EMPTY_BYTES)s, /*PyObject *code,*/ 4.216 + %(EMPTY_TUPLE)s, /*PyObject *consts,*/ 4.217 + %(EMPTY_TUPLE)s, /*PyObject *names,*/ 4.218 + %(EMPTY_TUPLE)s, /*PyObject *varnames,*/ 4.219 + %(EMPTY_TUPLE)s, /*PyObject *freevars,*/ 4.220 + %(EMPTY_TUPLE)s, /*PyObject *cellvars,*/ 4.221 + py_srcfile, /*PyObject *filename,*/ 4.222 + py_funcname, /*PyObject *name,*/ 4.223 + firstlineno, /*int firstlineno,*/ 4.224 + %(EMPTY_BYTES)s /*PyObject *lnotab*/ 4.225 + ); 4.226 + 4.227 +bad: 4.228 + Py_XDECREF(py_srcfile); 4.229 + Py_XDECREF(py_funcname); 4.230 + 4.231 + return py_code; 4.232 +} 4.233 + 4.234 +#endif /* CYTHON_TRACING */ 4.235 +""" % { 4.236 + 'EMPTY_TUPLE' : Naming.empty_tuple, 4.237 + 'EMPTY_BYTES' : Naming.empty_bytes, 4.238 + "MODULE": Naming.module_cname, 4.239 +})
5.1 --- a/Cython/Compiler/Options.py Thu Jul 16 02:32:52 2009 -0700 5.2 +++ b/Cython/Compiler/Options.py Mon Jul 20 15:39:57 2009 -0700 5.3 @@ -67,10 +67,11 @@ 5.4 'wraparound' : True, 5.5 'c99_complex' : False, # Don't use macro wrappers for complex arith, not sure what to name this... 5.6 'callspec' : "", 5.7 + 'profile': None, 5.8 } 5.9 5.10 # Override types possibilities above, if needed 5.11 -option_types = { } 5.12 +option_types = { 'profile': bool } 5.13 5.14 for key, val in option_defaults.items(): 5.15 if key not in option_types: