Skip to content

Commit ec5896d

Browse files
committed
[3.13] pythonGH-127953: Make line number lookup O(1) regardless of the size of the code object (python#129127)
pythonGH-127953: Make line number lookup O(1) regardless of the size of the code object (pythonGH-128350)
1 parent aab69a8 commit ec5896d

File tree

5 files changed

+217
-125
lines changed

5 files changed

+217
-125
lines changed

Include/cpython/code.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ typedef struct {
7676
} _PyCoCached;
7777

7878
/* Ancillary data structure used for instrumentation.
79-
Line instrumentation creates an array of
80-
these. One entry per code unit.*/
79+
Line instrumentation creates this with sufficient
80+
space for one entry per code unit. The total size
81+
of the data will be `bytes_per_entry * Py_SIZE(code)` */
8182
typedef struct {
82-
uint8_t original_opcode;
83-
int8_t line_delta;
83+
uint8_t bytes_per_entry;
84+
uint8_t data[1];
8485
} _PyCoLineInstrumentationData;
8586

8687
/* Main data structure used for instrumentation.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The time to handle a ``LINE`` event in sys.monitoring (and sys.settrace) is
2+
now independent of the number of lines in the code object.

Objects/codeobject.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,9 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq)
853853
if (addrq < 0) {
854854
return co->co_firstlineno;
855855
}
856+
if (co->_co_monitoring && co->_co_monitoring->lines) {
857+
return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT));
858+
}
856859
assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));
857860
PyCodeAddressRange bounds;
858861
_PyCode_InitAddressRange(co, &bounds);

Python/ceval.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -860,17 +860,25 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
860860
{
861861
_Py_CODEUNIT *prev = frame->prev_instr;
862862
_Py_CODEUNIT *here = frame->prev_instr = next_instr;
863-
_PyFrame_SetStackPointer(frame, stack_pointer);
864-
int original_opcode = _Py_call_instrumentation_line(
865-
tstate, frame, here, prev);
866-
stack_pointer = _PyFrame_GetStackPointer(frame);
867-
if (original_opcode < 0) {
868-
next_instr = here+1;
869-
goto error;
863+
int original_opcode = 0;
864+
if (tstate->tracing) {
865+
PyCodeObject *code = _PyFrame_GetCode(frame);
866+
int index = (int)(here - _PyCode_CODE(code));
867+
original_opcode = code->_co_monitoring->lines->data[index*code->_co_monitoring->lines->bytes_per_entry];
870868
}
871-
next_instr = frame->prev_instr;
872-
if (next_instr != here) {
873-
DISPATCH();
869+
else {
870+
_PyFrame_SetStackPointer(frame, stack_pointer);
871+
original_opcode = _Py_call_instrumentation_line(
872+
tstate, frame, here, prev);
873+
stack_pointer = _PyFrame_GetStackPointer(frame);
874+
if (original_opcode < 0) {
875+
next_instr = here+1;
876+
goto error;
877+
}
878+
next_instr = frame->prev_instr;
879+
if (next_instr != here) {
880+
DISPATCH();
881+
}
874882
}
875883
if (_PyOpcode_Caches[original_opcode]) {
876884
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);

0 commit comments

Comments
 (0)