mirror of
https://github.com/python/cpython.git
synced 2025-12-04 08:34:25 +00:00
Cleanup yielding a small speed boost: before rich comparisons were
introduced, list.sort() was rewritten to use only the "< or not <?" distinction. After rich comparisons were introduced, docompare() was fiddled to translate a Py_LT Boolean result into the old "-1 for <, 0 for ==, 1 for >" flavor of outcome, and the sorting code was left alone. This left things more obscure than they should be, and turns out it also cost measurable cycles. So: The old CMPERROR novelty is gone. docompare() is renamed to islt(), and now has the same return conditinos as PyObject_RichCompareBool. The SETK macro is renamed to ISLT, and is even weirder than before (don't complain unless you want to maintain the sort code <wink>). Overall, this yields a 1-2% speedup in the usual (no explicit function passed to list.sort()) case when sorting arrays of floats (as sortperf.py does). The boost is higher for arrays of ints.
This commit is contained in:
parent
3b01a1217f
commit
a8c974c157
1 changed files with 32 additions and 50 deletions
|
|
@ -758,39 +758,28 @@ reverse_slice(PyObject **lo, PyObject **hi)
|
||||||
/* New quicksort implementation for arrays of object pointers.
|
/* New quicksort implementation for arrays of object pointers.
|
||||||
Thanks to discussions with Tim Peters. */
|
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 ( (int) ((unsigned int)1 << (8*sizeof(int) - 1)) )
|
|
||||||
|
|
||||||
/* Comparison function. Takes care of calling a user-supplied
|
/* Comparison function. Takes care of calling a user-supplied
|
||||||
comparison function (any callable Python object). Calls the
|
comparison function (any callable Python object). Calls the
|
||||||
standard comparison function, PyObject_Compare(), if the user-
|
standard comparison function, PyObject_RichCompareBool(), if the user-
|
||||||
supplied function is NULL. */
|
supplied function is NULL.
|
||||||
|
Returns <0 on error, >0 if x < y, 0 if x >= y. */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
docompare(PyObject *x, PyObject *y, PyObject *compare)
|
islt(PyObject *x, PyObject *y, PyObject *compare)
|
||||||
{
|
{
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
PyObject *args;
|
PyObject *args;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (compare == NULL) {
|
if (compare == NULL)
|
||||||
/* NOTE: we rely on the fact here that the sorting algorithm
|
return PyObject_RichCompareBool(x, y, Py_LT);
|
||||||
only ever checks whether k<0, i.e., whether x<y. So we
|
|
||||||
invoke the rich comparison function with Py_LT ('<'), and
|
|
||||||
return -1 when it returns true and 0 when it returns
|
|
||||||
false. */
|
|
||||||
i = PyObject_RichCompareBool(x, y, Py_LT);
|
|
||||||
if (i < 0)
|
|
||||||
return CMPERROR;
|
|
||||||
else
|
|
||||||
return -i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Call the user's comparison function and translate the 3-way
|
||||||
|
* result into true or false (or error).
|
||||||
|
*/
|
||||||
args = PyTuple_New(2);
|
args = PyTuple_New(2);
|
||||||
if (args == NULL)
|
if (args == NULL)
|
||||||
return CMPERROR;
|
return -1;
|
||||||
Py_INCREF(x);
|
Py_INCREF(x);
|
||||||
Py_INCREF(y);
|
Py_INCREF(y);
|
||||||
PyTuple_SET_ITEM(args, 0, x);
|
PyTuple_SET_ITEM(args, 0, x);
|
||||||
|
|
@ -798,20 +787,16 @@ docompare(PyObject *x, PyObject *y, PyObject *compare)
|
||||||
res = PyObject_Call(compare, args, NULL);
|
res = PyObject_Call(compare, args, NULL);
|
||||||
Py_DECREF(args);
|
Py_DECREF(args);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
return CMPERROR;
|
return -1;
|
||||||
if (!PyInt_Check(res)) {
|
if (!PyInt_Check(res)) {
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"comparison function must return int");
|
"comparison function must return int");
|
||||||
return CMPERROR;
|
return -1;
|
||||||
}
|
}
|
||||||
i = PyInt_AsLong(res);
|
i = PyInt_AsLong(res);
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
if (i < 0)
|
return i < 0;
|
||||||
return -1;
|
|
||||||
if (i > 0)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MINSIZE is the smallest array that will get a full-blown samplesort
|
/* MINSIZE is the smallest array that will get a full-blown samplesort
|
||||||
|
|
@ -850,17 +835,21 @@ docompare(PyObject *x, PyObject *y, PyObject *compare)
|
||||||
exactly in two. */
|
exactly in two. */
|
||||||
#define STACKSIZE 60
|
#define STACKSIZE 60
|
||||||
|
|
||||||
|
/* Compare X to Y via islt(). Goto "fail" if the comparison raises an
|
||||||
#define SETK(X,Y) if ((k = docompare(X,Y,compare))==CMPERROR) goto fail
|
error. Else "k" is set to true iff X<Y, and an "if (k)" block is
|
||||||
|
started. It makes more sense in context <wink>. X and Y are PyObject*s.
|
||||||
|
*/
|
||||||
|
#define IFLT(X, Y) if ((k = islt(X, Y, compare)) < 0) goto fail; \
|
||||||
|
if (k)
|
||||||
|
|
||||||
/* binarysort is the best method for sorting small arrays: it does
|
/* binarysort is the best method for sorting small arrays: it does
|
||||||
few compares, but can do data movement quadratic in the number of
|
few compares, but can do data movement quadratic in the number of
|
||||||
elements.
|
elements.
|
||||||
[lo, hi) is a contiguous slice of a list, and is sorted via
|
[lo, hi) is a contiguous slice of a list, and is sorted via
|
||||||
binary insertion.
|
binary insertion. This sort is stable.
|
||||||
On entry, must have lo <= start <= hi, and that [lo, start) is already
|
On entry, must have lo <= start <= hi, and that [lo, start) is already
|
||||||
sorted (pass start == lo if you don't know!).
|
sorted (pass start == lo if you don't know!).
|
||||||
If docompare complains (returns CMPERROR) return -1, else 0.
|
If islt() complains return -1, else 0.
|
||||||
Even in case of error, the output slice will be some permutation of
|
Even in case of error, the output slice will be some permutation of
|
||||||
the input (nothing is lost or duplicated).
|
the input (nothing is lost or duplicated).
|
||||||
*/
|
*/
|
||||||
|
|
@ -869,12 +858,12 @@ static int
|
||||||
binarysort(PyObject **lo, PyObject **hi, PyObject **start, PyObject *compare)
|
binarysort(PyObject **lo, PyObject **hi, PyObject **start, PyObject *compare)
|
||||||
/* compare -- comparison function object, or NULL for default */
|
/* compare -- comparison function object, or NULL for default */
|
||||||
{
|
{
|
||||||
/* assert lo <= start <= hi
|
|
||||||
assert [lo, start) is sorted */
|
|
||||||
register int k;
|
register int k;
|
||||||
register PyObject **l, **p, **r;
|
register PyObject **l, **p, **r;
|
||||||
register PyObject *pivot;
|
register PyObject *pivot;
|
||||||
|
|
||||||
|
assert(lo <= start && start <= hi);
|
||||||
|
/* assert [lo, start) is sorted */
|
||||||
if (lo == start)
|
if (lo == start)
|
||||||
++start;
|
++start;
|
||||||
for (; start < hi; ++start) {
|
for (; start < hi; ++start) {
|
||||||
|
|
@ -884,8 +873,7 @@ binarysort(PyObject **lo, PyObject **hi, PyObject **start, PyObject *compare)
|
||||||
pivot = *r;
|
pivot = *r;
|
||||||
do {
|
do {
|
||||||
p = l + ((r - l) >> 1);
|
p = l + ((r - l) >> 1);
|
||||||
SETK(pivot, *p);
|
IFLT(pivot, *p)
|
||||||
if (k < 0)
|
|
||||||
r = p;
|
r = p;
|
||||||
else
|
else
|
||||||
l = p + 1;
|
l = p + 1;
|
||||||
|
|
@ -906,7 +894,7 @@ binarysort(PyObject **lo, PyObject **hi, PyObject **start, PyObject *compare)
|
||||||
/* samplesortslice is the sorting workhorse.
|
/* samplesortslice is the sorting workhorse.
|
||||||
[lo, hi) is a contiguous slice of a list, to be sorted in place.
|
[lo, hi) is a contiguous slice of a list, to be sorted in place.
|
||||||
On entry, must have lo <= hi,
|
On entry, must have lo <= hi,
|
||||||
If docompare complains (returns CMPERROR) return -1, else 0.
|
If islt() complains return -1, else 0.
|
||||||
Even in case of error, the output slice will be some permutation of
|
Even in case of error, the output slice will be some permutation of
|
||||||
the input (nothing is lost or duplicated).
|
the input (nothing is lost or duplicated).
|
||||||
|
|
||||||
|
|
@ -1023,8 +1011,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
*/
|
*/
|
||||||
/* assert lo < hi */
|
/* assert lo < hi */
|
||||||
for (r = lo+1; r < hi; ++r) {
|
for (r = lo+1; r < hi; ++r) {
|
||||||
SETK(*r, *(r-1));
|
IFLT(*r, *(r-1))
|
||||||
if (k < 0)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* [lo,r) is sorted, [r,hi) unknown. Get out cheap if there are
|
/* [lo,r) is sorted, [r,hi) unknown. Get out cheap if there are
|
||||||
|
|
@ -1036,8 +1023,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
benchmark-driven silliness <wink>. */
|
benchmark-driven silliness <wink>. */
|
||||||
/* assert lo < hi */
|
/* assert lo < hi */
|
||||||
for (r = lo+1; r < hi; ++r) {
|
for (r = lo+1; r < hi; ++r) {
|
||||||
SETK(*(r-1), *r);
|
IFLT(*(r-1), *r)
|
||||||
if (k < 0)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (hi - r <= MAXMERGE) {
|
if (hi - r <= MAXMERGE) {
|
||||||
|
|
@ -1192,8 +1178,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
do {
|
do {
|
||||||
/* slide l right, looking for key >= pivot */
|
/* slide l right, looking for key >= pivot */
|
||||||
do {
|
do {
|
||||||
SETK(*l, pivot);
|
IFLT(*l, pivot)
|
||||||
if (k < 0)
|
|
||||||
++l;
|
++l;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
@ -1202,8 +1187,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
/* slide r left, looking for key < pivot */
|
/* slide r left, looking for key < pivot */
|
||||||
while (l < r) {
|
while (l < r) {
|
||||||
register PyObject *rval = *r--;
|
register PyObject *rval = *r--;
|
||||||
SETK(rval, pivot);
|
IFLT(rval, pivot) {
|
||||||
if (k < 0) {
|
|
||||||
/* swap and advance */
|
/* swap and advance */
|
||||||
r[1] = *l;
|
r[1] = *l;
|
||||||
*l++ = rval;
|
*l++ = rval;
|
||||||
|
|
@ -1219,8 +1203,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
everything to the right of r is >= pivot */
|
everything to the right of r is >= pivot */
|
||||||
|
|
||||||
if (l == r) {
|
if (l == r) {
|
||||||
SETK(*r, pivot);
|
IFLT(*r, pivot)
|
||||||
if (k < 0)
|
|
||||||
++l;
|
++l;
|
||||||
else
|
else
|
||||||
--r;
|
--r;
|
||||||
|
|
@ -1249,8 +1232,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
*/
|
*/
|
||||||
while (l < hi) {
|
while (l < hi) {
|
||||||
/* pivot <= *l known */
|
/* pivot <= *l known */
|
||||||
SETK(pivot, *l);
|
IFLT(pivot, *l)
|
||||||
if (k < 0)
|
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
/* <= and not < implies == */
|
/* <= and not < implies == */
|
||||||
|
|
@ -1290,7 +1272,7 @@ samplesortslice(PyObject **lo, PyObject **hi, PyObject *compare)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef SETK
|
#undef IFLT
|
||||||
|
|
||||||
static PyTypeObject immutable_list_type;
|
static PyTypeObject immutable_list_type;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue