Skip to content

Commit b4903af

Browse files
authored
bpo-45256: Remove the usage of the C stack in Python to Python calls (GH-28488)
Ths commit inlines calls to Python functions in the eval loop and steals all the arguments in the call from the caller for performance.
1 parent ec04db7 commit b4903af

File tree

7 files changed

+222
-62
lines changed

7 files changed

+222
-62
lines changed

Include/internal/pycore_frame.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ typedef struct _interpreter_frame {
3131
int f_lasti; /* Last instruction if called */
3232
int stacktop; /* Offset of TOS from localsplus */
3333
PyFrameState f_state; /* What state the frame is in */
34+
int depth; /* Depth of the frame in a ceval loop */
3435
PyObject *localsplus[1];
3536
} InterpreterFrame;
3637

@@ -85,6 +86,7 @@ _PyFrame_InitializeSpecials(
8586
frame->generator = NULL;
8687
frame->f_lasti = -1;
8788
frame->f_state = FRAME_CREATED;
89+
frame->depth = 0;
8890
}
8991

9092
/* Gets the pointer to the locals array

Include/internal/pycore_tuple.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ extern "C" {
1313
#define _PyTuple_ITEMS(op) (_PyTuple_CAST(op)->ob_item)
1414

1515
extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
16+
extern PyObject *_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);
1617

1718
#ifdef __cplusplus
1819
}

Lib/test/gdb_sample.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Sample script for use by test_gdb.py
22

33
def foo(a, b, c):
4-
bar(a, b, c)
4+
bar(a=a, b=b, c=c)
55

66
def bar(a, b, c):
77
baz(a, b, c)

Lib/test/test_gdb.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -734,8 +734,14 @@ def test_pyup_command(self):
734734
cmds_after_breakpoint=['py-up', 'py-up'])
735735
self.assertMultilineMatches(bt,
736736
r'''^.*
737+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
738+
id\(42\)
737739
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
738740
baz\(a, b, c\)
741+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
742+
bar\(a=a, b=b, c=c\)
743+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
744+
foo\(1, 2, 3\)
739745
$''')
740746

741747
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
@@ -763,10 +769,18 @@ def test_up_then_down(self):
763769
cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
764770
self.assertMultilineMatches(bt,
765771
r'''^.*
772+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
773+
id\(42\)
766774
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
767775
baz\(a, b, c\)
776+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
777+
bar\(a=a, b=b, c=c\)
778+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
779+
foo\(1, 2, 3\)
768780
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
769781
id\(42\)
782+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
783+
baz\(a, b, c\)
770784
$''')
771785

772786
class PyBtTests(DebuggerTests):
@@ -785,7 +799,7 @@ def test_bt(self):
785799
File ".*gdb_sample.py", line 7, in bar
786800
baz\(a, b, c\)
787801
File ".*gdb_sample.py", line 4, in foo
788-
bar\(a, b, c\)
802+
bar\(a=a, b=b, c=c\)
789803
File ".*gdb_sample.py", line 12, in <module>
790804
foo\(1, 2, 3\)
791805
''')
@@ -801,7 +815,7 @@ def test_bt_full(self):
801815
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
802816
baz\(a, b, c\)
803817
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
804-
bar\(a, b, c\)
818+
bar\(a=a, b=b, c=c\)
805819
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
806820
foo\(1, 2, 3\)
807821
''')
@@ -1008,7 +1022,13 @@ def test_locals_after_up(self):
10081022
bt = self.get_stack_trace(script=self.get_sample_script(),
10091023
cmds_after_breakpoint=['py-up', 'py-up', 'py-locals'])
10101024
self.assertMultilineMatches(bt,
1011-
r".*\na = 1\nb = 2\nc = 3\n.*")
1025+
r'''^.*
1026+
Locals for foo
1027+
a = 1
1028+
b = 2
1029+
c = 3
1030+
Locals for <module>
1031+
.*$''')
10121032

10131033

10141034
def setUpModule():

Objects/tupleobject.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,26 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
484484
return (PyObject *)tuple;
485485
}
486486

487+
PyObject *
488+
_PyTuple_FromArraySteal(PyObject *const *src, Py_ssize_t n)
489+
{
490+
if (n == 0) {
491+
return tuple_get_empty();
492+
}
493+
494+
PyTupleObject *tuple = tuple_alloc(n);
495+
if (tuple == NULL) {
496+
return NULL;
497+
}
498+
PyObject **dst = tuple->ob_item;
499+
for (Py_ssize_t i = 0; i < n; i++) {
500+
PyObject *item = src[i];
501+
dst[i] = item;
502+
}
503+
_PyObject_GC_TRACK(tuple);
504+
return (PyObject *)tuple;
505+
}
506+
487507
static PyObject *
488508
tupleslice(PyTupleObject *a, Py_ssize_t ilow,
489509
Py_ssize_t ihigh)

0 commit comments

Comments
 (0)