Added new quicksort implementation, tailored to sorting arrays of

object pointers.  Should be a bit faster than the C library's qsort(),
and doesn't have the prohibition on recursion that Solaris qsort() has
in the threaded version of their C library.

Thanks to discussions with Tim Peters.
This commit is contained in:
Guido van Rossum 1996-12-10 23:55:39 +00:00
parent 53699e9ec1
commit 3f236dee3a
1 changed files with 227 additions and 0 deletions

View File

@ -550,6 +550,231 @@ listappend(self, args)
return ins(self, (int) self->ob_size, v);
}
#define NEWSORT
#ifdef NEWSORT
/* New quicksort implementation for arrays of object pointers.
Thanks to discussions with Tim Peters. */
/* CMPERROR is returned by our comparison function when an error
occurred. This is the largest negative integer (0x80000000 on a
32-bit system). */
#define CMPERROR (1 << (8*sizeof(int) - 1))
/* Comparison function. Takes care of calling a user-supplied
comparison function (any callable Python object). Calls the
standard comparison function, cmpobject(), if the user-supplied
function is NULL. */
static int
docompare(x, y, compare)
object *x;
object *y;
object *compare;
{
object *args, *res;
int i;
if (compare == NULL)
return cmpobject(x, y);
args = mkvalue("(OO)", x, y);
if (args == NULL)
return CMPERROR;
res = call_object(compare, args);
DECREF(args);
if (res == NULL)
return CMPERROR;
if (!is_intobject(res)) {
DECREF(res);
err_setstr(TypeError, "comparison function should return int");
return CMPERROR;
}
i = getintvalue(res);
DECREF(res);
if (i < 0)
return -1;
if (i > 0)
return 1;
return 0;
}
/* Straight insertion sort. More efficient for sorting small arrays. */
static int
insertionsort(array, size, compare)
object **array; /* Start of array to sort */
int size; /* Number of elements to sort */
object *compare;/* Comparison function object, or NULL for default */
{
register object **a = array;
register int i;
for (i = 1; i < size; i++) {
register object *key = a[i];
register int j = i;
while (--j >= 0) {
register int k = docompare(a[j], key, compare);
if (k == CMPERROR)
return -1;
if (k <= 0)
break;
a[j+1] = a[j];
a[j] = key; /* For consistency */
}
}
return 0;
}
/* MINSIZE is the smallest array we care to partition; smaller arrays
are sorted using a straight insertion sort (above). */
#define MINSIZE 10
/* STACKSIZE is the size of our work stack. A rough estimate is that
this allows us to sort arrays of MINSIZE * 2**STACKSIZE, or large
enough. (Because of the way we push the biggest partition first,
the worst case occurs when all subarrays are always partitioned
exactly in two.) */
#define STACKSIZE 64
/* Quicksort algorithm. Return -1 if an exception occurred; in this
case we leave the array partly sorted but otherwise in good health
(i.e. no items have been removed or duplicated). */
static int
quicksort(array, size, compare)
object **array; /* Start of array to sort */
int size; /* Number of elements to sort */
object *compare;/* Comparison function object, or NULL for default */
{
register object **a, *tmp, *pivot;
register int n, l, r, k;
int top, i, j, n2;
object **astack[STACKSIZE];
int nstack[STACKSIZE];
/* Start out with the whole array on the work stack */
astack[0] = array;
nstack[0] = size;
top = 1;
/* Repeat until the work stack is empty */
while (--top >= 0) {
a = astack[top];
n = nstack[top];
/* If it's a small one, use straight insertion sort */
if (n < MINSIZE) {
if (insertionsort(a, n, compare) < 0)
return -1;
continue;
}
/* Choose median of a[0], a[n/2], a[n-1] as pivot */
i = n>>1;
j = n-1;
k = docompare(a[0], a[i], compare);
if (k == CMPERROR)
return -1;
if (k < 0)
{ tmp = a[0]; a[0] = a[i]; a[i] = tmp; }
k = docompare(a[j], a[i], compare);
if (k == CMPERROR)
return -1;
if (k < 0)
{ tmp = a[j]; a[j] = a[i]; a[i] = tmp; }
k = docompare(a[j], a[0], compare);
if (k == CMPERROR)
return -1;
if (k < 0)
{ tmp = a[j]; a[j] = a[0]; a[0] = tmp; }
pivot = a[0];
/* Partition the array */
l = 0;
r = n;
for (;;) {
/* Move left index to element > pivot */
while (++l < n) {
k = docompare(a[l], pivot, compare);
if (k == CMPERROR)
return -1;
if (k > 0)
break;
}
/* Move right index to element < pivot */
while (--r > 0) {
k = docompare(a[r], pivot, compare);
if (k == CMPERROR)
return -1;
if (k < 0)
break;
}
/* If they met, we're through */
if (r < l)
break;
/* Swap elements and continue */
{ tmp = a[l]; a[l] = a[r]; a[r] = tmp; }
}
/* Move the pivot into the middle */
{ tmp = a[0]; a[0] = a[r]; a[r] = tmp; }
/* We have now reached the following conditions:
0 <= r < l <= n
all x in a[0:r] are <= pivot
all x in a[r:l] are == pivot
all x in a[l:n] are >= pivot
The partitions are a[0:r] and a[l:n]
*/
/* Push biggest partition first */
n2 = n - l;
if (r > n2) {
/* First one is bigger */
if (r > 1) {
astack[top] = a;
nstack[top++] = r;
if (n2 > 1) {
astack[top] = a+l;
nstack[top++] = n2;
}
}
} else {
/* Second one is bigger */
if (n2 > 1) {
astack[top] = a+l;
nstack[top++] = n2;
if (r > 1) {
astack[top] = a;
nstack[top++] = r;
}
}
}
/* Should assert top < STACKSIZE-1 */
}
/* Succes */
return 0;
}
static object *
listsort(self, compare)
listobject *self;
object *compare;
{
/* XXX Don't you *dare* changing the list's length in compare()! */
if (quicksort(self->ob_item, self->ob_size, compare) < 0)
return NULL;
INCREF(None);
return None;
}
#else /* !NEWSORT */
static object *comparefunc;
static int
@ -617,6 +842,8 @@ listsort(self, args)
return None;
}
#endif
static object *
listreverse(self, args)
listobject *self;