From 9aa341767e8b01f470a1c0ecc4259849be7f67b1 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Tue, 15 May 2018 11:32:37 -0400 Subject: [PATCH 1/2] bpo-33521 Optimize asyncio.isfuture by providing C implementation. --- Modules/_asynciomodule.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index d66cc4cf0a5f5f..190cd8f9b95d7a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -27,6 +27,7 @@ static PyObject *traceback_extract_stack; static PyObject *asyncio_get_event_loop_policy; static PyObject *asyncio_future_repr_info_func; static PyObject *asyncio_iscoroutine_func; +static PyObject *asyncio_isfuture_func; static PyObject *asyncio_task_get_stack_func; static PyObject *asyncio_task_print_stack_func; static PyObject *asyncio_task_repr_info_func; @@ -194,6 +195,19 @@ is_coroutine(PyObject *coro) } +static inline int +is_future(PyObject *fut) +{ + printf("is_future is called."); + if (PyObject_HasAttr( + Py_TYPE(fut), "_asyncio_future_blocking" + ) && PyObject_GetAttr(fut, "_asyncio_future_blocking") != Py_None) { + return 1; + } + return 0; +} + + static PyObject * get_future_loop(PyObject *fut) { @@ -3170,6 +3184,7 @@ module_free(void *m) Py_CLEAR(asyncio_future_repr_info_func); Py_CLEAR(asyncio_get_event_loop_policy); Py_CLEAR(asyncio_iscoroutine_func); + Py_CLEAR(asyncio_isfuture_func); Py_CLEAR(asyncio_task_get_stack_func); Py_CLEAR(asyncio_task_print_stack_func); Py_CLEAR(asyncio_task_repr_info_func); @@ -3236,6 +3251,7 @@ module_init(void) GET_MOD_ATTR(asyncio_future_repr_info_func, "_future_repr_info") GET_MOD_ATTR(asyncio_InvalidStateError, "InvalidStateError") GET_MOD_ATTR(asyncio_CancelledError, "CancelledError") + GET_MOD_ATTR(asyncio_isfuture_func, "isfuture") WITH_MOD("asyncio.base_tasks") GET_MOD_ATTR(asyncio_task_repr_info_func, "_task_repr_info") From d689d5fe6ef8cce20e647dd7ebba7dd3cb051d99 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Tue, 15 May 2018 12:49:09 -0400 Subject: [PATCH 2/2] bpo-33521: Add 1.32x faster C implementation of asyncio.isfuture(). --- Lib/asyncio/futures.py | 1 + .../2018-05-15-16-00-08.bpo-33521.GjWHfn.rst | 1 + Modules/_asynciomodule.c | 54 ++++++++++++++----- Modules/clinic/_asynciomodule.c.h | 15 +++++- 4 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-05-15-16-00-08.bpo-33521.GjWHfn.rst diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 4a56f32c7745c9..c0bae6b6e169e5 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -381,4 +381,5 @@ def wrap_future(future, *, loop=None): pass else: # _CFuture is needed for tests. + isfuture = _asyncio.isfuture Future = _CFuture = _asyncio.Future diff --git a/Misc/NEWS.d/next/Library/2018-05-15-16-00-08.bpo-33521.GjWHfn.rst b/Misc/NEWS.d/next/Library/2018-05-15-16-00-08.bpo-33521.GjWHfn.rst new file mode 100644 index 00000000000000..671f7795989e55 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-15-16-00-08.bpo-33521.GjWHfn.rst @@ -0,0 +1 @@ +Add 1.32x faster C implementation of asyncio.isfuture(). diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 190cd8f9b95d7a..86e96e5e82816e 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -27,7 +27,6 @@ static PyObject *traceback_extract_stack; static PyObject *asyncio_get_event_loop_policy; static PyObject *asyncio_future_repr_info_func; static PyObject *asyncio_iscoroutine_func; -static PyObject *asyncio_isfuture_func; static PyObject *asyncio_task_get_stack_func; static PyObject *asyncio_task_print_stack_func; static PyObject *asyncio_task_repr_info_func; @@ -194,17 +193,49 @@ is_coroutine(PyObject *coro) return has_it; } +/*[clinic input] +_asyncio.isfuture -static inline int -is_future(PyObject *fut) + obj: object + / + +Return True if obj is a Future instance. + +This returns True when obj is a Future instance or is advertising +itself as duck-type compatible by setting _asyncio_future_blocking. +See comment in Future for more details. + +[clinic start generated code]*/ + +static PyObject * +_asyncio_isfuture(PyObject *module, PyObject *obj) +/*[clinic end generated code: output=3c79d083f507d4fa input=a71fdef4d9b354b4]*/ { - printf("is_future is called."); - if (PyObject_HasAttr( - Py_TYPE(fut), "_asyncio_future_blocking" - ) && PyObject_GetAttr(fut, "_asyncio_future_blocking") != Py_None) { - return 1; - } - return 0; + _Py_IDENTIFIER(__class__); + PyObject* class = _PyObject_GetAttrId(obj, &PyId___class__); + if (class == NULL) { + return NULL; + } + _Py_IDENTIFIER(_asyncio_future_blocking); + int class_has_attr = _PyObject_HasAttrId( + class, &PyId__asyncio_future_blocking); + Py_DECREF(class); + if (class_has_attr < 0) { + return NULL; + } + if (!class_has_attr) { + Py_RETURN_FALSE; + } + PyObject* obj_attr = _PyObject_GetAttrId(obj, &PyId__asyncio_future_blocking); + if (obj_attr == NULL) { + return NULL; + } + if (obj_attr != Py_None) { + Py_DECREF(obj_attr); + Py_RETURN_TRUE; + } + Py_DECREF(obj_attr); + Py_RETURN_FALSE; } @@ -3184,7 +3215,6 @@ module_free(void *m) Py_CLEAR(asyncio_future_repr_info_func); Py_CLEAR(asyncio_get_event_loop_policy); Py_CLEAR(asyncio_iscoroutine_func); - Py_CLEAR(asyncio_isfuture_func); Py_CLEAR(asyncio_task_get_stack_func); Py_CLEAR(asyncio_task_print_stack_func); Py_CLEAR(asyncio_task_repr_info_func); @@ -3251,7 +3281,6 @@ module_init(void) GET_MOD_ATTR(asyncio_future_repr_info_func, "_future_repr_info") GET_MOD_ATTR(asyncio_InvalidStateError, "InvalidStateError") GET_MOD_ATTR(asyncio_CancelledError, "CancelledError") - GET_MOD_ATTR(asyncio_isfuture_func, "isfuture") WITH_MOD("asyncio.base_tasks") GET_MOD_ATTR(asyncio_task_repr_info_func, "_task_repr_info") @@ -3292,6 +3321,7 @@ PyDoc_STRVAR(module_doc, "Accelerator module for asyncio"); static PyMethodDef asyncio_methods[] = { _ASYNCIO_GET_EVENT_LOOP_METHODDEF + _ASYNCIO_ISFUTURE_METHODDEF _ASYNCIO_GET_RUNNING_LOOP_METHODDEF _ASYNCIO__GET_RUNNING_LOOP_METHODDEF _ASYNCIO__SET_RUNNING_LOOP_METHODDEF diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index d8e1b6a7987956..f2ff3cb558c301 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -2,6 +2,19 @@ preserve [clinic start generated code]*/ +PyDoc_STRVAR(_asyncio_isfuture__doc__, +"isfuture($module, obj, /)\n" +"--\n" +"\n" +"Return True if obj is a Future instance.\n" +"\n" +"This returns True when obj is a Future instance or is advertising\n" +"itself as duck-type compatible by setting _asyncio_future_blocking.\n" +"See comment in Future for more details."); + +#define _ASYNCIO_ISFUTURE_METHODDEF \ + {"isfuture", (PyCFunction)_asyncio_isfuture, METH_O, _asyncio_isfuture__doc__}, + PyDoc_STRVAR(_asyncio_Future___init____doc__, "Future(*, loop=None)\n" "--\n" @@ -711,4 +724,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs, exit: return return_value; } -/*[clinic end generated code: output=b6148b0134e7a819 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9ddba804d97cdf0d input=a9049054013a1b77]*/