Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4fa10dd

Browse files
sir-sigurdvstinner
authored andcommitted
bpo-36030: Improve performance of some tuple operations (GH-12052)
1 parent b0c8369 commit 4fa10dd

File tree

1 file changed

+71
-32
lines changed

1 file changed

+71
-32
lines changed

Objects/tupleobject.c

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ show_track(void)
5959
}
6060
#endif
6161

62+
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 */
6372
void
6473
_PyTuple_DebugMallocStats(FILE *out)
@@ -76,25 +85,25 @@ _PyTuple_DebugMallocStats(FILE *out)
7685
#endif
7786
}
7887

79-
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
{
8299
PyTupleObject *op;
83-
Py_ssize_t i;
84100
if (size < 0) {
85101
PyErr_BadInternalCall();
86102
return 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-
}
97105
if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
106+
assert(size != 0);
98107
free_list[size] = (PyTupleObject *) op->ob_item[0];
99108
numfree[size]--;
100109
#ifdef COUNT_ALLOCS
@@ -113,25 +122,41 @@ PyTuple_New(Py_ssize_t size)
113122
/* Check for overflow */
114123
if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) -
115124
sizeof(PyObject *)) / sizeof(PyObject *)) {
116-
return PyErr_NoMemory();
125+
return (PyTupleObject *)PyErr_NoMemory();
117126
}
118127
op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
119128
if (op == NULL)
120129
return 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++) {
123150
op->ob_item[i] = NULL;
151+
}
124152
#if PyTuple_MAXSAVESIZE > 0
125153
if (size == 0) {
126154
free_list[0] = op;
127155
++numfree[0];
128156
Py_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);
135160
return (PyObject *) op;
136161
}
137162

@@ -211,24 +236,28 @@ PyTuple_Pack(Py_ssize_t n, ...)
211236
{
212237
Py_ssize_t i;
213238
PyObject *o;
214-
PyObject *result;
215239
PyObject **items;
216240
va_list vargs;
217241

242+
if (n == 0) {
243+
return PyTuple_New(0);
244+
}
245+
218246
va_start(vargs, n);
219-
result = PyTuple_New(n);
247+
PyTupleObject *result = tuple_alloc(n);
220248
if (result == NULL) {
221249
va_end(vargs);
222250
return NULL;
223251
}
224-
items = ((PyTupleObject *)result)->ob_item;
252+
items = result->ob_item;
225253
for (i = 0; i < n; i++) {
226254
o = va_arg(vargs, PyObject *);
227255
Py_INCREF(o);
228256
items[i] = o;
229257
}
230258
va_end(vargs);
231-
return result;
259+
tuple_gc_track(result);
260+
return (PyObject *)result;
232261
}
233262

234263

@@ -421,7 +450,11 @@ tupleitem(PyTupleObject *a, Py_ssize_t i)
421450
PyObject *
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);
425458
if (tuple == NULL) {
426459
return NULL;
427460
}
@@ -431,6 +464,7 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
431464
Py_INCREF(item);
432465
dst[i] = item;
433466
}
467+
tuple_gc_track(tuple);
434468
return (PyObject *)tuple;
435469
}
436470

@@ -486,7 +520,11 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
486520
if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))
487521
return PyErr_NoMemory();
488522
size = 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);
490528
if (np == NULL) {
491529
return NULL;
492530
}
@@ -504,6 +542,7 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
504542
Py_INCREF(v);
505543
dest[i] = v;
506544
}
545+
tuple_gc_track(np);
507546
return (PyObject *)np;
508547
#undef b
509548
}
@@ -515,22 +554,21 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
515554
Py_ssize_t size;
516555
PyTupleObject *np;
517556
PyObject **p, **items;
518-
if (n < 0)
519-
n = 0;
520557
if (Py_SIZE(a) == 0 || n == 1) {
521558
if (PyTuple_CheckExact(a)) {
522559
/* Since tuples are immutable, we can return a shared
523560
copy in this case */
524561
Py_INCREF(a);
525562
return (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
}
530568
if (n > PY_SSIZE_T_MAX / Py_SIZE(a))
531569
return PyErr_NoMemory();
532570
size = Py_SIZE(a) * n;
533-
np = (PyTupleObject *) PyTuple_New(size);
571+
np = tuple_alloc(size);
534572
if (np == NULL)
535573
return NULL;
536574
p = np->ob_item;
@@ -542,6 +580,7 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
542580
p++;
543581
}
544582
}
583+
tuple_gc_track(np);
545584
return (PyObject *) np;
546585
}
547586

@@ -754,7 +793,6 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
754793
else if (PySlice_Check(item)) {
755794
Py_ssize_t start, stop, step, slicelength, i;
756795
size_t cur;
757-
PyObject* result;
758796
PyObject* it;
759797
PyObject **src, **dest;
760798

@@ -774,19 +812,20 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
774812
return (PyObject *)self;
775813
}
776814
else {
777-
result = PyTuple_New(slicelength);
815+
PyTupleObject* result = tuple_alloc(slicelength);
778816
if (!result) return NULL;
779817

780818
src = self->ob_item;
781-
dest = ((PyTupleObject *)result)->ob_item;
819+
dest = result->ob_item;
782820
for (cur = start, i = 0; i < slicelength;
783821
cur += step, i++) {
784822
it = src[cur];
785823
Py_INCREF(it);
786824
dest[i] = it;
787825
}
788826

789-
return result;
827+
tuple_gc_track(result);
828+
return (PyObject *)result;
790829
}
791830
}
792831
else {

0 commit comments

Comments
 (0)