MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
queues.c
1 /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 /*
17  Code for generell handling of priority Queues.
18  Implemention of queues from "Algoritms in C" by Robert Sedgewick.
19  An optimisation of _downheap suggested in Exercise 7.51 in "Data
20  Structures & Algorithms in C++" by Mark Allen Weiss, Second Edition
21  was implemented by Mikael Ronstrom 2005. Also the O(N) algorithm
22  of queue_fix was implemented.
23 */
24 
25 #include "mysys_priv.h"
26 #include "mysys_err.h"
27 #include <queues.h>
28 
29 
30 /*
31  Init queue
32 
33  SYNOPSIS
34  init_queue()
35  queue Queue to initialise
36  max_elements Max elements that will be put in queue
37  offset_to_key Offset to key in element stored in queue
38  Used when sending pointers to compare function
39  max_at_top Set to 1 if you want biggest element on top.
40  compare Compare function for elements, takes 3 arguments.
41  first_cmp_arg First argument to compare function
42 
43  NOTES
44  Will allocate max_element pointers for queue array
45 
46  RETURN
47  0 ok
48  1 Could not allocate memory
49 */
50 
51 int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
52  pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
53  void *first_cmp_arg)
54 {
55  DBUG_ENTER("init_queue");
56  if ((queue->root= (uchar **) my_malloc((max_elements+1)*sizeof(void*),
57  MYF(MY_WME))) == 0)
58  DBUG_RETURN(1);
59  queue->elements=0;
60  queue->compare=compare;
61  queue->first_cmp_arg=first_cmp_arg;
62  queue->max_elements=max_elements;
63  queue->offset_to_key=offset_to_key;
64  queue_set_max_at_top(queue, max_at_top);
65  DBUG_RETURN(0);
66 }
67 
68 
69 
70 /*
71  Init queue, uses init_queue internally for init work but also accepts
72  auto_extent as parameter
73 
74  SYNOPSIS
75  init_queue_ex()
76  queue Queue to initialise
77  max_elements Max elements that will be put in queue
78  offset_to_key Offset to key in element stored in queue
79  Used when sending pointers to compare function
80  max_at_top Set to 1 if you want biggest element on top.
81  compare Compare function for elements, takes 3 arguments.
82  first_cmp_arg First argument to compare function
83  auto_extent When the queue is full and there is insert operation
84  extend the queue.
85 
86  NOTES
87  Will allocate max_element pointers for queue array
88 
89  RETURN
90  0 ok
91  1 Could not allocate memory
92 */
93 
94 int init_queue_ex(QUEUE *queue, uint max_elements, uint offset_to_key,
95  pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
96  void *first_cmp_arg, uint auto_extent)
97 {
98  int ret;
99  DBUG_ENTER("init_queue_ex");
100 
101  if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
102  first_cmp_arg)))
103  DBUG_RETURN(ret);
104 
105  queue->auto_extent= auto_extent;
106  DBUG_RETURN(0);
107 }
108 
109 /*
110  Reinitialize queue for other usage
111 
112  SYNOPSIS
113  reinit_queue()
114  queue Queue to initialise
115  max_elements Max elements that will be put in queue
116  offset_to_key Offset to key in element stored in queue
117  Used when sending pointers to compare function
118  max_at_top Set to 1 if you want biggest element on top.
119  compare Compare function for elements, takes 3 arguments.
120  first_cmp_arg First argument to compare function
121 
122  NOTES
123  This will delete all elements from the queue. If you don't want this,
124  use resize_queue() instead.
125 
126  RETURN
127  0 ok
128  EE_OUTOFMEMORY Wrong max_elements
129 */
130 
131 int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
132  pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
133  void *first_cmp_arg)
134 {
135  DBUG_ENTER("reinit_queue");
136  queue->elements=0;
137  queue->compare=compare;
138  queue->first_cmp_arg=first_cmp_arg;
139  queue->offset_to_key=offset_to_key;
140  queue_set_max_at_top(queue, max_at_top);
141  resize_queue(queue, max_elements);
142  DBUG_RETURN(0);
143 }
144 
145 
146 /*
147  Resize queue
148 
149  SYNOPSIS
150  resize_queue()
151  queue Queue
152  max_elements New max size for queue
153 
154  NOTES
155  If you resize queue to be less than the elements you have in it,
156  the extra elements will be deleted
157 
158  RETURN
159  0 ok
160  1 Error. In this case the queue is unchanged
161 */
162 
163 int resize_queue(QUEUE *queue, uint max_elements)
164 {
165  uchar **new_root;
166  DBUG_ENTER("resize_queue");
167  if (queue->max_elements == max_elements)
168  DBUG_RETURN(0);
169  if ((new_root= (uchar **) my_realloc((void *)queue->root,
170  (max_elements+1)*sizeof(void*),
171  MYF(MY_WME))) == 0)
172  DBUG_RETURN(1);
173  set_if_smaller(queue->elements, max_elements);
174  queue->max_elements= max_elements;
175  queue->root= new_root;
176  DBUG_RETURN(0);
177 }
178 
179 
180 /*
181  Delete queue
182 
183  SYNOPSIS
184  delete_queue()
185  queue Queue to delete
186 
187  IMPLEMENTATION
188  Just free allocated memory.
189 
190  NOTES
191  Can be called safely multiple times
192 */
193 
194 void delete_queue(QUEUE *queue)
195 {
196  DBUG_ENTER("delete_queue");
197  my_free(queue->root);
198  queue->root= NULL;
199  DBUG_VOID_RETURN;
200 }
201 
202 
203  /* Code for insert, search and delete of elements */
204 
205 void queue_insert(register QUEUE *queue, uchar *element)
206 {
207  reg2 uint idx, next;
208  DBUG_ASSERT(queue->elements < queue->max_elements);
209  queue->root[0]= element;
210  idx= ++queue->elements;
211  /* max_at_top swaps the comparison if we want to order by desc */
212  while ((queue->compare(queue->first_cmp_arg,
213  element + queue->offset_to_key,
214  queue->root[(next= idx >> 1)] +
215  queue->offset_to_key) * queue->max_at_top) < 0)
216  {
217  queue->root[idx]= queue->root[next];
218  idx= next;
219  }
220  queue->root[idx]= element;
221 }
222 
223 /*
224  Does safe insert. If no more space left on the queue resize it.
225  Return codes:
226  0 - OK
227  1 - Cannot allocate more memory
228  2 - auto_extend is 0, the operation would
229 
230 */
231 
232 int queue_insert_safe(register QUEUE *queue, uchar *element)
233 {
234 
235  if (queue->elements == queue->max_elements)
236  {
237  if (!queue->auto_extent)
238  return 2;
239  else if (resize_queue(queue, queue->max_elements + queue->auto_extent))
240  return 1;
241  }
242 
243  queue_insert(queue, element);
244  return 0;
245 }
246 
247 
248  /* Remove item from queue */
249  /* Returns pointer to removed element */
250 
251 uchar *queue_remove(register QUEUE *queue, uint idx)
252 {
253  uchar *element;
254  DBUG_ASSERT(idx < queue->max_elements);
255  element= queue->root[++idx]; /* Intern index starts from 1 */
256  queue->root[idx]= queue->root[queue->elements--];
257  _downheap(queue, idx);
258  return element;
259 }
260 
261  /* Fix when element on top has been replaced */
262 
263 #ifndef queue_replaced
264 void queue_replaced(QUEUE *queue)
265 {
266  _downheap(queue,1);
267 }
268 #endif
269 
270 #ifndef OLD_VERSION
271 
272 void _downheap(register QUEUE *queue, uint idx)
273 {
274  uchar *element;
275  uint elements,half_queue,offset_to_key, next_index;
276  my_bool first= TRUE;
277  uint start_idx= idx;
278 
279  offset_to_key=queue->offset_to_key;
280  element=queue->root[idx];
281  half_queue=(elements=queue->elements) >> 1;
282 
283  while (idx <= half_queue)
284  {
285  next_index=idx+idx;
286  if (next_index < elements &&
287  (queue->compare(queue->first_cmp_arg,
288  queue->root[next_index]+offset_to_key,
289  queue->root[next_index+1]+offset_to_key) *
290  queue->max_at_top) > 0)
291  next_index++;
292  if (first &&
293  (((queue->compare(queue->first_cmp_arg,
294  queue->root[next_index]+offset_to_key,
295  element+offset_to_key) * queue->max_at_top) >= 0)))
296  {
297  queue->root[idx]= element;
298  return;
299  }
300  queue->root[idx]=queue->root[next_index];
301  idx=next_index;
302  first= FALSE;
303  }
304 
305  next_index= idx >> 1;
306  while (next_index > start_idx)
307  {
308  if ((queue->compare(queue->first_cmp_arg,
309  queue->root[next_index]+offset_to_key,
310  element+offset_to_key) *
311  queue->max_at_top) < 0)
312  break;
313  queue->root[idx]=queue->root[next_index];
314  idx=next_index;
315  next_index= idx >> 1;
316  }
317  queue->root[idx]=element;
318 }
319 
320 #else
321  /*
322  The old _downheap version is kept for comparisons with the benchmark
323  suit or new benchmarks anyone wants to run for comparisons.
324  */
325  /* Fix heap when index have changed */
326 void _downheap(register QUEUE *queue, uint idx)
327 {
328  uchar *element;
329  uint elements,half_queue,next_index,offset_to_key;
330 
331  offset_to_key=queue->offset_to_key;
332  element=queue->root[idx];
333  half_queue=(elements=queue->elements) >> 1;
334 
335  while (idx <= half_queue)
336  {
337  next_index=idx+idx;
338  if (next_index < elements &&
339  (queue->compare(queue->first_cmp_arg,
340  queue->root[next_index]+offset_to_key,
341  queue->root[next_index+1]+offset_to_key) *
342  queue->max_at_top) > 0)
343  next_index++;
344  if ((queue->compare(queue->first_cmp_arg,
345  queue->root[next_index]+offset_to_key,
346  element+offset_to_key) * queue->max_at_top) >= 0)
347  break;
348  queue->root[idx]=queue->root[next_index];
349  idx=next_index;
350  }
351  queue->root[idx]=element;
352 }
353 
354 
355 #endif
356 
357 /*
358  Fix heap when every element was changed.
359 */
360 
361 void queue_fix(QUEUE *queue)
362 {
363  uint i;
364  for (i= queue->elements >> 1; i > 0; i--)
365  _downheap(queue, i);
366 }
367 
368 #ifdef MAIN
369  /*
370  A test program for the priority queue implementation.
371  It can also be used to benchmark changes of the implementation
372  Build by doing the following in the directory mysys
373  make queues
374  ./queues
375 
376  Written by Mikael Ronström, 2005
377  */
378 
379 static uint num_array[1025];
380 static uint tot_no_parts= 0;
381 static uint tot_no_loops= 0;
382 static uint expected_part= 0;
383 static uint expected_num= 0;
384 static my_bool max_ind= 0;
385 static my_bool fix_used= 0;
386 static ulonglong start_time= 0;
387 
388 static my_bool is_divisible_by(uint num, uint divisor)
389 {
390  uint quotient= num / divisor;
391  if (quotient * divisor == num)
392  return TRUE;
393  return FALSE;
394 }
395 
396 void calculate_next()
397 {
398  uint part= expected_part, num= expected_num;
399  uint no_parts= tot_no_parts;
400  if (max_ind)
401  {
402  do
403  {
404  while (++part <= no_parts)
405  {
406  if (is_divisible_by(num, part) &&
407  (num <= ((1 << 21) + part)))
408  {
409  expected_part= part;
410  expected_num= num;
411  return;
412  }
413  }
414  part= 0;
415  } while (--num);
416  }
417  else
418  {
419  do
420  {
421  while (--part > 0)
422  {
423  if (is_divisible_by(num, part))
424  {
425  expected_part= part;
426  expected_num= num;
427  return;
428  }
429  }
430  part= no_parts + 1;
431  } while (++num);
432  }
433 }
434 
435 void calculate_end_next(uint part)
436 {
437  uint no_parts= tot_no_parts, num;
438  num_array[part]= 0;
439  if (max_ind)
440  {
441  expected_num= 0;
442  for (part= no_parts; part > 0 ; part--)
443  {
444  if (num_array[part])
445  {
446  num= num_array[part] & 0x3FFFFF;
447  if (num >= expected_num)
448  {
449  expected_num= num;
450  expected_part= part;
451  }
452  }
453  }
454  if (expected_num == 0)
455  expected_part= 0;
456  }
457  else
458  {
459  expected_num= 0xFFFFFFFF;
460  for (part= 1; part <= no_parts; part++)
461  {
462  if (num_array[part])
463  {
464  num= num_array[part] & 0x3FFFFF;
465  if (num <= expected_num)
466  {
467  expected_num= num;
468  expected_part= part;
469  }
470  }
471  }
472  if (expected_num == 0xFFFFFFFF)
473  expected_part= 0;
474  }
475  return;
476 }
477 static int test_compare(void *null_arg, uchar *a, uchar *b)
478 {
479  uint a_num= (*(uint*)a) & 0x3FFFFF;
480  uint b_num= (*(uint*)b) & 0x3FFFFF;
481  uint a_part, b_part;
482  (void) null_arg;
483  if (a_num > b_num)
484  return +1;
485  if (a_num < b_num)
486  return -1;
487  a_part= (*(uint*)a) >> 22;
488  b_part= (*(uint*)b) >> 22;
489  if (a_part < b_part)
490  return +1;
491  if (a_part > b_part)
492  return -1;
493  return 0;
494 }
495 
496 my_bool check_num(uint num_part)
497 {
498  uint part= num_part >> 22;
499  uint num= num_part & 0x3FFFFF;
500  if (part == expected_part)
501  if (num == expected_num)
502  return FALSE;
503  printf("Expect part %u Expect num 0x%x got part %u num 0x%x max_ind %u fix_used %u \n",
504  expected_part, expected_num, part, num, max_ind, fix_used);
505  return TRUE;
506 }
507 
508 
509 void perform_insert(QUEUE *queue)
510 {
511  uint i= 1, no_parts= tot_no_parts;
512  uint backward_start= 0;
513 
514  expected_part= 1;
515  expected_num= 1;
516 
517  if (max_ind)
518  backward_start= 1 << 21;
519 
520  do
521  {
522  uint num= (i + backward_start);
523  if (max_ind)
524  {
525  while (!is_divisible_by(num, i))
526  num--;
527  if (max_ind && (num > expected_num ||
528  (num == expected_num && i < expected_part)))
529  {
530  expected_num= num;
531  expected_part= i;
532  }
533  }
534  num_array[i]= num + (i << 22);
535  if (fix_used)
536  queue_element(queue, i-1)= (uchar*)&num_array[i];
537  else
538  queue_insert(queue, (uchar*)&num_array[i]);
539  } while (++i <= no_parts);
540  if (fix_used)
541  {
542  queue->elements= no_parts;
543  queue_fix(queue);
544  }
545 }
546 
547 my_bool perform_ins_del(QUEUE *queue, my_bool max_ind)
548 {
549  uint i= 0, no_loops= tot_no_loops, j= tot_no_parts;
550  do
551  {
552  uint num_part= *(uint*)queue_top(queue);
553  uint part= num_part >> 22;
554  if (check_num(num_part))
555  return TRUE;
556  if (j++ >= no_loops)
557  {
558  calculate_end_next(part);
559  queue_remove(queue, (uint) 0);
560  }
561  else
562  {
563  calculate_next();
564  if (max_ind)
565  num_array[part]-= part;
566  else
567  num_array[part]+= part;
568  queue_top(queue)= (uchar*)&num_array[part];
569  queue_replaced(queue);
570  }
571  } while (++i < no_loops);
572  return FALSE;
573 }
574 
575 my_bool do_test(uint no_parts, uint l_max_ind, my_bool l_fix_used)
576 {
577  QUEUE queue;
578  my_bool result;
579  max_ind= l_max_ind;
580  fix_used= l_fix_used;
581  init_queue(&queue, no_parts, 0, max_ind, test_compare, NULL);
582  tot_no_parts= no_parts;
583  tot_no_loops= 1024;
584  perform_insert(&queue);
585  if ((result= perform_ins_del(&queue, max_ind)))
586  delete_queue(&queue);
587  if (result)
588  {
589  printf("Error\n");
590  return TRUE;
591  }
592  return FALSE;
593 }
594 
595 static void start_measurement()
596 {
597  start_time= my_getsystime();
598 }
599 
600 static void stop_measurement()
601 {
602  ulonglong stop_time= my_getsystime();
603  uint time_in_micros;
604  stop_time-= start_time;
605  stop_time/= 10; /* Convert to microseconds */
606  time_in_micros= (uint)stop_time;
607  printf("Time expired is %u microseconds \n", time_in_micros);
608 }
609 
610 static void benchmark_test()
611 {
612  QUEUE queue_real;
613  QUEUE *queue= &queue_real;
614  uint i, add;
615  fix_used= TRUE;
616  max_ind= FALSE;
617  tot_no_parts= 1024;
618  init_queue(queue, tot_no_parts, 0, max_ind, test_compare, NULL);
619  /*
620  First benchmark whether queue_fix is faster than using queue_insert
621  for sizes of 16 partitions.
622  */
623  for (tot_no_parts= 2, add=2; tot_no_parts < 128;
624  tot_no_parts+= add, add++)
625  {
626  printf("Start benchmark queue_fix, tot_no_parts= %u \n", tot_no_parts);
627  start_measurement();
628  for (i= 0; i < 128; i++)
629  {
630  perform_insert(queue);
631  queue_remove_all(queue);
632  }
633  stop_measurement();
634 
635  fix_used= FALSE;
636  printf("Start benchmark queue_insert\n");
637  start_measurement();
638  for (i= 0; i < 128; i++)
639  {
640  perform_insert(queue);
641  queue_remove_all(queue);
642  }
643  stop_measurement();
644  }
645  /*
646  Now benchmark insertion and deletion of 16400 elements.
647  Used in consecutive runs this shows whether the optimised _downheap
648  is faster than the standard implementation.
649  */
650  printf("Start benchmarking _downheap \n");
651  start_measurement();
652  perform_insert(queue);
653  for (i= 0; i < 65536; i++)
654  {
655  uint num, part;
656  num= *(uint*)queue_top(queue);
657  num+= 16;
658  part= num >> 22;
659  num_array[part]= num;
660  queue_top(queue)= (uchar*)&num_array[part];
661  queue_replaced(queue);
662  }
663  for (i= 0; i < 16; i++)
664  queue_remove(queue, (uint) 0);
665  queue_remove_all(queue);
666  stop_measurement();
667 }
668 
669 int main()
670 {
671  int i, add= 1;
672  for (i= 1; i < 1024; i+=add, add++)
673  {
674  printf("Start test for priority queue of size %u\n", i);
675  if (do_test(i, 0, 1))
676  return -1;
677  if (do_test(i, 1, 1))
678  return -1;
679  if (do_test(i, 0, 0))
680  return -1;
681  if (do_test(i, 1, 0))
682  return -1;
683  }
684  benchmark_test();
685  printf("OK\n");
686  return 0;
687 }
688 #endif