Use PyObject_Vectorcall for calls to Python for better efficiency
No longer requires creating temporary tuple to hold the args. Introduced in Python 3.8.
This commit is contained in:
parent
8fe8862f06
commit
5a82ac3208
|
|
@ -332,8 +332,7 @@ add_done_callback(PyObject *self, PyObject *fn) {
|
|||
}
|
||||
|
||||
PythonTask *task = new PythonTask(fn);
|
||||
Py_DECREF(task->_args);
|
||||
task->_args = PyTuple_Pack(1, self);
|
||||
task->_args.assign(1, Py_NewRef(self));
|
||||
task->_append_task = false;
|
||||
task->_ignore_return = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ PythonTask::
|
|||
PythonTask(PyObject *func_or_coro, const std::string &name) :
|
||||
AsyncTask(name),
|
||||
_function(nullptr),
|
||||
_args(nullptr),
|
||||
_upon_death(nullptr),
|
||||
_owner(nullptr),
|
||||
_exception(nullptr),
|
||||
|
|
@ -45,6 +44,7 @@ PythonTask(PyObject *func_or_coro, const std::string &name) :
|
|||
_exc_traceback(nullptr),
|
||||
_generator(nullptr),
|
||||
_fut_waiter(nullptr),
|
||||
_append_task(true),
|
||||
_ignore_return(false),
|
||||
_registered_to_owner(false),
|
||||
_retrieved_exception(false) {
|
||||
|
|
@ -63,7 +63,6 @@ PythonTask(PyObject *func_or_coro, const std::string &name) :
|
|||
nassert_raise("Invalid function passed to PythonTask");
|
||||
}
|
||||
|
||||
set_args(Py_None, true);
|
||||
set_upon_death(Py_None);
|
||||
set_owner(Py_None);
|
||||
|
||||
|
|
@ -104,7 +103,9 @@ PythonTask::
|
|||
|
||||
// All of these may have already been cleared by __clear__.
|
||||
Py_XDECREF(_function);
|
||||
Py_XDECREF(_args);
|
||||
for (PyObject *arg : _args) {
|
||||
Py_DECREF(arg);
|
||||
}
|
||||
Py_XDECREF(__dict__);
|
||||
Py_XDECREF(_exception);
|
||||
Py_XDECREF(_exc_value);
|
||||
|
|
@ -134,21 +135,23 @@ set_function(PyObject *function) {
|
|||
*/
|
||||
void PythonTask::
|
||||
set_args(PyObject *args, bool append_task) {
|
||||
Py_XDECREF(_args);
|
||||
_args = nullptr;
|
||||
pvector<PyObject *> old_args = std::exchange(_args, {});
|
||||
|
||||
if (args == Py_None) {
|
||||
// None means no arguments; create an empty tuple.
|
||||
_args = PyTuple_New(0);
|
||||
} else {
|
||||
if (PySequence_Check(args)) {
|
||||
_args = PySequence_Tuple(args);
|
||||
if (PySequence_Check(args)) {
|
||||
Py_ssize_t len = PySequence_Size(args);
|
||||
_args.resize(len);
|
||||
|
||||
for (Py_ssize_t i = 0; i < len; ++i) {
|
||||
_args[i] = PySequence_GetItem(args, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (_args == nullptr) {
|
||||
else if (args != Py_None) {
|
||||
nassert_raise("Invalid args passed to PythonTask");
|
||||
_args = PyTuple_New(0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (PyObject *old_arg : old_args) {
|
||||
Py_DECREF(old_arg);
|
||||
}
|
||||
|
||||
_append_task = append_task;
|
||||
|
|
@ -159,19 +162,18 @@ set_args(PyObject *args, bool append_task) {
|
|||
*/
|
||||
PyObject *PythonTask::
|
||||
get_args() {
|
||||
// If we want to append the task, we have to create a new tuple with space
|
||||
// for one more at the end. We have to do this dynamically each time, to
|
||||
// avoid storing the task itself in its own arguments list, and thereby
|
||||
// creating a cyclical reference.
|
||||
|
||||
size_t num_args = _args.size();
|
||||
PyObject *result = PyTuple_New(num_args + _append_task);
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
PyTuple_SET_ITEM(result, i, Py_NewRef(_args[i]));
|
||||
}
|
||||
|
||||
if (_append_task) {
|
||||
// If we want to append the task, we have to create a new tuple with space
|
||||
// for one more at the end. We have to do this dynamically each time, to
|
||||
// avoid storing the task itself in its own arguments list, and thereby
|
||||
// creating a cyclical reference.
|
||||
|
||||
int num_args = PyTuple_GET_SIZE(_args);
|
||||
PyObject *with_task = PyTuple_New(num_args + 1);
|
||||
for (int i = 0; i < num_args; ++i) {
|
||||
PyObject *item = PyTuple_GET_ITEM(_args, i);
|
||||
PyTuple_SET_ITEM(with_task, i, Py_NewRef(item));
|
||||
}
|
||||
|
||||
// Check whether we have a Python wrapper. This is not the case if the
|
||||
// object has been created by C++ and never been exposed to Python code.
|
||||
if (__self__ == nullptr) {
|
||||
|
|
@ -180,12 +182,9 @@ get_args() {
|
|||
__self__ = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(with_task, num_args, Py_NewRef(__self__));
|
||||
return with_task;
|
||||
}
|
||||
else {
|
||||
return Py_NewRef(_args);
|
||||
PyTuple_SET_ITEM(result, num_args, Py_NewRef(__self__));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -376,7 +375,9 @@ int PythonTask::
|
|||
__traverse__(visitproc visit, void *arg) {
|
||||
Py_VISIT(__self__);
|
||||
Py_VISIT(_function);
|
||||
Py_VISIT(_args);
|
||||
for (PyObject *arg : _args) {
|
||||
Py_VISIT(arg);
|
||||
}
|
||||
Py_VISIT(_upon_death);
|
||||
Py_VISIT(_owner);
|
||||
Py_VISIT(__dict__);
|
||||
|
|
@ -390,7 +391,12 @@ __traverse__(visitproc visit, void *arg) {
|
|||
int PythonTask::
|
||||
__clear__() {
|
||||
Py_CLEAR(_function);
|
||||
Py_CLEAR(_args);
|
||||
{
|
||||
pvector<PyObject *> old_args = std::exchange(_args, {});
|
||||
for (PyObject *arg : old_args) {
|
||||
Py_DECREF(arg);
|
||||
}
|
||||
}
|
||||
Py_CLEAR(_upon_death);
|
||||
Py_CLEAR(_owner);
|
||||
Py_CLEAR(__dict__);
|
||||
|
|
@ -590,9 +596,21 @@ do_python_task() {
|
|||
// We are calling the function directly.
|
||||
nassertr(_function != nullptr, DS_interrupt);
|
||||
|
||||
PyObject *args = get_args();
|
||||
result = PythonThread::call_python_func(_function, args);
|
||||
Py_DECREF(args);
|
||||
size_t nargs = _args.size();
|
||||
PyObject **args = (PyObject **)alloca(sizeof(PyObject *) * (nargs + 2)) + 1;
|
||||
std::copy(_args.begin(), _args.end(), args);
|
||||
|
||||
if (_append_task) {
|
||||
// Check whether we have a Python wrapper. This is not the case if the
|
||||
// object has been created by C++ and never been exposed to Python code.
|
||||
if (__self__ == nullptr) {
|
||||
// A __self__ instance does not exist, let's create one now.
|
||||
ref();
|
||||
__self__ = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
|
||||
}
|
||||
args[nargs++] = __self__;
|
||||
}
|
||||
result = PythonThread::call_python_func(_function, args, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
|
||||
if (result != nullptr && PyGen_Check(result)) {
|
||||
// The function has yielded a generator. We will call into that
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ private:
|
|||
|
||||
private:
|
||||
PyObject *_function;
|
||||
PyObject *_args;
|
||||
pvector<PyObject *> _args;
|
||||
PyObject *_upon_death;
|
||||
PyObject *_owner;
|
||||
|
||||
|
|
|
|||
|
|
@ -101,17 +101,19 @@ pre_load(const Filename &orig_filename, const Filename &orig_alpha_filename,
|
|||
#endif
|
||||
|
||||
// Wrap the arguments.
|
||||
PyObject *args = Py_BuildValue("(OOiiNO)",
|
||||
DTool_CreatePyInstance((void *)&orig_filename, Dtool_Filename, false, true),
|
||||
DTool_CreatePyInstance((void *)&orig_alpha_filename, Dtool_Filename, false, true),
|
||||
primary_file_num_channels,
|
||||
alpha_file_channel,
|
||||
PyBool_FromLong(read_mipmaps),
|
||||
DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true)
|
||||
);
|
||||
PyObject *args[7];
|
||||
args[1] = DTool_CreatePyInstance((void *)&orig_filename, Dtool_Filename, false, true);
|
||||
args[2] = DTool_CreatePyInstance((void *)&orig_alpha_filename, Dtool_Filename, false, true);
|
||||
args[3] = PyLong_FromLong(primary_file_num_channels);
|
||||
args[4] = PyLong_FromLong(alpha_file_channel);
|
||||
args[5] = PyBool_FromLong(read_mipmaps);
|
||||
args[6] = DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true);
|
||||
|
||||
PyObject *result = PythonThread::call_python_func(_pre_load_func, args);
|
||||
Py_DECREF(args);
|
||||
PyObject *result = PythonThread::call_python_func(_pre_load_func, args + 1, 6 | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
|
||||
for (size_t i = 1; i < 7; ++i) {
|
||||
Py_DECREF(args[i]);
|
||||
}
|
||||
|
||||
PT(Texture) tex;
|
||||
if (result != nullptr) {
|
||||
|
|
@ -162,11 +164,11 @@ post_load(Texture *tex) {
|
|||
#endif
|
||||
|
||||
// Wrap the arguments.
|
||||
PyObject *args = PyTuple_Pack(1,
|
||||
DTool_CreatePyInstance(tex, Dtool_Texture, true, false)
|
||||
);
|
||||
PyObject *result = PythonThread::call_python_func(_post_load_func, args);
|
||||
Py_DECREF(args);
|
||||
PyObject *args[2];
|
||||
args[1] = DTool_CreatePyInstance(tex, Dtool_Texture, true, false);
|
||||
|
||||
PyObject *result = PythonThread::call_python_func(_post_load_func, args + 1, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
Py_DECREF(args[1]);
|
||||
|
||||
PT(Texture) result_tex;
|
||||
if (result != nullptr) {
|
||||
|
|
|
|||
|
|
@ -305,19 +305,19 @@ load_file(const Filename &path, const LoaderOptions &options,
|
|||
#endif
|
||||
|
||||
// Wrap the arguments.
|
||||
PyObject *args = PyTuple_New(3);
|
||||
PyTuple_SET_ITEM(args, 0, DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true));
|
||||
PyTuple_SET_ITEM(args, 1, DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true));
|
||||
PyObject *args[4];
|
||||
args[1] = DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true);
|
||||
args[2] = DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true);
|
||||
if (record != nullptr) {
|
||||
record->ref();
|
||||
PyTuple_SET_ITEM(args, 2, DTool_CreatePyInstanceTyped((void *)record, Dtool_BamCacheRecord, true, false, record->get_type_index()));
|
||||
args[3] = DTool_CreatePyInstanceTyped((void *)record, Dtool_BamCacheRecord, true, false, record->get_type_index());
|
||||
} else {
|
||||
PyTuple_SET_ITEM(args, 2, Py_NewRef(Py_None));
|
||||
args[3] = Py_NewRef(Py_None);
|
||||
}
|
||||
|
||||
PT(PandaNode) node;
|
||||
|
||||
PyObject *result = PythonThread::call_python_func(_load_func, args);
|
||||
PyObject *result = PythonThread::call_python_func(_load_func, args + 1, 3 | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
if (result != nullptr) {
|
||||
if (DtoolInstance_Check(result)) {
|
||||
node = (PandaNode *)DtoolInstance_UPCAST(result, Dtool_PandaNode);
|
||||
|
|
@ -325,7 +325,9 @@ load_file(const Filename &path, const LoaderOptions &options,
|
|||
Py_DECREF(result);
|
||||
}
|
||||
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(args[1]);
|
||||
Py_DECREF(args[2]);
|
||||
Py_DECREF(args[3]);
|
||||
|
||||
if (node == nullptr) {
|
||||
PyObject *exc_type = PyErr_Occurred();
|
||||
|
|
@ -372,13 +374,16 @@ save_file(const Filename &path, const LoaderOptions &options,
|
|||
#endif
|
||||
|
||||
// Wrap the arguments.
|
||||
PyObject *args = PyTuple_New(3);
|
||||
PyTuple_SET_ITEM(args, 0, DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true));
|
||||
PyTuple_SET_ITEM(args, 1, DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true));
|
||||
PyTuple_SET_ITEM(args, 2, DTool_CreatePyInstanceTyped((void *)node, Dtool_PandaNode, true, false, node->get_type_index()));
|
||||
PyObject *args[4];
|
||||
args[1] = DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true);
|
||||
args[2] = DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true);
|
||||
args[3] = DTool_CreatePyInstanceTyped((void *)node, Dtool_PandaNode, true, false, node->get_type_index());
|
||||
|
||||
PyObject *result = PythonThread::call_python_func(_save_func, args + 1, 3 | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
Py_DECREF(args[1]);
|
||||
Py_DECREF(args[2]);
|
||||
Py_DECREF(args[3]);
|
||||
|
||||
PyObject *result = PythonThread::call_python_func(_load_func, args);
|
||||
Py_DECREF(args);
|
||||
if (result != nullptr) {
|
||||
Py_DECREF(result);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ set_args(PyObject *args) {
|
|||
* exception.
|
||||
*/
|
||||
PyObject *PythonThread::
|
||||
call_python_func(PyObject *function, PyObject *args) {
|
||||
call_python_func(PyObject *function, PyObject **args, size_t nargsf) {
|
||||
Thread *current_thread = get_current_thread();
|
||||
|
||||
// Create a new Python thread state data structure, so Python can properly
|
||||
|
|
@ -132,7 +132,7 @@ call_python_func(PyObject *function, PyObject *args) {
|
|||
|
||||
if (current_thread == get_main_thread()) {
|
||||
// In the main thread, just call the function.
|
||||
result = PyObject_Call(function, args, nullptr);
|
||||
result = _PyObject_Vectorcall(function, args, nargsf, nullptr);
|
||||
|
||||
if (result == nullptr) {
|
||||
if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
||||
|
|
@ -189,7 +189,7 @@ call_python_func(PyObject *function, PyObject *args) {
|
|||
PyThreadState_Swap(new_thread_state);
|
||||
|
||||
// Call the user's function.
|
||||
result = PyObject_Call(function, args, nullptr);
|
||||
result = _PyObject_Vectorcall(function, args, nargsf, nullptr);
|
||||
if (result == nullptr && PyErr_Occurred()) {
|
||||
// We got an exception. Move the exception from the current thread into
|
||||
// the main thread, so it can be handled there.
|
||||
|
|
@ -230,7 +230,7 @@ call_python_func(PyObject *function, PyObject *args) {
|
|||
gstate = PyGILState_Ensure();
|
||||
|
||||
// Call the user's function.
|
||||
result = PyObject_Call(function, args, nullptr);
|
||||
result = _PyObject_Vectorcall(function, args, nargsf, nullptr);
|
||||
if (result == nullptr && PyErr_Occurred()) {
|
||||
// We got an exception. Move the exception from the current thread into
|
||||
// the main thread, so it can be handled there.
|
||||
|
|
@ -275,7 +275,9 @@ call_python_func(PyObject *function, PyObject *args) {
|
|||
*/
|
||||
void PythonThread::
|
||||
thread_main() {
|
||||
_result = call_python_func(_function, _args);
|
||||
PyObject **args = &PyTuple_GET_ITEM(_args, 0);
|
||||
Py_ssize_t nargs = PyTuple_GET_SIZE(_args);
|
||||
_result = call_python_func(_function, args, (size_t)nargs);
|
||||
}
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ public:
|
|||
PyObject *get_args() const;
|
||||
void set_args(PyObject *);
|
||||
|
||||
static PyObject *call_python_func(PyObject *function, PyObject *args);
|
||||
static PyObject *call_python_func(PyObject *function, PyObject **args,
|
||||
size_t nargsf);
|
||||
|
||||
PUBLISHED:
|
||||
MAKE_PROPERTY(args, get_args, set_args);
|
||||
|
|
|
|||
|
|
@ -41,15 +41,14 @@ static TypedWritable *factory_callback(const FactoryParams ¶ms){
|
|||
BamReader *manager;
|
||||
parse_params(params, scan, manager);
|
||||
|
||||
PyObject *py_scan = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
|
||||
PyObject *py_manager = DTool_CreatePyInstance(manager, Dtool_BamReader, false, false);
|
||||
PyObject *args = PyTuple_Pack(2, py_scan, py_manager);
|
||||
PyObject *args[3];
|
||||
args[1] = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
|
||||
args[2] = DTool_CreatePyInstance(manager, Dtool_BamReader, false, false);
|
||||
|
||||
// Now call the Python function.
|
||||
PyObject *result = PythonThread::call_python_func(func, args);
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(py_scan);
|
||||
Py_DECREF(py_manager);
|
||||
PyObject *result = PythonThread::call_python_func(func, args + 1, 2 | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
Py_DECREF(args[1]);
|
||||
Py_DECREF(args[2]);
|
||||
|
||||
if (result == nullptr) {
|
||||
util_cat.error()
|
||||
|
|
|
|||
|
|
@ -129,16 +129,13 @@ void PythonCallbackObject::
|
|||
do_python_callback(CallbackData *cbdata) {
|
||||
nassertv(cbdata != nullptr);
|
||||
|
||||
// Wrap the cbdata up in a Python object, then put it in a tuple, for the
|
||||
// argument list.
|
||||
PyObject *pycbdata =
|
||||
DTool_CreatePyInstanceTyped(cbdata, Dtool_TypedObject,
|
||||
false, false, cbdata->get_type_index());
|
||||
PyObject *args = Py_BuildValue("(O)", pycbdata);
|
||||
Py_DECREF(pycbdata);
|
||||
// Wrap the cbdata up in a Python object.
|
||||
PyObject *args[2];
|
||||
args[1] = DTool_CreatePyInstanceTyped(cbdata, Dtool_TypedObject,
|
||||
false, false, cbdata->get_type_index());
|
||||
|
||||
PyObject *result = PythonThread::call_python_func(_function, args);
|
||||
Py_DECREF(args);
|
||||
PyObject *result = PythonThread::call_python_func(_function, args + 1, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET);
|
||||
Py_DECREF(args[1]);
|
||||
|
||||
if (result == nullptr) {
|
||||
if (PyErr_Occurred() != PyExc_SystemExit) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue