mirror of
https://github.com/python/cpython.git
synced 2025-10-02 21:25:24 +00:00
GH-93516: Speedup line number checks when tracing. (GH-93763)
* Use a lookup table to reduce overhead of getting line numbers during tracing.
This commit is contained in:
parent
45e62a2bc1
commit
ab0e601016
6 changed files with 101 additions and 8 deletions
|
@ -62,7 +62,8 @@ typedef uint16_t _Py_CODEUNIT;
|
||||||
PyObject *co_exceptiontable; /* Byte string encoding exception handling \
|
PyObject *co_exceptiontable; /* Byte string encoding exception handling \
|
||||||
table */ \
|
table */ \
|
||||||
int co_flags; /* CO_..., see below */ \
|
int co_flags; /* CO_..., see below */ \
|
||||||
int co_warmup; /* Warmup counter for quickening */ \
|
short co_warmup; /* Warmup counter for quickening */ \
|
||||||
|
short _co_linearray_entry_size; /* Size of each entry in _co_linearray */ \
|
||||||
\
|
\
|
||||||
/* The rest are not so impactful on performance. */ \
|
/* The rest are not so impactful on performance. */ \
|
||||||
int co_argcount; /* #arguments, except *args */ \
|
int co_argcount; /* #arguments, except *args */ \
|
||||||
|
@ -90,6 +91,7 @@ typedef uint16_t _Py_CODEUNIT;
|
||||||
PyObject *co_weakreflist; /* to support weakrefs to code objects */ \
|
PyObject *co_weakreflist; /* to support weakrefs to code objects */ \
|
||||||
void *_co_code; /* cached co_code object/attribute */ \
|
void *_co_code; /* cached co_code object/attribute */ \
|
||||||
int _co_firsttraceable; /* index of first traceable instruction */ \
|
int _co_firsttraceable; /* index of first traceable instruction */ \
|
||||||
|
char *_co_linearray; /* array of line offsets */ \
|
||||||
/* Scratch space for extra data relating to the code object. \
|
/* Scratch space for extra data relating to the code object. \
|
||||||
Type is a void* to keep the format private in codeobject.c to force \
|
Type is a void* to keep the format private in codeobject.c to force \
|
||||||
people to go through the proper APIs. */ \
|
people to go through the proper APIs. */ \
|
||||||
|
|
|
@ -461,6 +461,35 @@ adaptive_counter_backoff(uint16_t counter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Line array cache for tracing */
|
||||||
|
|
||||||
|
extern int _PyCode_CreateLineArray(PyCodeObject *co);
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
_PyCode_InitLineArray(PyCodeObject *co)
|
||||||
|
{
|
||||||
|
if (co->_co_linearray) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return _PyCode_CreateLineArray(co);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
_PyCode_LineNumberFromArray(PyCodeObject *co, int index)
|
||||||
|
{
|
||||||
|
assert(co->_co_linearray != NULL);
|
||||||
|
assert(index >= 0);
|
||||||
|
assert(index < Py_SIZE(co));
|
||||||
|
if (co->_co_linearray_entry_size == 2) {
|
||||||
|
return ((int16_t *)co->_co_linearray)[index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(co->_co_linearray_entry_size == 4);
|
||||||
|
return ((int32_t *)co->_co_linearray)[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Lazily create a table mapping bytecode offsets to line numbers to speed up
|
||||||
|
calculation of line numbers when tracing.
|
|
@ -339,6 +339,8 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
|
||||||
co->_co_code = NULL;
|
co->_co_code = NULL;
|
||||||
|
|
||||||
co->co_warmup = QUICKENING_INITIAL_WARMUP_VALUE;
|
co->co_warmup = QUICKENING_INITIAL_WARMUP_VALUE;
|
||||||
|
co->_co_linearray_entry_size = 0;
|
||||||
|
co->_co_linearray = NULL;
|
||||||
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
|
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
|
||||||
PyBytes_GET_SIZE(con->code));
|
PyBytes_GET_SIZE(con->code));
|
||||||
int entry_point = 0;
|
int entry_point = 0;
|
||||||
|
@ -703,6 +705,50 @@ failed:
|
||||||
lnotab_notes.txt for the details of the lnotab representation.
|
lnotab_notes.txt for the details of the lnotab representation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyCode_CreateLineArray(PyCodeObject *co)
|
||||||
|
{
|
||||||
|
assert(co->_co_linearray == NULL);
|
||||||
|
PyCodeAddressRange bounds;
|
||||||
|
int size;
|
||||||
|
int max_line = 0;
|
||||||
|
_PyCode_InitAddressRange(co, &bounds);
|
||||||
|
while(_PyLineTable_NextAddressRange(&bounds)) {
|
||||||
|
if (bounds.ar_line > max_line) {
|
||||||
|
max_line = bounds.ar_line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (max_line < (1 << 15)) {
|
||||||
|
size = 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size = 4;
|
||||||
|
}
|
||||||
|
co->_co_linearray = PyMem_Malloc(Py_SIZE(co)*size);
|
||||||
|
if (co->_co_linearray == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
co->_co_linearray_entry_size = size;
|
||||||
|
_PyCode_InitAddressRange(co, &bounds);
|
||||||
|
while(_PyLineTable_NextAddressRange(&bounds)) {
|
||||||
|
int start = bounds.ar_start / sizeof(_Py_CODEUNIT);
|
||||||
|
int end = bounds.ar_end / sizeof(_Py_CODEUNIT);
|
||||||
|
for (int index = start; index < end; index++) {
|
||||||
|
assert(index < (int)Py_SIZE(co));
|
||||||
|
if (size == 2) {
|
||||||
|
assert(((int16_t)bounds.ar_line) == bounds.ar_line);
|
||||||
|
((int16_t *)co->_co_linearray)[index] = bounds.ar_line;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(size == 4);
|
||||||
|
((int32_t *)co->_co_linearray)[index] = bounds.ar_line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
PyCode_Addr2Line(PyCodeObject *co, int addrq)
|
PyCode_Addr2Line(PyCodeObject *co, int addrq)
|
||||||
{
|
{
|
||||||
|
@ -710,6 +756,9 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq)
|
||||||
return co->co_firstlineno;
|
return co->co_firstlineno;
|
||||||
}
|
}
|
||||||
assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));
|
assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));
|
||||||
|
if (co->_co_linearray) {
|
||||||
|
return _PyCode_LineNumberFromArray(co, addrq / sizeof(_Py_CODEUNIT));
|
||||||
|
}
|
||||||
PyCodeAddressRange bounds;
|
PyCodeAddressRange bounds;
|
||||||
_PyCode_InitAddressRange(co, &bounds);
|
_PyCode_InitAddressRange(co, &bounds);
|
||||||
return _PyCode_CheckLineNumber(addrq, &bounds);
|
return _PyCode_CheckLineNumber(addrq, &bounds);
|
||||||
|
@ -1549,6 +1598,9 @@ code_dealloc(PyCodeObject *co)
|
||||||
if (co->co_weakreflist != NULL) {
|
if (co->co_weakreflist != NULL) {
|
||||||
PyObject_ClearWeakRefs((PyObject*)co);
|
PyObject_ClearWeakRefs((PyObject*)co);
|
||||||
}
|
}
|
||||||
|
if (co->_co_linearray) {
|
||||||
|
PyMem_Free(co->_co_linearray);
|
||||||
|
}
|
||||||
if (co->co_warmup == 0) {
|
if (co->co_warmup == 0) {
|
||||||
_Py_QuickenedCount--;
|
_Py_QuickenedCount--;
|
||||||
}
|
}
|
||||||
|
@ -2106,6 +2158,10 @@ _PyStaticCode_Dealloc(PyCodeObject *co)
|
||||||
PyObject_ClearWeakRefs((PyObject *)co);
|
PyObject_ClearWeakRefs((PyObject *)co);
|
||||||
co->co_weakreflist = NULL;
|
co->co_weakreflist = NULL;
|
||||||
}
|
}
|
||||||
|
if (co->_co_linearray) {
|
||||||
|
PyMem_Free(co->_co_linearray);
|
||||||
|
co->_co_linearray = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -6799,9 +6799,10 @@ call_trace(Py_tracefunc func, PyObject *obj,
|
||||||
tstate->tracing_what = what;
|
tstate->tracing_what = what;
|
||||||
PyThreadState_EnterTracing(tstate);
|
PyThreadState_EnterTracing(tstate);
|
||||||
assert(_PyInterpreterFrame_LASTI(frame) >= 0);
|
assert(_PyInterpreterFrame_LASTI(frame) >= 0);
|
||||||
initialize_trace_info(&tstate->trace_info, frame);
|
if (_PyCode_InitLineArray(frame->f_code)) {
|
||||||
int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
|
return -1;
|
||||||
f->f_lineno = _PyCode_CheckLineNumber(addr, &tstate->trace_info.bounds);
|
}
|
||||||
|
f->f_lineno = _PyCode_LineNumberFromArray(frame->f_code, _PyInterpreterFrame_LASTI(frame));
|
||||||
result = func(obj, f, what, arg);
|
result = func(obj, f, what, arg);
|
||||||
f->f_lineno = 0;
|
f->f_lineno = 0;
|
||||||
PyThreadState_LeaveTracing(tstate);
|
PyThreadState_LeaveTracing(tstate);
|
||||||
|
@ -6838,16 +6839,17 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
|
||||||
represents a jump backwards, update the frame's line number and
|
represents a jump backwards, update the frame's line number and
|
||||||
then call the trace function if we're tracing source lines.
|
then call the trace function if we're tracing source lines.
|
||||||
*/
|
*/
|
||||||
initialize_trace_info(&tstate->trace_info, frame);
|
if (_PyCode_InitLineArray(frame->f_code)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
int lastline;
|
int lastline;
|
||||||
if (instr_prev <= frame->f_code->_co_firsttraceable) {
|
if (instr_prev <= frame->f_code->_co_firsttraceable) {
|
||||||
lastline = -1;
|
lastline = -1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lastline = _PyCode_CheckLineNumber(instr_prev*sizeof(_Py_CODEUNIT), &tstate->trace_info.bounds);
|
lastline = _PyCode_LineNumberFromArray(frame->f_code, instr_prev);
|
||||||
}
|
}
|
||||||
int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
|
int line = _PyCode_LineNumberFromArray(frame->f_code, _PyInterpreterFrame_LASTI(frame));
|
||||||
int line = _PyCode_CheckLineNumber(addr, &tstate->trace_info.bounds);
|
|
||||||
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
|
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -254,6 +254,7 @@ class Printer:
|
||||||
self.write(f".co_exceptiontable = {co_exceptiontable},")
|
self.write(f".co_exceptiontable = {co_exceptiontable},")
|
||||||
self.field(code, "co_flags")
|
self.field(code, "co_flags")
|
||||||
self.write(".co_warmup = QUICKENING_INITIAL_WARMUP_VALUE,")
|
self.write(".co_warmup = QUICKENING_INITIAL_WARMUP_VALUE,")
|
||||||
|
self.write("._co_linearray_entry_size = 0,")
|
||||||
self.field(code, "co_argcount")
|
self.field(code, "co_argcount")
|
||||||
self.field(code, "co_posonlyargcount")
|
self.field(code, "co_posonlyargcount")
|
||||||
self.field(code, "co_kwonlyargcount")
|
self.field(code, "co_kwonlyargcount")
|
||||||
|
@ -271,6 +272,7 @@ class Printer:
|
||||||
self.write(f".co_name = {co_name},")
|
self.write(f".co_name = {co_name},")
|
||||||
self.write(f".co_qualname = {co_qualname},")
|
self.write(f".co_qualname = {co_qualname},")
|
||||||
self.write(f".co_linetable = {co_linetable},")
|
self.write(f".co_linetable = {co_linetable},")
|
||||||
|
self.write("._co_linearray = NULL,")
|
||||||
self.write(f".co_code_adaptive = {co_code_adaptive},")
|
self.write(f".co_code_adaptive = {co_code_adaptive},")
|
||||||
for i, op in enumerate(code.co_code[::2]):
|
for i, op in enumerate(code.co_code[::2]):
|
||||||
if op == RESUME:
|
if op == RESUME:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue