mirror of https://github.com/python/cpython
bpo-46235: Do all ref-counting at once during list/tuple multiplication (GH-30346)
When multiplying lists and tuples by `n`, increment each element's refcount, by `n`, just once. Saves `n-1` increments per element, and allows for a leaner & faster copying loop. Code by sweeneyde (Dennis Sweeney).
This commit is contained in:
parent
6fa8b2ceee
commit
ad1d5908ad
|
@ -0,0 +1 @@
|
||||||
|
Certain sequence multiplication operations like ``[0] * 1_000`` are now faster due to reference-counting optimizations. Patch by Dennis Sweeney.
|
|
@ -553,11 +553,8 @@ list_concat(PyListObject *a, PyObject *bb)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
list_repeat(PyListObject *a, Py_ssize_t n)
|
list_repeat(PyListObject *a, Py_ssize_t n)
|
||||||
{
|
{
|
||||||
Py_ssize_t i, j;
|
|
||||||
Py_ssize_t size;
|
Py_ssize_t size;
|
||||||
PyListObject *np;
|
PyListObject *np;
|
||||||
PyObject **p, **items;
|
|
||||||
PyObject *elem;
|
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
n = 0;
|
n = 0;
|
||||||
if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n)
|
if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n)
|
||||||
|
@ -568,24 +565,32 @@ list_repeat(PyListObject *a, Py_ssize_t n)
|
||||||
np = (PyListObject *) list_new_prealloc(size);
|
np = (PyListObject *) list_new_prealloc(size);
|
||||||
if (np == NULL)
|
if (np == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
PyObject **dest = np->ob_item;
|
||||||
|
PyObject **dest_end = dest + size;
|
||||||
if (Py_SIZE(a) == 1) {
|
if (Py_SIZE(a) == 1) {
|
||||||
items = np->ob_item;
|
PyObject *elem = a->ob_item[0];
|
||||||
elem = a->ob_item[0];
|
Py_SET_REFCNT(elem, Py_REFCNT(elem) + n);
|
||||||
for (i = 0; i < n; i++) {
|
#ifdef Py_REF_DEBUG
|
||||||
items[i] = elem;
|
_Py_RefTotal += n;
|
||||||
Py_INCREF(elem);
|
#endif
|
||||||
|
while (dest < dest_end) {
|
||||||
|
*dest++ = elem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p = np->ob_item;
|
PyObject **src = a->ob_item;
|
||||||
items = a->ob_item;
|
PyObject **src_end = src + Py_SIZE(a);
|
||||||
for (i = 0; i < n; i++) {
|
while (src < src_end) {
|
||||||
for (j = 0; j < Py_SIZE(a); j++) {
|
Py_SET_REFCNT(*src, Py_REFCNT(*src) + n);
|
||||||
*p = items[j];
|
#ifdef Py_REF_DEBUG
|
||||||
Py_INCREF(*p);
|
_Py_RefTotal += n;
|
||||||
p++;
|
#endif
|
||||||
}
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
// Now src chases after dest in the same buffer
|
||||||
|
src = np->ob_item;
|
||||||
|
while (dest < dest_end) {
|
||||||
|
*dest++ = *src++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Py_SET_SIZE(np, size);
|
Py_SET_SIZE(np, size);
|
||||||
|
|
|
@ -589,10 +589,8 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
tuplerepeat(PyTupleObject *a, Py_ssize_t n)
|
tuplerepeat(PyTupleObject *a, Py_ssize_t n)
|
||||||
{
|
{
|
||||||
Py_ssize_t i, j;
|
|
||||||
Py_ssize_t size;
|
Py_ssize_t size;
|
||||||
PyTupleObject *np;
|
PyTupleObject *np;
|
||||||
PyObject **p, **items;
|
|
||||||
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
|
||||||
|
@ -610,13 +608,32 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
|
||||||
np = tuple_alloc(size);
|
np = tuple_alloc(size);
|
||||||
if (np == NULL)
|
if (np == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
p = np->ob_item;
|
PyObject **dest = np->ob_item;
|
||||||
items = a->ob_item;
|
PyObject **dest_end = dest + size;
|
||||||
for (i = 0; i < n; i++) {
|
if (Py_SIZE(a) == 1) {
|
||||||
for (j = 0; j < Py_SIZE(a); j++) {
|
PyObject *elem = a->ob_item[0];
|
||||||
*p = items[j];
|
Py_SET_REFCNT(elem, Py_REFCNT(elem) + n);
|
||||||
Py_INCREF(*p);
|
#ifdef Py_REF_DEBUG
|
||||||
p++;
|
_Py_RefTotal += n;
|
||||||
|
#endif
|
||||||
|
while (dest < dest_end) {
|
||||||
|
*dest++ = elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyObject **src = a->ob_item;
|
||||||
|
PyObject **src_end = src + Py_SIZE(a);
|
||||||
|
while (src < src_end) {
|
||||||
|
Py_SET_REFCNT(*src, Py_REFCNT(*src) + n);
|
||||||
|
#ifdef Py_REF_DEBUG
|
||||||
|
_Py_RefTotal += n;
|
||||||
|
#endif
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
// Now src chases after dest in the same buffer
|
||||||
|
src = np->ob_item;
|
||||||
|
while (dest < dest_end) {
|
||||||
|
*dest++ = *src++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_PyObject_GC_TRACK(np);
|
_PyObject_GC_TRACK(np);
|
||||||
|
|
Loading…
Reference in New Issue