FD.io VPP  v20.01-48-g3e0dafb74
Vector Packet Processing
qsort.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * Imported into CLIB by Eliot Dresselhaus from:
17  *
18  * This file is part of
19  * MakeIndex - A formatter and format independent index processor
20  *
21  * This file is public domain software donated by
22  * Nelson Beebe (beebe@science.utah.edu).
23  *
24  * modifications copyright (c) 2003 Cisco Systems, Inc.
25  */
26 
27 #include <vppinfra/clib.h>
28 
29 /*
30  * qsort.c: Our own version of the system qsort routine which is faster by an
31  * average of 25%, with lows and highs of 10% and 50%. The THRESHold below is
32  * the insertion sort threshold, and has been adjusted for records of size 48
33  * bytes. The MTHREShold is where we stop finding a better median.
34  */
35 
36 #define THRESH 4 /* threshold for insertion */
37 #define MTHRESH 6 /* threshold for median */
38 
39 typedef struct
40 {
41  word qsz; /* size of each record */
42  word thresh; /* THRESHold in chars */
43  word mthresh; /* MTHRESHold in chars */
44  int (*qcmp) (const void *, const void *); /* the comparison routine */
45 } qst_t;
46 
47 static void qst (qst_t * q, char *base, char *max);
48 
49 /*
50  * qqsort: First, set up some global parameters for qst to share.
51  * Then, quicksort with qst(), and then a cleanup insertion sort ourselves.
52  * Sound simple? It's not...
53  */
54 
55 void
56 qsort (void *base, uword n, uword size,
57  int (*compar) (const void *, const void *))
58 {
59  char *i;
60  char *j;
61  char *lo;
62  char *hi;
63  char *min;
64  char c;
65  char *max;
66  qst_t _q, *q = &_q;
67 
68  if (n <= 1)
69  return;
70 
71  q->qsz = size;
72  q->qcmp = compar;
73  q->thresh = q->qsz * THRESH;
74  q->mthresh = q->qsz * MTHRESH;
75  max = base + n * q->qsz;
76  if (n >= THRESH)
77  {
78  qst (q, base, max);
79  hi = base + q->thresh;
80  }
81  else
82  {
83  hi = max;
84  }
85  /*
86  * First put smallest element, which must be in the first THRESH, in the
87  * first position as a sentinel. This is done just by searching the
88  * first THRESH elements (or the first n if n < THRESH), finding the min,
89  * and swapping it into the first position.
90  */
91  for (j = lo = base; (lo += q->qsz) < hi;)
92  {
93  if ((*compar) (j, lo) > 0)
94  j = lo;
95  }
96  if (j != base)
97  { /* swap j into place */
98  for (i = base, hi = base + q->qsz; i < hi;)
99  {
100  c = *j;
101  *j++ = *i;
102  *i++ = c;
103  }
104  }
105  /*
106  * With our sentinel in place, we now run the following hyper-fast
107  * insertion sort. For each remaining element, min, from [1] to [n-1],
108  * set hi to the index of the element AFTER which this one goes. Then, do
109  * the standard insertion sort shift on a character at a time basis for
110  * each element in the frob.
111  */
112  for (min = base; (hi = min += q->qsz) < max;)
113  {
114  while ((*q->qcmp) (hi -= q->qsz, min) > 0);
115  if ((hi += q->qsz) != min)
116  {
117  for (lo = min + q->qsz; --lo >= min;)
118  {
119  c = *lo;
120  for (i = j = lo; (j -= q->qsz) >= hi; i = j)
121  *i = *j;
122  *i = c;
123  }
124  }
125  }
126 }
127 
128 
129 
130 /*
131  * qst: Do a quicksort. First, find the median element, and put that one in
132  * the first place as the discriminator. (This "median" is just the median
133  * of the first, last and middle elements). (Using this median instead of
134  * the first element is a big win). Then, the usual partitioning/swapping,
135  * followed by moving the discriminator into the right place. Then, figure
136  * out the sizes of the two partitions, do the smaller one recursively and the
137  * larger one via a repeat of this code. Stopping when there are less than
138  * THRESH elements in a partition and cleaning up with an insertion sort (in
139  * our caller) is a huge win. All data swaps are done in-line, which is
140  * space-losing but time-saving. (And there are only three places where this
141  * is done).
142  */
143 
144 static void
145 qst (qst_t * q, char *base, char *max)
146 {
147  char *i;
148  char *j;
149  char *jj;
150  char *mid;
151  int ii;
152  char c;
153  char *tmp;
154  int lo;
155  int hi;
156  int qsz = q->qsz;
157 
158  lo = (int) (max - base); /* number of elements as chars */
159  do
160  {
161  /*
162  * At the top here, lo is the number of characters of elements in the
163  * current partition. (Which should be max - base). Find the median
164  * of the first, last, and middle element and make that the middle
165  * element. Set j to largest of first and middle. If max is larger
166  * than that guy, then it's that guy, else compare max with loser of
167  * first and take larger. Things are set up to prefer the middle,
168  * then the first in case of ties.
169  */
170  mid = i = base + qsz * ((unsigned) (lo / qsz) >> 1);
171  if (lo >= q->mthresh)
172  {
173  j = ((*q->qcmp) ((jj = base), i) > 0 ? jj : i);
174  if ((*q->qcmp) (j, (tmp = max - qsz)) > 0)
175  {
176  /* switch to first loser */
177  j = (j == jj ? i : jj);
178  if ((*q->qcmp) (j, tmp) < 0)
179  j = tmp;
180  }
181  if (j != i)
182  {
183  ii = qsz;
184  do
185  {
186  c = *i;
187  *i++ = *j;
188  *j++ = c;
189  }
190  while (--ii);
191  }
192  }
193  /* Semi-standard quicksort partitioning/swapping */
194  for (i = base, j = max - qsz;;)
195  {
196  while (i < mid && (*q->qcmp) (i, mid) <= 0)
197  i += qsz;
198  while (j > mid)
199  {
200  if ((*q->qcmp) (mid, j) <= 0)
201  {
202  j -= qsz;
203  continue;
204  }
205  tmp = i + qsz; /* value of i after swap */
206  if (i == mid)
207  { /* j <-> mid, new mid is j */
208  mid = jj = j;
209  }
210  else
211  { /* i <-> j */
212  jj = j;
213  j -= qsz;
214  }
215  goto swap;
216  }
217  if (i == mid)
218  {
219  break;
220  }
221  else
222  { /* i <-> mid, new mid is i */
223  jj = mid;
224  tmp = mid = i; /* value of i after swap */
225  j -= qsz;
226  }
227  swap:
228  ii = qsz;
229  do
230  {
231  c = *i;
232  *i++ = *jj;
233  *jj++ = c;
234  }
235  while (--ii);
236  i = tmp;
237  }
238  /*
239  * Look at sizes of the two partitions, do the smaller one first by
240  * recursion, then do the larger one by making sure lo is its size,
241  * base and max are update correctly, and branching back. But only
242  * repeat (recursively or by branching) if the partition is of at
243  * least size THRESH.
244  */
245  i = (j = mid) + qsz;
246  if ((lo = (int) (j - base)) <= (hi = (int) (max - i)))
247  {
248  if (lo >= q->thresh)
249  qst (q, base, j);
250  base = i;
251  lo = hi;
252  }
253  else
254  {
255  if (hi >= q->thresh)
256  qst (q, i, max);
257  max = j;
258  }
259  }
260  while (lo >= q->thresh);
261 }
262 
263 /*
264  * fd.io coding-style-patch-verification: ON
265  *
266  * Local Variables:
267  * eval: (c-set-style "gnu")
268  * End:
269  */
word thresh
Definition: qsort.c:42
Definition: qsort.c:39
static void qst(qst_t *q, char *base, char *max)
Definition: qsort.c:145
int i
i64 word
Definition: types.h:111
word mthresh
Definition: qsort.c:43
int(* qcmp)(const void *, const void *)
Definition: qsort.c:44
lo
u64 size
Definition: vhost_user.h:140
word qsz
Definition: qsort.c:41
svmdb_client_t * c
vl_api_ip4_address_t hi
Definition: arp.api:37
void qsort(void *base, uword n, uword size, int(*compar)(const void *, const void *))
Definition: qsort.c:56
#define MTHRESH
Definition: qsort.c:37
u64 uword
Definition: types.h:112
#define THRESH
Definition: qsort.c:36