Commit 2cf0cd11 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Improve capi exception creation speed

Our exception throwing is still slower than CPython's, and it looks
like it's partially due to the CAPI calls we do as part of the throwing
process.  For example, we have to actually create an exception object,
which involves calling the exception type constructor, etc.  We could
try to jit this stuff, but for now add another fast-path to typeCall
that should catch some of this.

Also, add a fast-path that inlines most of the exception-creation behavior.
Looks like this isn't too much faster than the above-mentioned fast-path.
parent 22962bc5
......@@ -100,16 +100,16 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**) PY
/* */
// Pyston change: made these function calls for now
#if 0
#define PyExceptionClass_Check(x) \
(PyClass_Check((x)) || (PyType_Check((x)) && \
PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS)))
#define PyExceptionInstance_Check(x) \
(PyInstance_Check((x)) || \
PyType_FastSubclass((x)->ob_type, Py_TPFLAGS_BASE_EXC_SUBCLASS))
PyType_FastSubclass(Py_TYPE(x), Py_TPFLAGS_BASE_EXC_SUBCLASS))
// Pyston change: made these function calls for now
#if 0
#define PyExceptionClass_Name(x) \
(PyClass_Check((x)) \
? PyString_AS_STRING(((PyClassObject*)(x))->cl_name) \
......@@ -121,8 +121,6 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**) PY
: (PyObject*)((x)->ob_type)))
#endif
// (We might have to make these wrapper macros that do appropriate casting to PyObject)
PyAPI_FUNC(int) PyExceptionClass_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyExceptionInstance_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(const char*) PyExceptionClass_Name(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(PyObject*) PyExceptionInstance_Class(PyObject*) PYSTON_NOEXCEPT;
......@@ -244,6 +242,14 @@ PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc(
char *name, char *doc, PyObject *base, PyObject *dict) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *) PYSTON_NOEXCEPT;
// Pyston addition: allocate + initialize an instance of type `type`.
// arg represents the single value that will be passed to the constructor;
// a NULL value represents passing zero arguments, and a tuple value will
// not be expanded into multiple arguments.
// In the common cases this will be faster than creating the instance using
// PyObject_Call(type, PyTuple_Pack(1, arg), NULL)
PyAPI_FUNC(PyObject *) PyErr_CreateExceptionInstance(PyObject* type, PyObject* arg) PYSTON_NOEXCEPT;
/* In sigcheck.c or signalmodule.c */
PyAPI_FUNC(int) PyErr_CheckSignals(void) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) PyErr_SetInterrupt(void) PYSTON_NOEXCEPT;
......
......@@ -72,6 +72,46 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
return 0;
}
PyObject* PyErr_CreateExceptionInstance(PyObject* _type, PyObject* arg) {
if (PyType_Check(_type) && ((PyTypeObject*)_type)->tp_new == (newfunc)BaseException_new &&
((PyTypeObject*)_type)->tp_init == (initproc)BaseException_init) {
PyTypeObject* type = (PyTypeObject*)_type;
// Fast path: inline new and init
PyBaseExceptionObject *self;
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
if (!self)
return NULL;
self->dict = NULL;
if (arg) {
self->args = PyTuple_Pack(1, arg);
if (!self->args)
return NULL;
self->message = arg;
} else {
self->args = PyTuple_New(0);
self->message = PyString_FromString("");
if (!self->message)
return NULL;
}
return (PyObject*)self;
} else {
// Fallback
PyObject* args;
if (arg == NULL)
args = PyTuple_New(0);
else
args = PyTuple_Pack(1, arg);
if (args == NULL)
return NULL;
return PyObject_Call(_type, args, NULL);
}
}
static int
BaseException_clear(PyBaseExceptionObject *self)
{
......
......@@ -703,23 +703,21 @@ extern "C" void PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObjec
class.
*/
if (!inclass || !PyObject_IsSubclass(inclass, type)) {
PyObject* args, *res;
// Pyston change: rewrote this section
if (value == Py_None)
args = PyTuple_New(0);
else if (PyTuple_Check(value)) {
Py_INCREF(value);
args = value;
} else
args = PyTuple_Pack(1, value);
PyObject* res;
if (!PyTuple_Check(value)) {
res = PyErr_CreateExceptionInstance(type, value == Py_None ? NULL : value);
} else {
PyObject* args = value;
// Pyston change:
// res = PyEval_CallObject(type, args);
res = PyObject_Call(type, args, NULL);
}
if (args == NULL)
goto finally;
res = PyEval_CallObject(type, args);
Py_DECREF(args);
if (res == NULL)
goto finally;
Py_DECREF(value);
value = res;
}
/* if the class of the instance doesn't exactly match the
......@@ -909,14 +907,6 @@ extern "C" PyObject* PyErr_NoMemory() noexcept {
return nullptr;
}
extern "C" int PyExceptionClass_Check(PyObject* o) noexcept {
return PyClass_Check(o) || (PyType_Check(o) && isSubclass(static_cast<BoxedClass*>(o), BaseException));
}
extern "C" int PyExceptionInstance_Check(PyObject* o) noexcept {
return PyInstance_Check(o) || isSubclass(o->cls, BaseException);
}
extern "C" const char* PyExceptionClass_Name(PyObject* o) noexcept {
return PyClass_Check(o) ? PyString_AS_STRING(static_cast<BoxedClassobj*>(o)->name)
: static_cast<BoxedClass*>(o)->tp_name;
......
......@@ -599,6 +599,35 @@ extern "C" CLFunction* unboxCLFunction(Box* b) {
return static_cast<BoxedFunction*>(b)->f;
}
static PyObject* cpython_type_call(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept {
PyObject* obj;
if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name);
return NULL;
}
obj = type->tp_new(type, args, kwds);
if (obj != NULL) {
/* Ugly exception: when the call was type(something),
* don't call tp_init on the result. */
if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1
&& (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
* it won't be initialized. */
if (!PyType_IsSubtype(obj->cls, type))
return obj;
type = obj->cls;
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL
&& type->tp_init(obj, args, kwds) < 0) {
Py_DECREF(obj);
obj = NULL;
}
}
return obj;
}
template <ExceptionStyle S>
static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI);
......@@ -608,6 +637,25 @@ static Box* typeTppCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSpec ar
Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI) {
int npassed_args = argspec.totalPassed();
// Common CAPI path call this function with *args, **kw.
if (argspec == ArgPassSpec(0, 0, true, false) || argspec == ArgPassSpec(0, 0, true, true)) {
// Wouldn't be able to rewrite anyway:
assert(!rewrite_args || !rewrite_args->out_success);
arg1 = PySequence_Tuple(arg1);
if (!arg1) {
if (S == CAPI)
return NULL;
else
throwCAPIException();
}
Box* r = cpython_type_call(static_cast<BoxedClass*>(self), arg1, argspec.has_kwargs ? arg2 : NULL);
if (S == CXX && !r)
throwCAPIException();
return r;
}
if (argspec.has_starargs || argspec.has_kwargs) {
// This would fail in typeCallInner
rewrite_args = NULL;
......@@ -654,35 +702,6 @@ static void assertInitNone(Box* obj) {
}
}
static PyObject* cpython_type_call(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept {
PyObject* obj;
if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name);
return NULL;
}
obj = type->tp_new(type, args, kwds);
if (obj != NULL) {
/* Ugly exception: when the call was type(something),
* don't call tp_init on the result. */
if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1
&& (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
* it won't be initialized. */
if (!PyType_IsSubtype(obj->cls, type))
return obj;
type = obj->cls;
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL
&& type->tp_init(obj, args, kwds) < 0) {
Py_DECREF(obj);
obj = NULL;
}
}
return obj;
}
static PyObject* cpythonTypeCall(BoxedClass* type, PyObject* args, PyObject* kwds) {
Box* r = cpython_type_call(type, args, kwds);
if (!r)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment