diff --git a/Objects/listobject.c b/Objects/listobject.c index 6da8391fc2..3d44ed1f2b 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -1238,11 +1238,13 @@ struct s_MergeState { the input (nothing is lost or duplicated). */ static int -binarysort(MergeState *ms, sortslice lo, PyObject **hi, PyObject **start) +binarysort(MergeState *ms, sortslice lo, PyObject **hi, + PyObject **start, int descending) { Py_ssize_t k; PyObject **l, **p, **r; PyObject *pivot; + int initial = 1; assert(lo.keys <= start && start <= hi); /* assert [lo, start) is sorted */ @@ -1261,11 +1263,35 @@ binarysort(MergeState *ms, sortslice lo, PyObject **hi, PyObject **start) assert(l < r); do { p = l + ((r - l) >> 1); - IFLT(pivot, *p) + if (initial && !descending && start == p + 1) { + /* Compare two consecutive elements: + * the start of the non-run and the last of the ascending run. + * We've already done this comparison on the initial scan, + * so skip the less-than check. + */ r = p; - else - l = p+1; + } + else if (initial && descending && pivot == *start && p == lo.keys) { + /* Compare two consecutive elements: + * the first element of an already-reversed descending run + * (previously the last element of the run), and the first + * element of the non-run. + * We've already done this comparison on the initial scan, + * so skip the less-than check. + */ + l = p + 1; + } + else { + IFLT(pivot, *p) { + r = p; + } + else { + l = p + 1; + } + } } while (l < r); + initial = 0; + assert(l == r); /* The invariants still hold, so pivot >= all in [lo, l) and pivot < all in [l, start), so pivot belongs at l. Note @@ -2392,7 +2418,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse) if (n < minrun) { const Py_ssize_t force = nremaining <= minrun ? nremaining : minrun; - if (binarysort(&ms, lo, lo.keys + force, lo.keys + n) < 0) + if (binarysort(&ms, lo, lo.keys + force, lo.keys + n, descending) < 0) goto fail; n = force; }