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/mysys_priv.h>
26
#include <mysys/mysys_err.h>
36
queue Queue to initialise
37
max_elements Max elements that will be put in queue
38
offset_to_key Offset to key in element stored in queue
39
Used when sending pointers to compare function
40
max_at_top Set to 1 if you want biggest element on top.
41
compare Compare function for elements, takes 3 arguments.
42
first_cmp_arg First argument to compare function
45
Will allocate max_element pointers for queue array
49
1 Could not allocate memory
52
int init_queue(QUEUE *queue, uint32_t max_elements, uint32_t offset_to_key,
53
bool max_at_top, queue_compare compare,
57
(unsigned char **) malloc((max_elements+1)*sizeof(void*))) == 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);
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, uint32_t max_elements, uint32_t offset_to_key,
95
bool max_at_top, queue_compare compare,
96
void *first_cmp_arg, uint32_t auto_extent)
100
if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
104
queue->auto_extent= auto_extent;
109
Reinitialize queue for other usage
113
queue Queue to initialise
114
max_elements Max elements that will be put in queue
115
offset_to_key Offset to key in element stored in queue
116
Used when sending pointers to compare function
117
max_at_top Set to 1 if you want biggest element on top.
118
compare Compare function for elements, takes 3 arguments.
119
first_cmp_arg First argument to compare function
122
This will delete all elements from the queue. If you don't want this,
123
use resize_queue() instead.
127
EE_OUTOFMEMORY Wrong max_elements
130
int reinit_queue(QUEUE *queue, uint32_t max_elements, uint32_t offset_to_key,
131
bool max_at_top, queue_compare compare,
135
queue->compare=compare;
136
queue->first_cmp_arg=first_cmp_arg;
137
queue->offset_to_key=offset_to_key;
138
queue_set_max_at_top(queue, max_at_top);
139
resize_queue(queue, max_elements);
150
max_elements New max size for queue
153
If you resize queue to be less than the elements you have in it,
154
the extra elements will be deleted
158
1 Error. In this case the queue is unchanged
161
int resize_queue(QUEUE *queue, uint32_t max_elements)
163
unsigned char **new_root;
164
if (queue->max_elements == max_elements)
167
(unsigned char **) realloc(queue->root,
168
(max_elements+1)*sizeof(void*))) == 0)
170
set_if_smaller(queue->elements, max_elements);
171
queue->max_elements= max_elements;
172
queue->root= new_root;
182
queue Queue to delete
185
Just free allocated memory.
188
Can be called safely multiple times
191
void delete_queue(QUEUE *queue)
195
free((unsigned char*) queue->root);
202
/* Code for insert, search and delete of elements */
204
void queue_insert(register QUEUE *queue, unsigned char *element)
206
register uint32_t idx, next;
207
assert(queue->elements < queue->max_elements);
208
queue->root[0]= element;
209
idx= ++queue->elements;
210
/* max_at_top swaps the comparison if we want to order by desc */
211
while ((queue->compare(queue->first_cmp_arg,
212
element + queue->offset_to_key,
213
queue->root[(next= idx >> 1)] +
214
queue->offset_to_key) * queue->max_at_top) < 0)
216
queue->root[idx]= queue->root[next];
219
queue->root[idx]= element;
223
Does safe insert. If no more space left on the queue resize it.
226
1 - Cannot allocate more memory
227
2 - auto_extend is 0, the operation would
231
int queue_insert_safe(register QUEUE *queue, unsigned char *element)
234
if (queue->elements == queue->max_elements)
236
if (!queue->auto_extent)
238
else if (resize_queue(queue, queue->max_elements + queue->auto_extent))
242
queue_insert(queue, element);
247
/* Remove item from queue */
248
/* Returns pointer to removed element */
250
unsigned char *queue_remove(register QUEUE *queue, uint32_t idx)
252
unsigned char *element;
253
assert(idx < queue->max_elements);
254
element= queue->root[++idx]; /* Intern index starts from 1 */
255
queue->root[idx]= queue->root[queue->elements--];
256
_downheap(queue, idx);
260
/* Fix when element on top has been replaced */
262
#ifndef queue_replaced
263
void queue_replaced(QUEUE *queue)
271
void _downheap(register QUEUE *queue, uint32_t idx)
273
unsigned char *element;
274
uint32_t elements,half_queue,offset_to_key, next_index;
276
uint32_t start_idx= idx;
278
offset_to_key=queue->offset_to_key;
279
element=queue->root[idx];
280
half_queue=(elements=queue->elements) >> 1;
282
while (idx <= half_queue)
285
if (next_index < elements &&
286
(queue->compare(queue->first_cmp_arg,
287
queue->root[next_index]+offset_to_key,
288
queue->root[next_index+1]+offset_to_key) *
289
queue->max_at_top) > 0)
292
(((queue->compare(queue->first_cmp_arg,
293
queue->root[next_index]+offset_to_key,
294
element+offset_to_key) * queue->max_at_top) >= 0)))
296
queue->root[idx]= element;
299
queue->root[idx]=queue->root[next_index];
304
next_index= idx >> 1;
305
while (next_index > start_idx)
307
if ((queue->compare(queue->first_cmp_arg,
308
queue->root[next_index]+offset_to_key,
309
element+offset_to_key) *
310
queue->max_at_top) < 0)
312
queue->root[idx]=queue->root[next_index];
314
next_index= idx >> 1;
316
queue->root[idx]=element;
321
The old _downheap version is kept for comparisons with the benchmark
322
suit or new benchmarks anyone wants to run for comparisons.
324
/* Fix heap when index have changed */
325
void _downheap(register QUEUE *queue, uint32_t idx)
327
unsigned char *element;
328
uint32_t elements,half_queue,next_index,offset_to_key;
330
offset_to_key=queue->offset_to_key;
331
element=queue->root[idx];
332
half_queue=(elements=queue->elements) >> 1;
334
while (idx <= half_queue)
337
if (next_index < elements &&
338
(queue->compare(queue->first_cmp_arg,
339
queue->root[next_index]+offset_to_key,
340
queue->root[next_index+1]+offset_to_key) *
341
queue->max_at_top) > 0)
343
if ((queue->compare(queue->first_cmp_arg,
344
queue->root[next_index]+offset_to_key,
345
element+offset_to_key) * queue->max_at_top) >= 0)
347
queue->root[idx]=queue->root[next_index];
350
queue->root[idx]=element;
357
Fix heap when every element was changed.
360
void queue_fix(QUEUE *queue)
363
for (i= queue->elements >> 1; i > 0; i--)
369
A test program for the priority queue implementation.
370
It can also be used to benchmark changes of the implementation
371
Build by doing the following in the directory mysys
372
make test_priority_queue
373
./test_priority_queue
375
Written by Mikael Ronström, 2005
378
static uint32_t num_array[1025];
379
static uint32_t tot_no_parts= 0;
380
static uint32_t tot_no_loops= 0;
381
static uint32_t expected_part= 0;
382
static uint32_t expected_num= 0;
383
static bool max_ind= 0;
384
static bool fix_used= 0;
385
static uint64_t start_time= 0;
387
static bool is_divisible_by(uint32_t num, uint32_t divisor)
389
uint32_t quotient= num / divisor;
390
if (quotient * divisor == num)
395
void calculate_next()
397
uint32_t part= expected_part, num= expected_num;
398
uint32_t no_parts= tot_no_parts;
403
while (++part <= no_parts)
405
if (is_divisible_by(num, part) &&
406
(num <= ((1 << 21) + part)))
422
if (is_divisible_by(num, part))
434
void calculate_end_next(uint32_t part)
436
uint32_t no_parts= tot_no_parts, num;
441
for (part= no_parts; part > 0 ; part--)
445
num= num_array[part] & 0x3FFFFF;
446
if (num >= expected_num)
453
if (expected_num == 0)
458
expected_num= 0xFFFFFFFF;
459
for (part= 1; part <= no_parts; part++)
463
num= num_array[part] & 0x3FFFFF;
464
if (num <= expected_num)
471
if (expected_num == 0xFFFFFFFF)
476
static int test_compare(void *null_arg, unsigned char *a, unsigned char *b)
478
uint32_t a_num= (*(uint*)a) & 0x3FFFFF;
479
uint32_t b_num= (*(uint*)b) & 0x3FFFFF;
480
uint32_t a_part, b_part;
485
a_part= (*(uint*)a) >> 22;
486
b_part= (*(uint*)b) >> 22;
494
bool check_num(uint32_t num_part)
496
uint32_t part= num_part >> 22;
497
uint32_t num= num_part & 0x3FFFFF;
498
if (part == expected_part)
499
if (num == expected_num)
501
printf("Expect part %u Expect num 0x%x got part %u num 0x%x max_ind %u fix_used %u \n",
502
expected_part, expected_num, part, num, max_ind, fix_used);
507
void perform_insert(QUEUE *queue)
509
uint32_t i= 1, no_parts= tot_no_parts;
510
uint32_t backward_start= 0;
516
backward_start= 1 << 21;
520
uint32_t num= (i + backward_start);
523
while (!is_divisible_by(num, i))
525
if (max_ind && (num > expected_num ||
526
(num == expected_num && i < expected_part)))
532
num_array[i]= num + (i << 22);
534
queue_element(queue, i-1)= (unsigned char*)&num_array[i];
536
queue_insert(queue, (unsigned char*)&num_array[i]);
537
} while (++i <= no_parts);
540
queue->elements= no_parts;
545
bool perform_ins_del(QUEUE *queue, bool max_ind)
547
uint32_t i= 0, no_loops= tot_no_loops, j= tot_no_parts;
550
uint32_t num_part= *(uint*)queue_top(queue);
551
uint32_t part= num_part >> 22;
552
if (check_num(num_part))
556
calculate_end_next(part);
557
queue_remove(queue, (uint32_t) 0);
563
num_array[part]-= part;
565
num_array[part]+= part;
566
queue_top(queue)= (unsigned char*)&num_array[part];
567
queue_replaced(queue);
569
} while (++i < no_loops);
573
bool do_test(uint32_t no_parts, uint32_t l_max_ind, bool l_fix_used)
578
fix_used= l_fix_used;
579
init_queue(&queue, no_parts, 0, max_ind, test_compare, NULL);
580
tot_no_parts= no_parts;
582
perform_insert(&queue);
583
if ((result= perform_ins_del(&queue, max_ind)))
584
delete_queue(&queue);
593
static void start_measurement()
595
start_time= my_getsystime();
598
static void stop_measurement()
600
uint64_t stop_time= my_getsystime();
601
uint32_t time_in_micros;
602
stop_time-= start_time;
603
stop_time/= 10; /* Convert to microseconds */
604
time_in_micros= (uint32_t)stop_time;
605
printf("Time expired is %u microseconds \n", time_in_micros);
608
static void benchmark_test()
611
QUEUE *queue= &queue_real;
616
init_queue(queue, tot_no_parts, 0, max_ind, test_compare, NULL);
618
First benchmark whether queue_fix is faster than using queue_insert
619
for sizes of 16 partitions.
621
for (tot_no_parts= 2, add=2; tot_no_parts < 128;
622
tot_no_parts+= add, add++)
624
printf("Start benchmark queue_fix, tot_no_parts= %u \n", tot_no_parts);
626
for (i= 0; i < 128; i++)
628
perform_insert(queue);
629
queue_remove_all(queue);
634
printf("Start benchmark queue_insert\n");
636
for (i= 0; i < 128; i++)
638
perform_insert(queue);
639
queue_remove_all(queue);
644
Now benchmark insertion and deletion of 16400 elements.
645
Used in consecutive runs this shows whether the optimised _downheap
646
is faster than the standard implementation.
648
printf("Start benchmarking _downheap \n");
650
perform_insert(queue);
651
for (i= 0; i < 65536; i++)
654
num= *(uint*)queue_top(queue);
657
num_array[part]= num;
658
queue_top(queue)= (unsigned char*)&num_array[part];
659
queue_replaced(queue);
661
for (i= 0; i < 16; i++)
662
queue_remove(queue, (uint32_t) 0);
663
queue_remove_all(queue);
670
for (i= 1; i < 1024; i+=add, add++)
672
printf("Start test for priority queue of size %u\n", i);
673
if (do_test(i, 0, 1))
675
if (do_test(i, 1, 1))
677
if (do_test(i, 0, 0))
679
if (do_test(i, 1, 0))