mirror of https://github.com/python/cpython
bpo-36030: Improve performance of some tuple operations (GH-12052)
This commit is contained in:
parent
b0c8369c60
commit
4fa10dde40
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue