Skip to content

Commit 2646724

Browse files
committed
pythongh-118702: Implement vectorcall for BaseException
* BaseException_vectorcall() now creates a tuple from 'args' array. * Creation an exception using BaseException_vectorcall() is now a single function call, rather than having to call BaseException_new() and then BaseException_init(). Calling BaseException_init() is inefficient since it overrides the 'args' attribute. * _PyErr_SetKeyError() now uses PyObject_CallOneArg() to create the KeyError instance to use BaseException_vectorcall(). Micro-benchmark on creating a KeyError on accessing a non-existent dictionary key: Mean +- std dev: 447 ns +- 31 ns -> 373 ns +- 15 ns: 1.20x faster
1 parent 698417f commit 2646724

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

Objects/exceptions.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,33 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
7878
return 0;
7979
}
8080

81+
82+
static PyObject *
83+
BaseException_vectorcall(PyObject *type_obj, PyObject * const*args,
84+
size_t nargsf, PyObject *kwnames)
85+
{
86+
PyTypeObject *type = _PyType_CAST(type_obj);
87+
PyBaseExceptionObject *self;
88+
89+
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
90+
if (!self)
91+
return NULL;
92+
/* the dict is created on the fly in PyObject_GenericSetAttr */
93+
self->dict = NULL;
94+
self->notes = NULL;
95+
self->traceback = self->cause = self->context = NULL;
96+
self->suppress_context = 0;
97+
98+
self->args = _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf));
99+
if (!self->args) {
100+
Py_DECREF(self);
101+
return NULL;
102+
}
103+
104+
return (PyObject *)self;
105+
}
106+
107+
81108
static int
82109
BaseException_clear(PyBaseExceptionObject *self)
83110
{
@@ -486,6 +513,7 @@ static PyTypeObject _PyExc_BaseException = {
486513
(initproc)BaseException_init, /* tp_init */
487514
0, /* tp_alloc */
488515
BaseException_new, /* tp_new */
516+
.tp_vectorcall = BaseException_vectorcall,
489517
};
490518
/* the CPython API expects exceptions to be (PyObject *) - both a hold-over
491519
from the previous implementation and also allowing Python objects to be used
@@ -3675,6 +3703,11 @@ _PyExc_InitTypes(PyInterpreterState *interp)
36753703
if (_PyStaticType_InitBuiltin(interp, exc) < 0) {
36763704
return -1;
36773705
}
3706+
if (exc->tp_new == BaseException_new
3707+
&& exc->tp_init == (initproc)BaseException_init)
3708+
{
3709+
exc->tp_vectorcall = BaseException_vectorcall;
3710+
}
36783711
}
36793712
return 0;
36803713
}

Python/errors.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,13 @@ void
257257
_PyErr_SetKeyError(PyObject *arg)
258258
{
259259
PyThreadState *tstate = _PyThreadState_GET();
260-
PyObject *tup = PyTuple_Pack(1, arg);
261-
if (!tup) {
260+
PyObject *exc = PyObject_CallOneArg(PyExc_KeyError, arg);
261+
if (!exc) {
262262
/* caller will expect error to be set anyway */
263263
return;
264264
}
265-
_PyErr_SetObject(tstate, PyExc_KeyError, tup);
266-
Py_DECREF(tup);
265+
266+
_PyErr_SetRaisedException(tstate, exc);
267267
}
268268

269269
void

0 commit comments

Comments
 (0)