Skip to content

Commit ad18e02

Browse files
committed
implemented a module global cache for the code objects used in traceback building
1 parent 6f7bc67 commit ad18e02

File tree

4 files changed

+140
-4
lines changed

4 files changed

+140
-4
lines changed

Cython/Compiler/ModuleNode.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,10 @@ def generate_module_cleanup_func(self, env, code):
18951895
code.put_decref_clear(Naming.empty_tuple,
18961896
PyrexTypes.py_object_type,
18971897
nanny=False)
1898+
code.putln("/*--- Code object cache cleanup code ---*/")
1899+
code.globalstate.use_utility_code(
1900+
UtilityCode.load_cached("CodeObjectCache", "ModuleSetupCode.c"))
1901+
code.putln('%s();' % Naming.global_code_object_cache_clear)
18981902
# for entry in env.pynum_entries:
18991903
# code.put_decref_clear(entry.cname,
19001904
# PyrexTypes.py_object_type,

Cython/Compiler/Naming.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
fused_func_prefix = pyrex_prefix + 'fuse_'
100100
quick_temp_cname = pyrex_prefix + "temp" # temp variable for quick'n'dirty temping
101101

102+
global_code_object_cache_find = pyrex_prefix + 'find_code_object'
103+
global_code_object_cache_insert = pyrex_prefix + 'insert_code_object'
104+
global_code_object_cache_clear = pyrex_prefix + 'clear_code_object_cache'
105+
102106
genexpr_id_ref = 'genexpr'
103107

104108
line_c_macro = "__LINE__"

Cython/Compiler/Nodes.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8634,6 +8634,8 @@ def generate_execution_code(self, code):
86348634

86358635
#------------------------------------------------------------------------------------
86368636

