mirror of
https://github.com/python/cpython.git
synced 2025-11-03 11:23:31 +00:00
Refactor list_extend() and list_fill() for gains in code size, memory
utilization, and speed:
* Moved the responsibility for emptying the previous list from list_fill
to list_init.
* Replaced the code in list_extend with the superior code from list_fill.
* Eliminated list_fill.
Results:
* list.extend() no longer creates an intermediate tuple except to handle
the special case of x.extend(x). The saves memory and time.
* list.extend(x) runs
5 to 10% faster when x is a list or tuple
15% faster when x is an iterable not defining __len__
twice as fast when x is an iterable defining __len__
* the code is about 15 lines shorter and no longer duplicates
functionality.
This commit is contained in:
parent
ab517d2eac
commit
90a39bf12c
1 changed files with 71 additions and 84 deletions
|
|
@ -324,7 +324,6 @@ list_contains(PyListObject *a, PyObject *el)
|
||||||
return cmp;
|
return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
list_item(PyListObject *a, int i)
|
list_item(PyListObject *a, int i)
|
||||||
{
|
{
|
||||||
|
|
@ -686,7 +685,6 @@ listextend_internal(PyListObject *self, PyObject *b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
list_inplace_concat(PyListObject *self, PyObject *other)
|
list_inplace_concat(PyListObject *self, PyObject *other)
|
||||||
{
|
{
|
||||||
|
|
@ -704,16 +702,70 @@ list_inplace_concat(PyListObject *self, PyObject *other)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
listextend(PyListObject *self, PyObject *b)
|
listextend(PyListObject *self, PyObject *b)
|
||||||
{
|
{
|
||||||
|
PyObject *it; /* iter(v) */
|
||||||
|
int m; /* size of self */
|
||||||
|
int n; /* guess for size of b */
|
||||||
|
int mn; /* m + n */
|
||||||
|
int i;
|
||||||
|
|
||||||
b = PySequence_Fast(b, "list.extend() argument must be iterable");
|
/* Special cases:
|
||||||
if (!b)
|
1) lists and tuples which can use PySequence_Fast ops
|
||||||
|
2) extending self to self requires making a copy first
|
||||||
|
*/
|
||||||
|
if (PyList_CheckExact(b) || PyTuple_CheckExact(b) || (PyObject *)self == b) {
|
||||||
|
b = PySequence_Fast(b, "argument must be iterable");
|
||||||
|
if (!b)
|
||||||
|
return NULL;
|
||||||
|
if (listextend_internal(self, b) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = PyObject_GetIter(b);
|
||||||
|
if (it == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (listextend_internal(self, b) < 0)
|
/* Guess a result list size. */
|
||||||
return NULL;
|
n = PyObject_Size(b);
|
||||||
|
if (n < 0) {
|
||||||
|
PyErr_Clear();
|
||||||
|
n = 8; /* arbitrary */
|
||||||
|
}
|
||||||
|
m = self->ob_size;
|
||||||
|
mn = m + n;
|
||||||
|
if (list_resize(self, mn) == -1)
|
||||||
|
goto error;
|
||||||
|
memset(&(self->ob_item[m]), 0, sizeof(*self->ob_item) * n);
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
/* Run iterator to exhaustion. */
|
||||||
return Py_None;
|
for (i = m; ; i++) {
|
||||||
|
PyObject *item = PyIter_Next(it);
|
||||||
|
if (item == NULL) {
|
||||||
|
if (PyErr_Occurred())
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < mn)
|
||||||
|
PyList_SET_ITEM(self, i, item); /* steals ref */
|
||||||
|
else {
|
||||||
|
int status = ins1(self, self->ob_size, item);
|
||||||
|
Py_DECREF(item); /* append creates a new ref */
|
||||||
|
if (status < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cut back result list if initial guess was too large. */
|
||||||
|
if (i < mn && self != NULL) {
|
||||||
|
if (list_ass_slice(self, i, mn, (PyObject *)NULL) != 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Py_DECREF(it);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_DECREF(it);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
@ -2220,78 +2272,6 @@ list_richcompare(PyObject *v, PyObject *w, int op)
|
||||||
return PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op);
|
return PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adapted from newer code by Tim */
|
|
||||||
static int
|
|
||||||
list_fill(PyListObject *result, PyObject *v)
|
|
||||||
{
|
|
||||||
PyObject *it; /* iter(v) */
|
|
||||||
int n; /* guess for result list size */
|
|
||||||
int i;
|
|
||||||
|
|
||||||
n = result->ob_size;
|
|
||||||
|
|
||||||
/* Special-case list(a_list), for speed. */
|
|
||||||
if (PyList_Check(v)) {
|
|
||||||
if (v == (PyObject *)result)
|
|
||||||
return 0; /* source is destination, we're done */
|
|
||||||
return list_ass_slice(result, 0, n, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Empty previous contents */
|
|
||||||
if (n != 0) {
|
|
||||||
if (list_ass_slice(result, 0, n, (PyObject *)NULL) != 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get iterator. There may be some low-level efficiency to be gained
|
|
||||||
* by caching the tp_iternext slot instead of using PyIter_Next()
|
|
||||||
* later, but premature optimization is the root etc.
|
|
||||||
*/
|
|
||||||
it = PyObject_GetIter(v);
|
|
||||||
if (it == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Guess a result list size. */
|
|
||||||
n = PyObject_Size(v);
|
|
||||||
if (n < 0) {
|
|
||||||
PyErr_Clear();
|
|
||||||
n = 8; /* arbitrary */
|
|
||||||
}
|
|
||||||
if (list_resize(result, n) == -1)
|
|
||||||
goto error;
|
|
||||||
memset(result->ob_item, 0, sizeof(*result->ob_item) * n);
|
|
||||||
|
|
||||||
/* Run iterator to exhaustion. */
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
PyObject *item = PyIter_Next(it);
|
|
||||||
if (item == NULL) {
|
|
||||||
if (PyErr_Occurred())
|
|
||||||
goto error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i < n)
|
|
||||||
PyList_SET_ITEM(result, i, item); /* steals ref */
|
|
||||||
else {
|
|
||||||
int status = ins1(result, result->ob_size, item);
|
|
||||||
Py_DECREF(item); /* append creates a new ref */
|
|
||||||
if (status < 0)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cut back result list if initial guess was too large. */
|
|
||||||
if (i < n && result != NULL) {
|
|
||||||
if (list_ass_slice(result, i, n, (PyObject *)NULL) != 0)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
Py_DECREF(it);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
Py_DECREF(it);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
list_init(PyListObject *self, PyObject *args, PyObject *kw)
|
list_init(PyListObject *self, PyObject *args, PyObject *kw)
|
||||||
{
|
{
|
||||||
|
|
@ -2300,10 +2280,17 @@ list_init(PyListObject *self, PyObject *args, PyObject *kw)
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:list", kwlist, &arg))
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:list", kwlist, &arg))
|
||||||
return -1;
|
return -1;
|
||||||
if (arg != NULL)
|
/* Empty previous contents */
|
||||||
return list_fill(self, arg);
|
if (self->ob_size != 0) {
|
||||||
if (self->ob_size > 0)
|
if (list_ass_slice(self, 0, self->ob_size, (PyObject *)NULL) != 0)
|
||||||
return list_ass_slice(self, 0, self->ob_size, (PyObject*)NULL);
|
return -1;
|
||||||
|
}
|
||||||
|
if (arg != NULL) {
|
||||||
|
PyObject *rv = listextend(self, arg);
|
||||||
|
if (rv == NULL)
|
||||||
|
return -1;
|
||||||
|
Py_DECREF(rv);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue