1*11449Smckusick /* @(#)qsort.c 4.2 (Berkeley) 03/09/83 */ 21975Swnj 3*11449Smckusick /* 4*11449Smckusick * qsort.c: 5*11449Smckusick * Our own version of the system qsort routine which is faster by an average 6*11449Smckusick * of 25%, with lows and highs of 10% and 50%. 7*11449Smckusick * The THRESHold below is the insertion sort threshold, and has been adjusted 8*11449Smckusick * for records of size 48 bytes. 9*11449Smckusick * The MTHREShold is where we stop finding a better median. 10*11449Smckusick */ 111975Swnj 12*11449Smckusick #define THRESH 4 /* threshold for insertion */ 13*11449Smckusick #define MTHRESH 6 /* threshold for median */ 141975Swnj 15*11449Smckusick static int (*qcmp)(); /* the comparison routine */ 16*11449Smckusick static int qsz; /* size of each record */ 17*11449Smckusick static int thresh; /* THRESHold in chars */ 18*11449Smckusick static int mthresh; /* MTHRESHold in chars */ 191975Swnj 20*11449Smckusick /* 21*11449Smckusick * qsort: 22*11449Smckusick * First, set up some global parameters for qst to share. Then, quicksort 23*11449Smckusick * with qst(), and then a cleanup insertion sort ourselves. Sound simple? 24*11449Smckusick * It's not... 25*11449Smckusick */ 261975Swnj 27*11449Smckusick qsort(base, n, size, compar) 28*11449Smckusick char *base; 29*11449Smckusick int n; 30*11449Smckusick int size; 31*11449Smckusick int (*compar)(); 32*11449Smckusick { 33*11449Smckusick register char c, *i, *j, *lo, *hi; 34*11449Smckusick char *min, *max; 351975Swnj 36*11449Smckusick if (n <= 1) 371975Swnj return; 38*11449Smckusick qsz = size; 39*11449Smckusick qcmp = compar; 40*11449Smckusick thresh = qsz * THRESH; 41*11449Smckusick mthresh = qsz * MTHRESH; 42*11449Smckusick max = base + n * qsz; 43*11449Smckusick if (n >= THRESH) { 44*11449Smckusick qst(base, max); 45*11449Smckusick hi = base + thresh; 46*11449Smckusick } else { 47*11449Smckusick hi = max; 48*11449Smckusick } 49*11449Smckusick /* 50*11449Smckusick * First put smallest element, which must be in the first THRESH, in 51*11449Smckusick * the first position as a sentinel. This is done just by searching 52*11449Smckusick * the first THRESH elements (or the first n if n < THRESH), finding 53*11449Smckusick * the min, and swapping it into the first position. 54*11449Smckusick */ 55*11449Smckusick for (j = lo = base; (lo += qsz) < hi; ) 56*11449Smckusick if (qcmp(j, lo) > 0) 57*11449Smckusick j = lo; 58*11449Smckusick if (j != base) { 59*11449Smckusick /* swap j into place */ 60*11449Smckusick for (i = base, hi = base + qsz; i < hi; ) { 61*11449Smckusick c = *j; 62*11449Smckusick *j++ = *i; 63*11449Smckusick *i++ = c; 64*11449Smckusick } 65*11449Smckusick } 66*11449Smckusick /* 67*11449Smckusick * With our sentinel in place, we now run the following hyper-fast 68*11449Smckusick * insertion sort. For each remaining element, min, from [1] to [n-1], 69*11449Smckusick * set hi to the index of the element AFTER which this one goes. 70*11449Smckusick * Then, do the standard insertion sort shift on a character at a time 71*11449Smckusick * basis for each element in the frob. 72*11449Smckusick */ 73*11449Smckusick for (min = base; (hi = min += qsz) < max; ) { 74*11449Smckusick while (qcmp(hi -= qsz, min) > 0) 75*11449Smckusick /* void */; 76*11449Smckusick if ((hi += qsz) != min) { 77*11449Smckusick for (lo = min + qsz; --lo >= min; ) { 78*11449Smckusick c = *lo; 79*11449Smckusick for (i = j = lo; (j -= qsz) >= hi; i = j) 80*11449Smckusick *i = *j; 81*11449Smckusick *i = c; 821975Swnj } 831975Swnj } 84*11449Smckusick } 85*11449Smckusick } 861975Swnj 87*11449Smckusick /* 88*11449Smckusick * qst: 89*11449Smckusick * Do a quicksort 90*11449Smckusick * First, find the median element, and put that one in the first place as the 91*11449Smckusick * discriminator. (This "median" is just the median of the first, last and 92*11449Smckusick * middle elements). (Using this median instead of the first element is a big 93*11449Smckusick * win). Then, the usual partitioning/swapping, followed by moving the 94*11449Smckusick * discriminator into the right place. Then, figure out the sizes of the two 95*11449Smckusick * partions, do the smaller one recursively and the larger one via a repeat of 96*11449Smckusick * this code. Stopping when there are less than THRESH elements in a partition 97*11449Smckusick * and cleaning up with an insertion sort (in our caller) is a huge win. 98*11449Smckusick * All data swaps are done in-line, which is space-losing but time-saving. 99*11449Smckusick * (And there are only three places where this is done). 100*11449Smckusick */ 101*11449Smckusick 102*11449Smckusick static 103*11449Smckusick qst(base, max) 104*11449Smckusick char *base, *max; 105*11449Smckusick { 106*11449Smckusick register char c, *i, *j, *jj; 107*11449Smckusick register int ii; 108*11449Smckusick char *mid, *tmp; 109*11449Smckusick int lo, hi; 110*11449Smckusick 111*11449Smckusick /* 112*11449Smckusick * At the top here, lo is the number of characters of elements in the 113*11449Smckusick * current partition. (Which should be max - base). 114*11449Smckusick * Find the median of the first, last, and middle element and make 115*11449Smckusick * that the middle element. Set j to largest of first and middle. 116*11449Smckusick * If max is larger than that guy, then it's that guy, else compare 117*11449Smckusick * max with loser of first and take larger. Things are set up to 118*11449Smckusick * prefer the middle, then the first in case of ties. 119*11449Smckusick */ 120*11449Smckusick lo = max - base; /* number of elements as chars */ 121*11449Smckusick do { 122*11449Smckusick mid = i = base + qsz * ((lo / qsz) >> 1); 123*11449Smckusick if (lo >= mthresh) { 124*11449Smckusick j = (qcmp((jj = base), i) > 0 ? jj : i); 125*11449Smckusick if (qcmp(j, (tmp = max - qsz)) > 0) { 126*11449Smckusick /* switch to first loser */ 127*11449Smckusick j = (j == jj ? i : jj); 128*11449Smckusick if (qcmp(j, tmp) < 0) 129*11449Smckusick j = tmp; 1301975Swnj } 131*11449Smckusick if (j != i) { 132*11449Smckusick ii = qsz; 133*11449Smckusick do { 134*11449Smckusick c = *i; 135*11449Smckusick *i++ = *j; 136*11449Smckusick *j++ = c; 137*11449Smckusick } while (--ii); 138*11449Smckusick } 139*11449Smckusick } 140*11449Smckusick /* 141*11449Smckusick * Semi-standard quicksort partitioning/swapping 142*11449Smckusick */ 143*11449Smckusick for (i = base, j = max - qsz; ; ) { 144*11449Smckusick while (i < mid && qcmp(i, mid) <= 0) 145*11449Smckusick i += qsz; 146*11449Smckusick while (j > mid) { 147*11449Smckusick if (qcmp(mid, j) <= 0) { 148*11449Smckusick j -= qsz; 149*11449Smckusick continue; 1501975Swnj } 151*11449Smckusick tmp = i + qsz; /* value of i after swap */ 152*11449Smckusick if (i == mid) { 153*11449Smckusick /* j <-> mid, new mid is j */ 154*11449Smckusick mid = jj = j; 155*11449Smckusick } else { 156*11449Smckusick /* i <-> j */ 157*11449Smckusick jj = j; 158*11449Smckusick j -= qsz; 159*11449Smckusick } 160*11449Smckusick goto swap; 1611975Swnj } 162*11449Smckusick if (i == mid) { 163*11449Smckusick break; 1641975Swnj } else { 165*11449Smckusick /* i <-> mid, new mid is i */ 166*11449Smckusick jj = mid; 167*11449Smckusick tmp = mid = i; /* value of i after swap */ 168*11449Smckusick j -= qsz; 1691975Swnj } 170*11449Smckusick swap: 171*11449Smckusick ii = qsz; 172*11449Smckusick do { 173*11449Smckusick c = *i; 174*11449Smckusick *i++ = *jj; 175*11449Smckusick *jj++ = c; 176*11449Smckusick } while (--ii); 177*11449Smckusick i = tmp; 1781975Swnj } 179*11449Smckusick /* 180*11449Smckusick * Look at sizes of the two partitions, do the smaller 181*11449Smckusick * one first by recursion, then do the larger one by 182*11449Smckusick * making sure lo is its size, base and max are update 183*11449Smckusick * correctly, and branching back. But only repeat 184*11449Smckusick * (recursively or by branching) if the partition is 185*11449Smckusick * of at least size THRESH. 186*11449Smckusick */ 187*11449Smckusick i = (j = mid) + qsz; 188*11449Smckusick if ((lo = j - base) <= (hi = max - i)) { 189*11449Smckusick if (lo >= thresh) 190*11449Smckusick qst(base, j); 191*11449Smckusick base = i; 192*11449Smckusick lo = hi; 193*11449Smckusick } else { 194*11449Smckusick if (hi >= thresh) 195*11449Smckusick qst(i, max); 196*11449Smckusick max = j; 197*11449Smckusick } 198*11449Smckusick } while (lo >= thresh); 1991975Swnj } 200