8637+
code_object_cache_utility_code = UtilityCode.load_cached("CodeObjectCache", "ModuleSetupCode.c")
8638+
86378639
traceback_utility_code = UtilityCode(
86388640
proto = """
86398641
static void __Pyx_AddTraceback(const char *funcname, int %(CLINENO)s,
@@ -8709,9 +8711,13 @@ def generate_execution_code(self, code):
87098711
PyObject *py_globals = 0;
87108712
PyFrameObject *py_frame = 0;
87118713
8712-
py_code = __Pyx_CreateCodeObjectForTraceback(
8713-
funcname, %(CLINENO)s, %(LINENO)s, %(FILENAME)s);
8714-
if (!py_code) goto bad;
8714+
py_code = %(FINDCODEOBJECT)s(%(CLINENO)s);
8715+
if (!py_code) {
8716+
py_code = __Pyx_CreateCodeObjectForTraceback(
8717+
funcname, %(CLINENO)s, %(LINENO)s, %(FILENAME)s);
8718+
if (!py_code) goto bad;
8719+
%(INSERTCODEOBJECT)s(%(CLINENO)s, py_code);
8720+
}
87158721
py_globals = PyModule_GetDict(%(GLOBALS)s);
87168722
if (!py_globals) goto bad;
87178723
py_frame = PyFrame_New(
@@ -8733,9 +8739,12 @@ def generate_execution_code(self, code):
87338739
'CFILENAME': Naming.cfilenm_cname,
87348740
'CLINENO': Naming.clineno_cname,
87358741
'GLOBALS': Naming.module_cname,
8742+
'FINDCODEOBJECT' : Naming.global_code_object_cache_find,
8743+
'INSERTCODEOBJECT' : Naming.global_code_object_cache_insert,
87368744
'EMPTY_TUPLE' : Naming.empty_tuple,
87378745
'EMPTY_BYTES' : Naming.empty_bytes,
8738-
})
8746+
},
8747+
requires=[code_object_cache_utility_code])
87398748

87408749
#------------------------------------------------------------------------------------
87418750

Cython/Utility/ModuleSetupCode.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,125 @@
219219
#define __Pyx_DOCSTR(n) (n)
220220
#endif
221221

222+
/////////////// CodeObjectCache.proto ///////////////
223+
224+
typedef struct {
225+
int code_line;
226+
PyCodeObject* code_object;
227+
} __Pyx_CodeObjectCacheEntry;
228+
229+
struct __Pyx_CodeObjectCache {
230+
int count;
231+
int max_count;
232+
__Pyx_CodeObjectCacheEntry* entries;
233+
};
234+
235+
static struct __Pyx_CodeObjectCache __pyx_code_cache = {0,0,NULL};
236+
237+
static void __pyx_clear_code_object_cache(void);
238+
static PyCodeObject *__pyx_find_code_object(int code_line);
239+
static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object);
240+
241+
/////////////// CodeObjectCache ///////////////
242+
// Note that errors are simply ignored in the code below.
243+
// This is just a cache, if a lookup or insertion fails - so what?
244+
245+
static void __pyx_clear_code_object_cache(void) {
246+
__Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries;
247+
int count = __pyx_code_cache.count;
248+
int i;
249+
if (entries == NULL) {
250+
return;
251+
}
252+
__pyx_code_cache.count = 0;
253+
__pyx_code_cache.max_count = 0;
254+
__pyx_code_cache.entries = NULL;
255+
for (i=0; i<count; i++) {
256+
Py_DECREF(entries[i].code_object);
257+
}
258+
PyMem_Free(entries);
259+
}
260+
261+
static int __pyx_bisect_code_objects(int code_line) {
262+
int start, end, mid;
263+
__Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries;
264+
start = 0;
265+
end = __pyx_code_cache.count - 1;
266+
while (start < end) {
267+
mid = (start + end) / 2;
268+
if (code_line < entries[mid].code_line) {
269+
end = mid - 1;
270+
} else if (code_line > entries[mid].code_line) {
271+
start = mid + 1;
272+
} else {
273+
return mid;
274+
}
275+
}
276+
if (start == end || code_line <= entries[start].code_line) {
277+
return start;
278+
} else {
279+
return end;
280+
}
281+
}
282+
283+
static PyCodeObject *__pyx_find_code_object(int code_line) {
284+
PyCodeObject* code_object;
285+
int pos;
286+
if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) {
287+
return NULL;
288+
}
289+
pos = __pyx_bisect_code_objects(code_line);
290+
if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) {
291+
return NULL;
292+
}
293+
code_object = __pyx_code_cache.entries[pos].code_object;
294+
Py_INCREF(code_object);
295+
return code_object;
296+
}
297+
298+
static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) {
299+
int pos, i;
300+
__Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries;
301+
if (unlikely(!code_line)) {
302+
return;
303+
}
304+
if (unlikely(!entries)) {
305+
entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry));
306+
if (likely(entries)) {
307+
__pyx_code_cache.entries = entries;
308+
__pyx_code_cache.max_count = 64;
309+
__pyx_code_cache.count = 1;
310+
entries[0].code_line = code_line;
311+
entries[0].code_object = code_object;
312+
Py_INCREF(code_object);
313+
}
314+
return;
315+
}
316+
pos = __pyx_bisect_code_objects(code_line);
317+
if ((pos < __pyx_code_cache.count) && unlikely(__pyx_code_cache.entries[pos].code_line == code_line)) {
318+
PyCodeObject* tmp = entries[pos].code_object;
319+
entries[pos].code_object = code_object;
320+
Py_DECREF(tmp);
321+
return;
322+
}
323+
if (__pyx_code_cache.count == __pyx_code_cache.max_count) {
324+
int new_max = __pyx_code_cache.max_count + 64;
325+
entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc(
326+
__pyx_code_cache.entries, new_max*sizeof(__Pyx_CodeObjectCacheEntry));
327+
if (unlikely(!entries)) {
328+
return;
329+
}
330+
__pyx_code_cache.entries = entries;
331+
__pyx_code_cache.max_count = new_max;
332+
}
333+
for (i=__pyx_code_cache.count; i>pos; i--) {
334+
entries[i] = entries[i-1];
335+
}
336+
entries[pos].code_line = code_line;
337+
entries[pos].code_object = code_object;
338+
__pyx_code_cache.count++;
339+
Py_INCREF(code_object);
340+
}
222341

223342
/////////////// CheckBinaryVersion.proto ///////////////
224343

0 commit comments

Comments
 (0)