More sort cleanup: Moved the special cases from samplesortslice into

listsort.  If the former calls itself recursively, they're a waste of
time, since it's called on a random permutation of a random subset of
elements.  OTOH, for exactly the same reason, they're an immeasurably
small waste of time (the odds of finding exploitable order in a random
permutation are ~= 0, so the special-case loops looking for order give
up quickly).  The point is more for conceptual clarity.
Also changed some "assert comments" into real asserts; when this code
was first written, Python.h didn't supply assert.h.
This commit is contained in:
Tim Peters 2002-07-19 07:05:44 +00:00
parent 8235ea1c3a
commit 330f9e9581
1 changed files with 73 additions and 65 deletions

View File

@ -1005,43 +1005,10 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
int n, extra, top, extraOnRight;
struct SamplesortStackNode stack[STACKSIZE];
/* assert lo <= hi */
assert(lo <= hi);
n = hi - lo;
/* ----------------------------------------------------------
* Special cases
* --------------------------------------------------------*/
if (n < 2)
return 0;
/* Set r to the largest value such that [lo,r) is sorted.
This catches the already-sorted case, the all-the-same
case, and the appended-a-few-elements-to-a-sorted-list case.
If the array is unsorted, we're very likely to get out of
the loop fast, so the test is cheap if it doesn't pay off.
*/
/* assert lo < hi */
for (r = lo+1; r < hi; ++r) {
IFLT(*r, *(r-1))
break;
}
/* [lo,r) is sorted, [r,hi) unknown. Get out cheap if there are
few unknowns, or few elements in total. */
if (hi - r <= MAXMERGE || n < MINSIZE)
return binarysort(lo, hi, r, compare);
/* Check for the array already being reverse-sorted. Typical
benchmark-driven silliness <wink>. */
/* assert lo < hi */
for (r = lo+1; r < hi; ++r) {
IFLT(*(r-1), *r)
break;
}
if (hi - r <= MAXMERGE) {
/* Reverse the reversed prefix, then insert the tail */
reverse_slice(lo, r);
return binarysort(lo, hi, r, compare);
}
if (n < MINSIZE)
return binarysort(lo, hi, lo, compare);
/* ----------------------------------------------------------
* Normal case setup: a large array without obvious pattern.
@ -1093,7 +1060,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
* Partition [lo, hi), and repeat until out of work.
* --------------------------------------------------------*/
for (;;) {
/* assert lo <= hi, so n >= 0 */
assert(lo <= hi);
n = hi - lo;
/* We may not want, or may not be able, to partition:
@ -1103,8 +1070,8 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
*/
if (n < MINPARTITIONSIZE || extra == 0) {
if (n >= MINSIZE) {
/* assert extra == 0
This is rare, since the average size
assert(extra == 0);
/* This is rare, since the average size
of a final block is only about
ln(original n). */
if (samplesortslice(lo, hi, compare) < 0)
@ -1184,7 +1151,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
duplicates later. */
l = lo + 1;
r = hi - 1;
/* assert lo < l < r < hi (small n weeded out above) */
assert(lo < l && l < r && r < hi);
do {
/* slide l right, looking for key >= pivot */
@ -1208,9 +1175,8 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
} while (l < r);
/* assert lo < r <= l < hi
assert r == l or r+1 == l
everything to the left of l is < pivot, and
assert(lo < r && r <= l && l < hi);
/* everything to the left of l is < pivot, and
everything to the right of r is >= pivot */
if (l == r) {
@ -1219,13 +1185,12 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
else
--r;
}
/* assert lo <= r and r+1 == l and l <= hi
assert r == lo or a[r] < pivot
assert a[lo] is pivot
assert l == hi or a[l] >= pivot
Swap the pivot into "the middle", so we can henceforth
ignore it.
*/
assert(lo <= r && r+1 == l && l <= hi);
/* assert r == lo or a[r] < pivot */
assert(*lo == pivot);
/* assert l == hi or a[l] >= pivot */
/* Swap the pivot into "the middle", so we can henceforth
ignore it. */
*lo = *r;
*r = pivot;
@ -1250,13 +1215,12 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
++l;
}
/* assert lo <= r < l <= hi
Partitions are [lo, r) and [l, hi) */
/* push fattest first; remember we still have extra PPs
assert(lo <= r && r < l && l <= hi);
/* Partitions are [lo, r) and [l, hi)
:ush fattest first; remember we still have extra PPs
to the left of the left chunk and to the right of
the right chunk! */
/* assert top < STACKSIZE */
assert(top < STACKSIZE);
if (r - lo <= hi - l) {
/* second is bigger */
stack[top].lo = l;
@ -1283,33 +1247,77 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
return -1;
}
#undef IFLT
static PyTypeObject immutable_list_type;
static PyObject *
listsort(PyListObject *self, PyObject *args)
{
int err;
int k;
PyObject *compare = NULL;
PyObject **hi, **p;
PyTypeObject *savetype;
if (args != NULL) {
if (!PyArg_ParseTuple(args, "|O:sort", &compare))
return NULL;
}
savetype = self->ob_type;
if (self->ob_size < 2) {
k = 0;
goto done;
}
self->ob_type = &immutable_list_type;
err = samplesortslice(self->ob_item,
self->ob_item + self->ob_size,
compare);
self->ob_type = savetype;
if (err < 0)
hi = self->ob_item + self->ob_size;
/* Set p to the largest value such that [lo, p) is sorted.
This catches the already-sorted case, the all-the-same
case, and the appended-a-few-elements-to-a-sorted-list case.
If the array is unsorted, we're very likely to get out of
the loop fast, so the test is cheap if it doesn't pay off.
*/
for (p = self->ob_item + 1; p < hi; ++p) {
IFLT(*p, *(p-1))
break;
}
/* [lo, p) is sorted, [p, hi) unknown. Get out cheap if there are
few unknowns, or few elements in total. */
if (hi - p <= MAXMERGE || self->ob_size < MINSIZE) {
k = binarysort(self->ob_item, hi, p, compare);
goto done;
}
/* Check for the array already being reverse-sorted, or that with
a few elements tacked on to the end. */
for (p = self->ob_item + 1; p < hi; ++p) {
IFLT(*(p-1), *p)
break;
}
/* [lo, p) is reverse-sorted, [p, hi) unknown. */
if (hi - p <= MAXMERGE) {
/* Reverse the reversed prefix, then insert the tail */
reverse_slice(self->ob_item, p);
k = binarysort(self->ob_item, hi, p, compare);
goto done;
}
/* A large array without obvious pattern. */
k = samplesortslice(self->ob_item, hi, compare);
done: /* The IFLT macro requires a label named "fail". */;
fail:
self->ob_type = savetype;
if (k >= 0) {
Py_INCREF(Py_None);
return Py_None;
}
else
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
#undef IFLT
int
PyList_Sort(PyObject *v)
{