1
/* Copyright (C) 2000, 2005 MySQL AB
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
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.
25
#include "mysys_priv.h"
26
#include "mysys_err.h"
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
44
Will allocate max_element pointers for queue array
48
1 Could not allocate memory
51
int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
52
pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
55
DBUG_ENTER("init_queue");
56
if ((queue->root= (uchar **) my_malloc((max_elements+1)*sizeof(void*),
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);
71
Init queue, uses init_queue internally for init work but also accepts
72
auto_extent as parameter
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
87
Will allocate max_element pointers for queue array
91
1 Could not allocate memory
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)
99
DBUG_ENTER("init_queue_ex");
101
if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
105
queue->auto_extent= auto_extent;
110
Reinitialize queue for other usage
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
123
This will delete all elements from the queue. If you don't want this,
124
use resize_queue() instead.
128
EE_OUTOFMEMORY Wrong max_elements
131
int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
132
pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
135
DBUG_ENTER("reinit_queue");
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);
152
max_elements New max size for queue
155
If you resize queue to be less than the elements you have in it,
156
the extra elements will be deleted
160
1 Error. In this case the queue is unchanged
163
int resize_queue(QUEUE *queue, uint max_elements)
166
DBUG_ENTER("resize_queue");
167
if (queue->max_elements == max_elements)
169
if ((new_root= (uchar **) my_realloc((void *)queue->root,
170
(max_elements+1)*sizeof(void*),
173
set_if_smaller(queue->elements, max_elements);
174
queue->max_elements= max_elements;
175
queue->root= new_root;
185
queue Queue to delete
188
Just free allocated memory.
191
Can be called safely multiple times
194
void delete_queue(QUEUE *queue)
196
DBUG_ENTER("delete_queue");
199
my_free((uchar*) queue->root,MYF(0));
206
/* Code for insert, search and delete of elements */
208
void queue_insert(register QUEUE *queue, uchar *element)
210
register uint idx, next;
211
DBUG_ASSERT(queue->elements < queue->max_elements);
212
queue->root[0]= element;
213
idx= ++queue->elements;
214
/* max_at_top swaps the comparison if we want to order by desc */
215
while ((queue->compare(queue->first_cmp_arg,
216
element + queue->offset_to_key,
217
queue->root[(next= idx >> 1)] +
218
queue->offset_to_key) * queue->max_at_top) < 0)
220
queue->root[idx]= queue->root[next];
223
queue->root[idx]= element;
227
Does safe insert. If no more space left on the queue resize it.
230
1 - Cannot allocate more memory
231
2 - auto_extend is 0, the operation would
235
int queue_insert_safe(register QUEUE *queue, uchar *element)
238
if (queue->elements == queue->max_elements)
240
if (!queue->auto_extent)
242
else if (resize_queue(queue, queue->max_elements + queue->auto_extent))
246
queue_insert(queue, element);
251
/* Remove item from queue */
252
/* Returns pointer to removed element */
254
uchar *queue_remove(register QUEUE *queue, uint idx)
257
DBUG_ASSERT(idx < queue->max_elements);
258
element= queue->root[++idx]; /* Intern index starts from 1 */
259
queue->root[idx]= queue->root[queue->elements--];
260
_downheap(queue, idx);
264
/* Fix when element on top has been replaced */
266
#ifndef queue_replaced
267
void queue_replaced(QUEUE *queue)
275
void _downheap(register QUEUE *queue, uint idx)
278
uint elements,half_queue,offset_to_key, next_index;
282
offset_to_key=queue->offset_to_key;
283
element=queue->root[idx];
284
half_queue=(elements=queue->elements) >> 1;
286
while (idx <= half_queue)
289
if (next_index < elements &&
290
(queue->compare(queue->first_cmp_arg,
291
queue->root[next_index]+offset_to_key,
292
queue->root[next_index+1]+offset_to_key) *
293
queue->max_at_top) > 0)
296
(((queue->compare(queue->first_cmp_arg,
297
queue->root[next_index]+offset_to_key,
298
element+offset_to_key) * queue->max_at_top) >= 0)))
300
queue->root[idx]= element;
303
queue->root[idx]=queue->root[next_index];
308
next_index= idx >> 1;
309
while (next_index > start_idx)
311
if ((queue->compare(queue->first_cmp_arg,
312
queue->root[next_index]+offset_to_key,
313
element+offset_to_key) *
314
queue->max_at_top) < 0)
316
queue->root[idx]=queue->root[next_index];
318
next_index= idx >> 1;
320
queue->root[idx]=element;
325
The old _downheap version is kept for comparisons with the benchmark
326
suit or new benchmarks anyone wants to run for comparisons.
328
/* Fix heap when index have changed */
329
void _downheap(register QUEUE *queue, uint idx)
332
uint elements,half_queue,next_index,offset_to_key;
334
offset_to_key=queue->offset_to_key;
335
element=queue->root[idx];
336
half_queue=(elements=queue->elements) >> 1;
338
while (idx <= half_queue)
341
if (next_index < elements &&
342
(queue->compare(queue->first_cmp_arg,
343
queue->root[next_index]+offset_to_key,
344
queue->root[next_index+1]+offset_to_key) *
345
queue->max_at_top) > 0)
347
if ((queue->compare(queue->first_cmp_arg,
348
queue->root[next_index]+offset_to_key,
349
element+offset_to_key) * queue->max_at_top) >= 0)
351
queue->root[idx]=queue->root[next_index];
354
queue->root[idx]=element;
361
Fix heap when every element was changed.
364
void queue_fix(QUEUE *queue)
367
for (i= queue->elements >> 1; i > 0; i--)
373
A test program for the priority queue implementation.
374
It can also be used to benchmark changes of the implementation
375
Build by doing the following in the directory mysys
376
make test_priority_queue
377
./test_priority_queue
379
Written by Mikael Ronström, 2005
382
static uint num_array[1025];
383
static uint tot_no_parts= 0;
384
static uint tot_no_loops= 0;
385
static uint expected_part= 0;
386
static uint expected_num= 0;
387
static bool max_ind= 0;
388
static bool fix_used= 0;
389
static ulonglong start_time= 0;
391
static bool is_divisible_by(uint num, uint divisor)
393
uint quotient= num / divisor;
394
if (quotient * divisor == num)
399
void calculate_next()
401
uint part= expected_part, num= expected_num;
402
uint no_parts= tot_no_parts;
407
while (++part <= no_parts)
409
if (is_divisible_by(num, part) &&
410
(num <= ((1 << 21) + part)))
426
if (is_divisible_by(num, part))
438
void calculate_end_next(uint part)
440
uint no_parts= tot_no_parts, num;
445
for (part= no_parts; part > 0 ; part--)
449
num= num_array[part] & 0x3FFFFF;
450
if (num >= expected_num)
457
if (expected_num == 0)
462
expected_num= 0xFFFFFFFF;
463
for (part= 1; part <= no_parts; part++)
467
num= num_array[part] & 0x3FFFFF;
468
if (num <= expected_num)
475
if (expected_num == 0xFFFFFFFF)
480
static int test_compare(void *null_arg, uchar *a, uchar *b)
482
uint a_num= (*(uint*)a) & 0x3FFFFF;
483
uint b_num= (*(uint*)b) & 0x3FFFFF;
489
a_part= (*(uint*)a) >> 22;
490
b_part= (*(uint*)b) >> 22;
498
bool check_num(uint num_part)
500
uint part= num_part >> 22;
501
uint num= num_part & 0x3FFFFF;
502
if (part == expected_part)
503
if (num == expected_num)
505
printf("Expect part %u Expect num 0x%x got part %u num 0x%x max_ind %u fix_used %u \n",
506
expected_part, expected_num, part, num, max_ind, fix_used);
511
void perform_insert(QUEUE *queue)
513
uint i= 1, no_parts= tot_no_parts;
514
uint backward_start= 0;
520
backward_start= 1 << 21;
524
uint num= (i + backward_start);
527
while (!is_divisible_by(num, i))
529
if (max_ind && (num > expected_num ||
530
(num == expected_num && i < expected_part)))
536
num_array[i]= num + (i << 22);
538
queue_element(queue, i-1)= (uchar*)&num_array[i];
540
queue_insert(queue, (uchar*)&num_array[i]);
541
} while (++i <= no_parts);
544
queue->elements= no_parts;
549
bool perform_ins_del(QUEUE *queue, bool max_ind)
551
uint i= 0, no_loops= tot_no_loops, j= tot_no_parts;
554
uint num_part= *(uint*)queue_top(queue);
555
uint part= num_part >> 22;
556
if (check_num(num_part))
560
calculate_end_next(part);
561
queue_remove(queue, (uint) 0);
567
num_array[part]-= part;
569
num_array[part]+= part;
570
queue_top(queue)= (uchar*)&num_array[part];
571
queue_replaced(queue);
573
} while (++i < no_loops);
577
bool do_test(uint no_parts, uint l_max_ind, bool l_fix_used)
582
fix_used= l_fix_used;
583
init_queue(&queue, no_parts, 0, max_ind, test_compare, NULL);
584
tot_no_parts= no_parts;
586
perform_insert(&queue);
587
if ((result= perform_ins_del(&queue, max_ind)))
588
delete_queue(&queue);
597
static void start_measurement()
599
start_time= my_getsystime();
602
static void stop_measurement()
604
ulonglong stop_time= my_getsystime();
606
stop_time-= start_time;
607
stop_time/= 10; /* Convert to microseconds */
608
time_in_micros= (uint)stop_time;
609
printf("Time expired is %u microseconds \n", time_in_micros);
612
static void benchmark_test()
615
QUEUE *queue= &queue_real;
620
init_queue(queue, tot_no_parts, 0, max_ind, test_compare, NULL);
622
First benchmark whether queue_fix is faster than using queue_insert
623
for sizes of 16 partitions.
625
for (tot_no_parts= 2, add=2; tot_no_parts < 128;
626
tot_no_parts+= add, add++)
628
printf("Start benchmark queue_fix, tot_no_parts= %u \n", tot_no_parts);
630
for (i= 0; i < 128; i++)
632
perform_insert(queue);
633
queue_remove_all(queue);
638
printf("Start benchmark queue_insert\n");
640
for (i= 0; i < 128; i++)
642
perform_insert(queue);
643
queue_remove_all(queue);
648
Now benchmark insertion and deletion of 16400 elements.
649
Used in consecutive runs this shows whether the optimised _downheap
650
is faster than the standard implementation.
652
printf("Start benchmarking _downheap \n");
654
perform_insert(queue);
655
for (i= 0; i < 65536; i++)
658
num= *(uint*)queue_top(queue);
661
num_array[part]= num;
662
queue_top(queue)= (uchar*)&num_array[part];
663
queue_replaced(queue);
665
for (i= 0; i < 16; i++)
666
queue_remove(queue, (uint) 0);
667
queue_remove_all(queue);
674
for (i= 1; i < 1024; i+=add, add++)
676
printf("Start test for priority queue of size %u\n", i);
677
if (do_test(i, 0, 1))
679
if (do_test(i, 1, 1))
681
if (do_test(i, 0, 0))
683
if (do_test(i, 1, 0))