Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 67d687a

Browse files
committed
builtin_zip(): Take a good guess at how big the result list will be,
and allocate it in one gulp. This isn't a bugfix, it's just a minor optimization that may or may not pay off.
1 parent 541703b commit 67d687a

2 files changed

Lines changed: 70 additions & 15 deletions

File tree

Lib/test/test_iter.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,34 @@ def next(self):
467467
except OSError:
468468
pass
469469

470+
self.assertEqual(zip(xrange(5)), [(i,) for i in range(5)])
471+
472+
# Classes that lie about their lengths.
473+
class NoGuessLen5:
474+
def __getitem__(self, i):
475+
if i >= 5:
476+
raise IndexError
477+
return i
478+
479+
class Guess3Len5(NoGuessLen5):
480+
def __len__(self):
481+
return 3
482+
483+
class Guess30Len5(NoGuessLen5):
484+
def __len__(self):
485+
return 30
486+
487+
self.assertEqual(len(Guess3Len5()), 3)
488+
self.assertEqual(len(Guess30Len5()), 30)
489+
self.assertEqual(zip(NoGuessLen5()), zip(range(5)))
490+
self.assertEqual(zip(Guess3Len5()), zip(range(5)))
491+
self.assertEqual(zip(Guess30Len5()), zip(range(5)))
492+
493+
expected = [(i, i) for i in range(5)]
494+
for x in NoGuessLen5(), Guess3Len5(), Guess30Len5():
495+
for y in NoGuessLen5(), Guess3Len5(), Guess30Len5():
496+
self.assertEqual(zip(x, y), expected)
497+
470498
# Test reduces()'s use of iterators.
471499
def test_builtin_reduce(self):
472500
from operator import add

Python/bltinmodule.c

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ builtin_compile(PyObject *self, PyObject *args)
381381
int supplied_flags = 0;
382382
PyCompilerFlags cf;
383383

384-
if (!PyArg_ParseTuple(args, "sss|ii:compile", &str, &filename,
384+
if (!PyArg_ParseTuple(args, "sss|ii:compile", &str, &filename,
385385
&startstr, &supplied_flags, &dont_inherit))
386386
return NULL;
387387

@@ -1128,7 +1128,7 @@ min_max(PyObject *args, int op)
11281128
v = args;
11291129
else if (!PyArg_ParseTuple(args, "O:min/max", &v))
11301130
return NULL;
1131-
1131+
11321132
it = PyObject_GetIter(v);
11331133
if (it == NULL)
11341134
return NULL;
@@ -1704,9 +1704,10 @@ static PyObject*
17041704
builtin_zip(PyObject *self, PyObject *args)
17051705
{
17061706
PyObject *ret;
1707-
int itemsize = PySequence_Length(args);
1707+
const int itemsize = PySequence_Length(args);
17081708
int i;
17091709
PyObject *itlist; /* tuple of iterators */
1710+
int len; /* guess at result length */
17101711

17111712
if (itemsize < 1) {
17121713
PyErr_SetString(PyExc_TypeError,
@@ -1716,8 +1717,21 @@ builtin_zip(PyObject *self, PyObject *args)
17161717
/* args must be a tuple */
17171718
assert(PyTuple_Check(args));
17181719

1720+
/* Guess at result length: the shortest of the input lengths. */
1721+
len = -1; /* unknown */
1722+
for (i = 0; i < itemsize; ++i) {
1723+
PyObject *item = PyTuple_GET_ITEM(args, i);
1724+
int thislen = PySequence_Length(item);
1725+
if (thislen < 0)
1726+
PyErr_Clear();
1727+
else if (len < 0 || thislen < len)
1728+
len = thislen;
1729+
}
1730+
17191731
/* allocate result list */
1720-
if ((ret = PyList_New(0)) == NULL)
1732+
if (len < 0)
1733+
len = 10; /* arbitrary */
1734+
if ((ret = PyList_New(len)) == NULL)
17211735
return NULL;
17221736

17231737
/* obtain iterators */
@@ -1738,14 +1752,14 @@ builtin_zip(PyObject *self, PyObject *args)
17381752
}
17391753

17401754
/* build result into ret list */
1741-
for (;;) {
1742-
int status;
1755+
for (i = 0; ; ++i) {
1756+
int j;
17431757
PyObject *next = PyTuple_New(itemsize);
17441758
if (!next)
17451759
goto Fail_ret_itlist;
17461760

1747-
for (i = 0; i < itemsize; i++) {
1748-
PyObject *it = PyTuple_GET_ITEM(itlist, i);
1761+
for (j = 0; j < itemsize; j++) {
1762+
PyObject *it = PyTuple_GET_ITEM(itlist, j);
17491763
PyObject *item = PyIter_Next(it);
17501764
if (!item) {
17511765
if (PyErr_Occurred()) {
@@ -1754,16 +1768,29 @@ builtin_zip(PyObject *self, PyObject *args)
17541768
}
17551769
Py_DECREF(next);
17561770
Py_DECREF(itlist);
1757-
return ret;
1771+
goto Done;
17581772
}
1759-
PyTuple_SET_ITEM(next, i, item);
1773+
PyTuple_SET_ITEM(next, j, item);
17601774
}
17611775

1762-
status = PyList_Append(ret, next);
1763-
Py_DECREF(next);
1764-
if (status < 0)
1765-
goto Fail_ret_itlist;
1776+
if (i < len)
1777+
PyList_SET_ITEM(ret, i, next);
1778+
else {
1779+
int status = PyList_Append(ret, next);
1780+
Py_DECREF(next);
1781+
++len;
1782+
if (status < 0)
1783+
goto Fail_ret_itlist;
1784+
}
1785+
}
1786+
1787+
Done:
1788+
if (ret != NULL && i < len) {
1789+
/* The list is too big. */
1790+
if (PyList_SetSlice(ret, i, len, NULL) < 0)
1791+
return NULL;
17661792
}
1793+
return ret;
17671794

17681795
Fail_ret_itlist:
17691796
Py_DECREF(itlist);
@@ -1864,7 +1891,7 @@ _PyBuiltin_Init(void)
18641891
SETBUILTIN("complex", &PyComplex_Type);
18651892
#endif
18661893
SETBUILTIN("dict", &PyDict_Type);
1867-
SETBUILTIN("enumerate", &PyEnum_Type);
1894+
SETBUILTIN("enumerate", &PyEnum_Type);
18681895
SETBUILTIN("float", &PyFloat_Type);
18691896
SETBUILTIN("property", &PyProperty_Type);
18701897
SETBUILTIN("int", &PyInt_Type);

0 commit comments

Comments
 (0)