bpo-36030: Improve performance of some tuple operations (GH-12052) · python/cpython@4fa10dd
@@ -59,6 +59,15 @@ show_track(void)
5959}
6060#endif
616162+static inline void
63+tuple_gc_track(PyTupleObject *op)
64+{
65+#ifdef SHOW_TRACK_COUNT
66+count_tracked++;
67+#endif
68+_PyObject_GC_TRACK(op);
69+}
70+6271/* Print summary info about the state of the optimized allocator */
6372void
6473_PyTuple_DebugMallocStats(FILE *out)
@@ -76,25 +85,25 @@ _PyTuple_DebugMallocStats(FILE *out)
7685#endif
7786}
788779-PyObject *
80-PyTuple_New(Py_ssize_t size)
88+/* Allocate an uninitialized tuple object. Before making it public following
89+ steps must be done:
90+ - initialize its items
91+ - call tuple_gc_track() on it
92+ Because the empty tuple is always reused and it's already tracked by GC,
93+ this function must not be called with size == 0 (unless from PyTuple_New()
94+ which wraps this function).
95+*/
96+static PyTupleObject *
97+tuple_alloc(Py_ssize_t size)
8198{
8299PyTupleObject *op;
83-Py_ssize_t i;
84100if (size < 0) {
85101PyErr_BadInternalCall();
86102return NULL;
87103 }
88104#if PyTuple_MAXSAVESIZE > 0
89-if (size == 0 && free_list[0]) {
90-op = free_list[0];
91-Py_INCREF(op);
92-#ifdef COUNT_ALLOCS
93-_Py_tuple_zero_allocs++;
94-#endif
95-return (PyObject *) op;
96- }
97105if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
106+assert(size != 0);
98107free_list[size] = (PyTupleObject *) op->ob_item[0];
99108numfree[size]--;
100109#ifdef COUNT_ALLOCS
@@ -113,25 +122,41 @@ PyTuple_New(Py_ssize_t size)
113122/* Check for overflow */
114123if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) -
115124sizeof(PyObject *)) / sizeof(PyObject *)) {
116-return PyErr_NoMemory();
125+return (PyTupleObject *)PyErr_NoMemory();
117126 }
118127op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
119128if (op == NULL)
120129return NULL;
121130 }
122-for (i=0; i < size; i++)
131+return op;
132+}
133+134+PyObject *
135+PyTuple_New(Py_ssize_t size)
136+{
137+PyTupleObject *op;
138+#if PyTuple_MAXSAVESIZE > 0
139+if (size == 0 && free_list[0]) {
140+op = free_list[0];
141+Py_INCREF(op);
142+#ifdef COUNT_ALLOCS
143+_Py_tuple_zero_allocs++;
144+#endif
145+return (PyObject *) op;
146+ }
147+#endif
148+op = tuple_alloc(size);
149+for (Py_ssize_t i = 0; i < size; i++) {
123150op->ob_item[i] = NULL;
151+ }
124152#if PyTuple_MAXSAVESIZE > 0
125153if (size == 0) {
126154free_list[0] = op;
127155++numfree[0];
128156Py_INCREF(op); /* extra INCREF so that this is never freed */
129157 }
130158#endif
131-#ifdef SHOW_TRACK_COUNT
132-count_tracked++;
133-#endif
134-_PyObject_GC_TRACK(op);
159+tuple_gc_track(op);
135160return (PyObject *) op;
136161}
137162@@ -211,24 +236,28 @@ PyTuple_Pack(Py_ssize_t n, ...)
211236{
212237Py_ssize_t i;
213238PyObject *o;
214-PyObject *result;
215239PyObject **items;
216240va_list vargs;
217241242+if (n == 0) {
243+return PyTuple_New(0);
244+ }
245+218246va_start(vargs, n);
219-result = PyTuple_New(n);
247+PyTupleObject *result = tuple_alloc(n);
220248if (result == NULL) {
221249va_end(vargs);
222250return NULL;
223251 }
224-items = ((PyTupleObject *)result)->ob_item;
252+items = result->ob_item;
225253for (i = 0; i < n; i++) {
226254o = va_arg(vargs, PyObject *);
227255Py_INCREF(o);
228256items[i] = o;
229257 }
230258va_end(vargs);
231-return result;
259+tuple_gc_track(result);
260+return (PyObject *)result;
232261}
233262234263@@ -421,7 +450,11 @@ tupleitem(PyTupleObject *a, Py_ssize_t i)
421450PyObject *
422451_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
423452{
424-PyTupleObject *tuple = (PyTupleObject *)PyTuple_New(n);
453+if (n == 0) {
454+return PyTuple_New(0);
455+ }
456+457+PyTupleObject *tuple = tuple_alloc(n);
425458if (tuple == NULL) {
426459return NULL;
427460 }
@@ -431,6 +464,7 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
431464Py_INCREF(item);
432465dst[i] = item;
433466 }
467+tuple_gc_track(tuple);
434468return (PyObject *)tuple;
435469}
436470@@ -486,7 +520,11 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
486520if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))
487521return PyErr_NoMemory();
488522size = Py_SIZE(a) + Py_SIZE(b);
489-np = (PyTupleObject *) PyTuple_New(size);
523+if (size == 0) {
524+return PyTuple_New(0);
525+ }
526+527+np = tuple_alloc(size);
490528if (np == NULL) {
491529return NULL;
492530 }
@@ -504,6 +542,7 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
504542Py_INCREF(v);
505543dest[i] = v;
506544 }
545+tuple_gc_track(np);
507546return (PyObject *)np;
508547#undef b
509548}
@@ -515,22 +554,21 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
515554Py_ssize_t size;
516555PyTupleObject *np;
517556PyObject **p, **items;
518-if (n < 0)
519-n = 0;
520557if (Py_SIZE(a) == 0 || n == 1) {
521558if (PyTuple_CheckExact(a)) {
522559/* Since tuples are immutable, we can return a shared
523560 copy in this case */
524561Py_INCREF(a);
525562return (PyObject *)a;
526563 }
527-if (Py_SIZE(a) == 0)
528-return PyTuple_New(0);
564+ }
565+if (Py_SIZE(a) == 0 || n <= 0) {
566+return PyTuple_New(0);
529567 }
530568if (n > PY_SSIZE_T_MAX / Py_SIZE(a))
531569return PyErr_NoMemory();
532570size = Py_SIZE(a) * n;
533-np = (PyTupleObject *) PyTuple_New(size);
571+np = tuple_alloc(size);
534572if (np == NULL)
535573return NULL;
536574p = np->ob_item;
@@ -542,6 +580,7 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
542580p++;
543581 }
544582 }
583+tuple_gc_track(np);
545584return (PyObject *) np;
546585}
547586@@ -754,7 +793,6 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
754793else if (PySlice_Check(item)) {
755794Py_ssize_t start, stop, step, slicelength, i;
756795size_t cur;
757-PyObject* result;
758796PyObject* it;
759797PyObject **src, **dest;
760798@@ -774,19 +812,20 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
774812return (PyObject *)self;
775813 }
776814else {
777-result = PyTuple_New(slicelength);
815+PyTupleObject* result = tuple_alloc(slicelength);
778816if (!result) return NULL;
779817780818src = self->ob_item;
781-dest = ((PyTupleObject *)result)->ob_item;
819+dest = result->ob_item;
782820for (cur = start, i = 0; i < slicelength;
783821cur += step, i++) {
784822it = src[cur];
785823Py_INCREF(it);
786824dest[i] = it;
787825 }
788826789-return result;
827+tuple_gc_track(result);
828+return (PyObject *)result;
790829 }
791830 }
792831else {