bpo-36030: Improve performance of some tuple operations (GH-12052)

This commit is contained in:
Sergey Fedoseev 2019-08-14 19:10:33 +05:00 committed by Victor Stinner
parent b0c8369c60
commit 4fa10dde40
1 changed files with 71 additions and 32 deletions

View File

@ -59,6 +59,15 @@ show_track(void)
} }
#endif #endif
static inline void
tuple_gc_track(PyTupleObject *op)
{
#ifdef SHOW_TRACK_COUNT
count_tracked++;
#endif
_PyObject_GC_TRACK(op);
}
/* Print summary info about the state of the optimized allocator */ /* Print summary info about the state of the optimized allocator */
void void
_PyTuple_DebugMallocStats(FILE *out) _PyTuple_DebugMallocStats(FILE *out)
@ -76,25 +85,25 @@ _PyTuple_DebugMallocStats(FILE *out)
#endif #endif
} }
PyObject * /* Allocate an uninitialized tuple object. Before making it public following
PyTuple_New(Py_ssize_t size) steps must be done:
- initialize its items
- call tuple_gc_track() on it
Because the empty tuple is always reused and it's already tracked by GC,
this function must not be called with size == 0 (unless from PyTuple_New()
which wraps this function).
*/
static PyTupleObject *
tuple_alloc(Py_ssize_t size)
{ {
PyTupleObject *op; PyTupleObject *op;
Py_ssize_t i;
if (size < 0) { if (size < 0) {
PyErr_BadInternalCall(); PyErr_BadInternalCall();
return NULL; return NULL;
} }
#if PyTuple_MAXSAVESIZE > 0 #if PyTuple_MAXSAVESIZE > 0
if (size == 0 && free_list[0]) {
op = free_list[0];
Py_INCREF(op);
#ifdef COUNT_ALLOCS
_Py_tuple_zero_allocs++;
#endif
return (PyObject *) op;
}
if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) { if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
assert(size != 0);
free_list[size] = (PyTupleObject *) op->ob_item[0]; free_list[size] = (PyTupleObject *) op->ob_item[0];
numfree[size]--; numfree[size]--;
#ifdef COUNT_ALLOCS #ifdef COUNT_ALLOCS
@ -113,14 +122,33 @@ PyTuple_New(Py_ssize_t size)
/* Check for overflow */ /* Check for overflow */
if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) - if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) -
sizeof(PyObject *)) / sizeof(PyObject *)) { sizeof(PyObject *)) / sizeof(PyObject *)) {
return PyErr_NoMemory(); return (PyTupleObject *)PyErr_NoMemory();
} }
op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size); op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
if (op == NULL) if (op == NULL)
return NULL; return NULL;
} }
for (i=0; i < size; i++) return op;
}
PyObject *
PyTuple_New(Py_ssize_t size)
{
PyTupleObject *op;
#if PyTuple_MAXSAVESIZE > 0
if (size == 0 && free_list[0]) {
op = free_list[0];
Py_INCREF(op);
#ifdef COUNT_ALLOCS
_Py_tuple_zero_allocs++;
#endif
return (PyObject *) op;
}
#endif
op = tuple_alloc(size);
for (Py_ssize_t i = 0; i < size; i++) {
op->ob_item[i] = NULL; op->ob_item[i] = NULL;
}
#if PyTuple_MAXSAVESIZE > 0 #if PyTuple_MAXSAVESIZE > 0
if (size == 0) { if (size == 0) {
free_list[0] = op; free_list[0] = op;
@ -128,10 +156,7 @@ PyTuple_New(Py_ssize_t size)
Py_INCREF(op); /* extra INCREF so that this is never freed */ Py_INCREF(op); /* extra INCREF so that this is never freed */
} }
#endif #endif
#ifdef SHOW_TRACK_COUNT tuple_gc_track(op);
count_tracked++;
#endif
_PyObject_GC_TRACK(op);
return (PyObject *) op; return (PyObject *) op;
} }
@ -211,24 +236,28 @@ PyTuple_Pack(Py_ssize_t n, ...)
{ {
Py_ssize_t i; Py_ssize_t i;
PyObject *o; PyObject *o;
PyObject *result;
PyObject **items; PyObject **items;
va_list vargs; va_list vargs;
if (n == 0) {
return PyTuple_New(0);
}
va_start(vargs, n); va_start(vargs, n);
result = PyTuple_New(n); PyTupleObject *result = tuple_alloc(n);
if (result == NULL) { if (result == NULL) {
va_end(vargs); va_end(vargs);
return NULL; return NULL;
} }
items = ((PyTupleObject *)result)->ob_item; items = result->ob_item;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
o = va_arg(vargs, PyObject *); o = va_arg(vargs, PyObject *);
Py_INCREF(o); Py_INCREF(o);
items[i] = o; items[i] = o;
} }
va_end(vargs); va_end(vargs);
return result; tuple_gc_track(result);
return (PyObject *)result;
} }
@ -421,7 +450,11 @@ tupleitem(PyTupleObject *a, Py_ssize_t i)
PyObject * PyObject *
_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
{ {
PyTupleObject *tuple = (PyTupleObject *)PyTuple_New(n); if (n == 0) {
return PyTuple_New(0);
}
PyTupleObject *tuple = tuple_alloc(n);
if (tuple == NULL) { if (tuple == NULL) {
return NULL; return NULL;
} }
@ -431,6 +464,7 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
Py_INCREF(item); Py_INCREF(item);
dst[i] = item; dst[i] = item;
} }
tuple_gc_track(tuple);
return (PyObject *)tuple; return (PyObject *)tuple;
} }
@ -486,7 +520,11 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b)) if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))
return PyErr_NoMemory(); return PyErr_NoMemory();
size = Py_SIZE(a) + Py_SIZE(b); size = Py_SIZE(a) + Py_SIZE(b);
np = (PyTupleObject *) PyTuple_New(size); if (size == 0) {
return PyTuple_New(0);
}
np = tuple_alloc(size);
if (np == NULL) { if (np == NULL) {
return NULL; return NULL;
} }
@ -504,6 +542,7 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
Py_INCREF(v); Py_INCREF(v);
dest[i] = v; dest[i] = v;
} }
tuple_gc_track(np);
return (PyObject *)np; return (PyObject *)np;
#undef b #undef b
} }
@ -515,8 +554,6 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
Py_ssize_t size; Py_ssize_t size;
PyTupleObject *np; PyTupleObject *np;
PyObject **p, **items; PyObject **p, **items;
if (n < 0)
n = 0;
if (Py_SIZE(a) == 0 || n == 1) { if (Py_SIZE(a) == 0 || n == 1) {
if (PyTuple_CheckExact(a)) { if (PyTuple_CheckExact(a)) {
/* Since tuples are immutable, we can return a shared /* Since tuples are immutable, we can return a shared
@ -524,13 +561,14 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
Py_INCREF(a); Py_INCREF(a);
return (PyObject *)a; return (PyObject *)a;
} }
if (Py_SIZE(a) == 0) }
return PyTuple_New(0); if (Py_SIZE(a) == 0 || n <= 0) {
return PyTuple_New(0);
} }
if (n > PY_SSIZE_T_MAX / Py_SIZE(a)) if (n > PY_SSIZE_T_MAX / Py_SIZE(a))
return PyErr_NoMemory(); return PyErr_NoMemory();
size = Py_SIZE(a) * n; size = Py_SIZE(a) * n;
np = (PyTupleObject *) PyTuple_New(size); np = tuple_alloc(size);
if (np == NULL) if (np == NULL)
return NULL; return NULL;
p = np->ob_item; p = np->ob_item;
@ -542,6 +580,7 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
p++; p++;
} }
} }
tuple_gc_track(np);
return (PyObject *) np; return (PyObject *) np;
} }
@ -754,7 +793,6 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
else if (PySlice_Check(item)) { else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step, slicelength, i; Py_ssize_t start, stop, step, slicelength, i;
size_t cur; size_t cur;
PyObject* result;
PyObject* it; PyObject* it;
PyObject **src, **dest; PyObject **src, **dest;
@ -774,11 +812,11 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
return (PyObject *)self; return (PyObject *)self;
} }
else { else {
result = PyTuple_New(slicelength); PyTupleObject* result = tuple_alloc(slicelength);
if (!result) return NULL; if (!result) return NULL;
src = self->ob_item; src = self->ob_item;
dest = ((PyTupleObject *)result)->ob_item; dest = result->ob_item;
for (cur = start, i = 0; i < slicelength; for (cur = start, i = 0; i < slicelength;
cur += step, i++) { cur += step, i++) {
it = src[cur]; it = src[cur];
@ -786,7 +824,8 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
dest[i] = it; dest[i] = it;
} }
return result; tuple_gc_track(result);
return (PyObject *)result;
} }
} }
else { else {