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