summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2018-12-30 09:24:03 (GMT)
committerRaymond Hettinger <rhettinger@users.noreply.github.com>2018-12-30 09:24:03 (GMT)
commit3f5fc70c6213008243e7d605f7d8a2d8f94cf919 (patch)
tree44c396f3a052803c4725eec2e6ccb1ab32529c8f /Modules
parentb0a6196ffd58ff91462191f426706897dc920eee (diff)
downloadcpython-3f5fc70c6213008243e7d605f7d8a2d8f94cf919.zip
cpython-3f5fc70c6213008243e7d605f7d8a2d8f94cf919.tar.gz
cpython-3f5fc70c6213008243e7d605f7d8a2d8f94cf919.tar.bz2
bpo-32492: 1.6x speed up in namedtuple attribute access using C fast-path (#10495)
* bpo-32492: 2.5x speed up in namedtuple attribute access using C fast path * Add News entry * fixup! bpo-32492: 2.5x speed up in namedtuple attribute access using C fast path * Check for tuple in the __get__ of the new descriptor and don't cache the descriptor itself * Don't inherit from property. Implement GC methods to handle __doc__ * Add a test for the docstring substitution in descriptors * Update NEWS entry to reflect time against 3.7 branch * Simplify implementation with argument clinic, better error messages, only __new__ * Use positional-only parameters for the __new__ * Use PyTuple_GET_SIZE and PyTuple_GET_ITEM to tighter the implementation of tuplegetterdescr_get * Implement __set__ to make tuplegetter a data descriptor * Use Py_INCREF now that we inline PyTuple_GetItem * Apply the valid_index() function, saving one test * Move Py_None test out of the critical path.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_collectionsmodule.c163
-rw-r--r--Modules/clinic/_collectionsmodule.c.h28
2 files changed, 191 insertions, 0 deletions
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index 4da0662..cc325e1 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -7,6 +7,14 @@
#include <sys/types.h> /* For size_t */
#endif
+/*[clinic input]
+class _tuplegetter "_tuplegetterobject *" "&tuplegetter_type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ee5ed5baabe35068]*/
+
+static PyTypeObject tuplegetter_type;
+#include "clinic/_collectionsmodule.c.h"
+
/* collections module implementation of a deque() datatype
Written and maintained by Raymond D. Hettinger <python@rcn.com>
*/
@@ -2328,6 +2336,156 @@ done:
Py_RETURN_NONE;
}
+/* Helper functions for namedtuples */
+
+typedef struct {
+ PyObject_HEAD
+ Py_ssize_t index;
+ PyObject* doc;
+} _tuplegetterobject;
+
+/*[clinic input]
+@classmethod
+_tuplegetter.__new__ as tuplegetter_new
+
+ index: Py_ssize_t
+ doc: object
+ /
+[clinic start generated code]*/
+
+static PyObject *
+tuplegetter_new_impl(PyTypeObject *type, Py_ssize_t index, PyObject *doc)
+/*[clinic end generated code: output=014be444ad80263f input=87c576a5bdbc0bbb]*/
+{
+ _tuplegetterobject* self;
+ self = (_tuplegetterobject *)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->index = index;
+ Py_INCREF(doc);
+ self->doc = doc;
+ return (PyObject *)self;
+}
+
+static PyObject *
+tuplegetterdescr_get(PyObject *self, PyObject *obj, PyObject *type)
+{
+ PyObject *result;
+ if (obj == NULL) {
+ Py_INCREF(self);
+ return self;
+ }
+ if (!PyTuple_Check(obj)) {
+ if (obj == Py_None) {
+ Py_INCREF(self);
+ return self;
+ }
+ PyErr_Format(PyExc_TypeError,
+ "descriptor for index '%d' for tuple subclasses "
+ "doesn't apply to '%s' object",
+ ((_tuplegetterobject*)self)->index,
+ obj->ob_type->tp_name);
+ return NULL;
+ }
+
+ Py_ssize_t index = ((_tuplegetterobject*)self)->index;
+
+ if (!valid_index(index, PyTuple_GET_SIZE(obj))) {
+ PyErr_SetString(PyExc_IndexError, "tuple index out of range");
+ return NULL;
+ }
+
+ result = PyTuple_GET_ITEM(obj, index);
+ Py_INCREF(result);
+ return result;
+}
+
+static int
+tuplegetter_set(PyObject *self, PyObject *obj, PyObject *value)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "can't delete attribute");
+ } else {
+ PyErr_SetString(PyExc_AttributeError, "can't set attribute");
+ }
+ return -1;
+}
+
+static int
+tuplegetter_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ _tuplegetterobject *tuplegetter = (_tuplegetterobject *)self;
+ Py_VISIT(tuplegetter->doc);
+ return 0;
+}
+
+static int
+tuplegetter_clear(PyObject *self)
+{
+ _tuplegetterobject *tuplegetter = (_tuplegetterobject *)self;
+ Py_CLEAR(tuplegetter->doc);
+ return 0;
+}
+
+static void
+tuplegetter_dealloc(_tuplegetterobject *self)
+{
+ PyObject_GC_UnTrack(self);
+ tuplegetter_clear((PyObject*)self);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+
+static PyMemberDef tuplegetter_members[] = {
+ {"__doc__", T_OBJECT, offsetof(_tuplegetterobject, doc), 0},
+ {0}
+};
+
+static PyTypeObject tuplegetter_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_collections._tuplegetter", /* tp_name */
+ sizeof(_tuplegetterobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)tuplegetter_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)tuplegetter_traverse, /* tp_traverse */
+ (inquiry)tuplegetter_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ tuplegetter_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ tuplegetterdescr_get, /* tp_descr_get */
+ tuplegetter_set, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ tuplegetter_new, /* tp_new */
+ 0,
+};
+
+
/* module level code ********************************************************/
PyDoc_STRVAR(module_doc,
@@ -2386,5 +2544,10 @@ PyInit__collections(void)
Py_INCREF(&dequereviter_type);
PyModule_AddObject(m, "_deque_reverse_iterator", (PyObject *)&dequereviter_type);
+ if (PyType_Ready(&tuplegetter_type) < 0)
+ return NULL;
+ Py_INCREF(&tuplegetter_type);
+ PyModule_AddObject(m, "_tuplegetter", (PyObject *)&tuplegetter_type);
+
return m;
}
diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h
new file mode 100644
index 0000000..12626c1
--- /dev/null
+++ b/Modules/clinic/_collectionsmodule.c.h
@@ -0,0 +1,28 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+static PyObject *
+tuplegetter_new_impl(PyTypeObject *type, Py_ssize_t index, PyObject *doc);
+
+static PyObject *
+tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ Py_ssize_t index;
+ PyObject *doc;
+
+ if ((type == &tuplegetter_type) &&
+ !_PyArg_NoKeywords("_tuplegetter", kwargs)) {
+ goto exit;
+ }
+ if (!PyArg_ParseTuple(args, "nO:_tuplegetter",
+ &index, &doc)) {
+ goto exit;
+ }
+ return_value = tuplegetter_new_impl(type, index, doc);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=83746071eacc28d3 input=a9049054013a1b77]*/