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
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 */
void
_PyTuple_DebugMallocStats(FILE *out)
@ -76,25 +85,25 @@ _PyTuple_DebugMallocStats(FILE *out)
#endif
}
PyObject *
PyTuple_New(Py_ssize_t size)
/* Allocate an uninitialized tuple object. Before making it public following
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;
Py_ssize_t i;
if (size < 0) {
PyErr_BadInternalCall();
return NULL;
}
#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) {
assert(size != 0);
free_list[size] = (PyTupleObject *) op->ob_item[0];
numfree[size]--;
#ifdef COUNT_ALLOCS
@ -113,14 +122,33 @@ PyTuple_New(Py_ssize_t size)
/* Check for overflow */
if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) -
sizeof(PyObject *)) / sizeof(PyObject *)) {
return PyErr_NoMemory();
return (PyTupleObject *)PyErr_NoMemory();
}
op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
if (op == 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;
}
#if PyTuple_MAXSAVESIZE > 0
if (size == 0) {
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 */
}
#endif
#ifdef SHOW_TRACK_COUNT
count_tracked++;
#endif
_PyObject_GC_TRACK(op);
tuple_gc_track(op);
return (PyObject *) op;
}
@ -211,24 +236,28 @@ PyTuple_Pack(Py_ssize_t n, ...)
{
Py_ssize_t i;
PyObject *o;
PyObject *result;
PyObject **items;
va_list vargs;
if (n == 0) {
return PyTuple_New(0);
}
va_start(vargs, n);
result = PyTuple_New(n);
PyTupleObject *result = tuple_alloc(n);
if (result == NULL) {
va_end(vargs);
return NULL;
}
items = ((PyTupleObject *)result)->ob_item;
items = result->ob_item;
for (i = 0; i < n; i++) {
o = va_arg(vargs, PyObject *);
Py_INCREF(o);
items[i] = o;
}
va_end(vargs);
return result;
tuple_gc_track(result);
return (PyObject *)result;
}
@ -421,7 +450,11 @@ tupleitem(PyTupleObject *a, Py_ssize_t i)
PyObject *
_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) {
return NULL;
}
@ -431,6 +464,7 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
Py_INCREF(item);
dst[i] = item;
}
tuple_gc_track(tuple);
return (PyObject *)tuple;
}
@ -486,7 +520,11 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))
return PyErr_NoMemory();
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) {
return NULL;
}
@ -504,6 +542,7 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
Py_INCREF(v);
dest[i] = v;
}
tuple_gc_track(np);
return (PyObject *)np;
#undef b
}
@ -515,8 +554,6 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
Py_ssize_t size;
PyTupleObject *np;
PyObject **p, **items;
if (n < 0)
n = 0;
if (Py_SIZE(a) == 0 || n == 1) {
if (PyTuple_CheckExact(a)) {
/* Since tuples are immutable, we can return a shared
@ -524,13 +561,14 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
Py_INCREF(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))
return PyErr_NoMemory();
size = Py_SIZE(a) * n;
np = (PyTupleObject *) PyTuple_New(size);
np = tuple_alloc(size);
if (np == NULL)
return NULL;
p = np->ob_item;
@ -542,6 +580,7 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
p++;
}
}
tuple_gc_track(np);
return (PyObject *) np;
}
@ -754,7 +793,6 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step, slicelength, i;
size_t cur;
PyObject* result;
PyObject* it;
PyObject **src, **dest;
@ -774,11 +812,11 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
return (PyObject *)self;
}
else {
result = PyTuple_New(slicelength);
PyTupleObject* result = tuple_alloc(slicelength);
if (!result) return NULL;
src = self->ob_item;
dest = ((PyTupleObject *)result)->ob_item;
dest = result->ob_item;
for (cur = start, i = 0; i < slicelength;
cur += step, i++) {
it = src[cur];
@ -786,7 +824,8 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
dest[i] = it;
}
return result;
tuple_gc_track(result);
return (PyObject *)result;
}
}
else {