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
bool max_at_top, int (*compare) (void *, uchar *, uchar *),
55
if ((queue->root= (uchar **) my_malloc((max_elements+1)*sizeof(void*),
59
queue->compare=compare;
60
queue->first_cmp_arg=first_cmp_arg;
61
queue->max_elements=max_elements;
62
queue->offset_to_key=offset_to_key;
63
queue_set_max_at_top(queue, max_at_top);
70
Init queue, uses init_queue internally for init work but also accepts
71
auto_extent as parameter
75
queue Queue to initialise
76
max_elements Max elements that will be put in queue
77
offset_to_key Offset to key in element stored in queue
78
Used when sending pointers to compare function
79
max_at_top Set to 1 if you want biggest element on top.
80
compare Compare function for elements, takes 3 arguments.
81
first_cmp_arg First argument to compare function
82
auto_extent When the queue is full and there is insert operation
86
Will allocate max_element pointers for queue array
90
1 Could not allocate memory
93
int init_queue_ex(QUEUE *queue, uint max_elements, uint offset_to_key,
94
bool max_at_top, int (*compare) (void *, uchar *, uchar *),
95
void *first_cmp_arg, uint auto_extent)
99
if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
103
queue->auto_extent= auto_extent;
108
Reinitialize queue for other usage
112
queue Queue to initialise
113
max_elements Max elements that will be put in queue
114
offset_to_key Offset to key in element stored in queue
115
Used when sending pointers to compare function
116
max_at_top Set to 1 if you want biggest element on top.
117
compare Compare function for elements, takes 3 arguments.
118
first_cmp_arg First argument to compare function
121
This will delete all elements from the queue. If you don't want this,
122
use resize_queue() instead.
126
EE_OUTOFMEMORY Wrong max_elements
129
int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
130
bool max_at_top, int (*compare) (void *, uchar *, uchar *),
134
queue->compare=compare;
135
queue->first_cmp_arg=first_cmp_arg;
136
queue->offset_to_key=offset_to_key;
137
queue_set_max_at_top(queue, max_at_top);
138
resize_queue(queue, max_elements);
149
max_elements New max size for queue
152
If you resize queue to be less than the elements you have in it,
153
the extra elements will be deleted
157
1 Error. In this case the queue is unchanged
160
int resize_queue(QUEUE *queue, uint max_elements)
163
if (queue->max_elements == max_elements)
165
if ((new_root= (uchar **) my_realloc((void *)queue->root,
166
(max_elements+1)*sizeof(void*),
169
set_if_smaller(queue->elements, max_elements);
170
queue->max_elements= max_elements;
171
queue->root= new_root;
181
queue Queue to delete
184
Just free allocated memory.
187
Can be called safely multiple times
190
void delete_queue(QUEUE *queue)
194
my_free((uchar*) queue->root,MYF(0));
201
/* Code for insert, search and delete of elements */
203
void queue_insert(register QUEUE *queue, uchar *element)
205
register uint idx, next;
206
assert(queue->elements < queue->max_elements);
207
queue->root[0]= element;
208
idx= ++queue->elements;
209
/* max_at_top swaps the comparison if we want to order by desc */
210
while ((queue->compare(queue->first_cmp_arg,
211
element + queue->offset_to_key,
212
queue->root[(next= idx >> 1)] +
213
queue->offset_to_key) * queue->max_at_top) < 0)
215
queue->root[idx]= queue->root[next];
218
queue->root[idx]= element;
222
Does safe insert. If no more space left on the queue resize it.
225
1 - Cannot allocate more memory
226
2 - auto_extend is 0, the operation would
230
int queue_insert_safe(register QUEUE *queue, uchar *element)
233
if (queue->elements == queue->max_elements)
235
if (!queue->auto_extent)
237
else if (resize_queue(queue, queue->max_elements + queue->auto_extent))
241
queue_insert(queue, element);
246
/* Remove item from queue */
247
/* Returns pointer to removed element */
249
uchar *queue_remove(register QUEUE *queue, uint idx)
252
assert(idx < queue->max_elements);
253
element= queue->root[++idx]; /* Intern index starts from 1 */
254
queue->root[idx]= queue->root[queue->elements--];
255
_downheap(queue, idx);
259
/* Fix when element on top has been replaced */
261
#ifndef queue_replaced
262
void queue_replaced(QUEUE *queue)
270
void _downheap(register QUEUE *queue, uint idx)
273
uint elements,half_queue,offset_to_key, next_index;
277
offset_to_key=queue->offset_to_key;
278
element=queue->root[idx];
279
half_queue=(elements=queue->elements) >> 1;
281
while (idx <= half_queue)
284
if (next_index < elements &&
285
(queue->compare(queue->first_cmp_arg,
286
queue->root[next_index]+offset_to_key,
287
queue->root[next_index+1]+offset_to_key) *
288
queue->max_at_top) > 0)
291
(((queue->compare(queue->first_cmp_arg,
292
queue->root[next_index]+offset_to_key,
293
element+offset_to_key) * queue->max_at_top) >= 0)))
295
queue->root[idx]= element;
298
queue->root[idx]=queue->root[next_index];
303
next_index= idx >> 1;
304
while (next_index > start_idx)
306
if ((queue->compare(queue->first_cmp_arg,
307
queue->root[next_index]+offset_to_key,
308
element+offset_to_key) *
309
queue->max_at_top) < 0)
311
queue->root[idx]=queue->root[next_index];
313
next_index= idx >> 1;
315
queue->root[idx]=element;
320
The old _downheap version is kept for comparisons with the benchmark
321
suit or new benchmarks anyone wants to run for comparisons.
323
/* Fix heap when index have changed */
324
void _downheap(register QUEUE *queue, uint idx)
327
uint elements,half_queue,next_index,offset_to_key;
329
offset_to_key=queue->offset_to_key;
330
element=queue->root[idx];
331
half_queue=(elements=queue->elements) >> 1;
333
while (idx <= half_queue)
336
if (next_index < elements &&
337
(queue->compare(queue->first_cmp_arg,
338
queue->root[next_index]+offset_to_key,
339
queue->root[next_index+1]+offset_to_key) *
340
queue->max_at_top) > 0)
342
if ((queue->compare(queue->first_cmp_arg,
343
queue->root[next_index]+offset_to_key,
344
element+offset_to_key) * queue->max_at_top) >= 0)
346
queue->root[idx]=queue->root[next_index];
349
queue->root[idx]=element;
356
Fix heap when every element was changed.
359
void queue_fix(QUEUE *queue)
362
for (i= queue->elements >> 1; i > 0; i--)
368
A test program for the priority queue implementation.
369
It can also be used to benchmark changes of the implementation
370
Build by doing the following in the directory mysys
371
make test_priority_queue
372
./test_priority_queue
374
Written by Mikael Ronström, 2005
377
static uint num_array[1025];
378
static uint tot_no_parts= 0;
379
static uint tot_no_loops= 0;
380
static uint expected_part= 0;
381
static uint expected_num= 0;
382
static bool max_ind= 0;
383
static bool fix_used= 0;
384
static uint64_t start_time= 0;
386
static bool is_divisible_by(uint num, uint divisor)
388
uint quotient= num / divisor;
389
if (quotient * divisor == num)
394
void calculate_next()
396
uint part= expected_part, num= expected_num;
397
uint no_parts= tot_no_parts;
402
while (++part <= no_parts)
404
if (is_divisible_by(num, part) &&
405
(num <= ((1 << 21) + part)))
421
if (is_divisible_by(num, part))
433
void calculate_end_next(uint part)
435
uint no_parts= tot_no_parts, num;
440
for (part= no_parts; part > 0 ; part--)
444
num= num_array[part] & 0x3FFFFF;
445
if (num >= expected_num)
452
if (expected_num == 0)
457
expected_num= 0xFFFFFFFF;
458
for (part= 1; part <= no_parts; part++)
462
num= num_array[part] & 0x3FFFFF;
463
if (num <= expected_num)
470
if (expected_num == 0xFFFFFFFF)
475
static int test_compare(void *null_arg, uchar *a, uchar *b)
477
uint a_num= (*(uint*)a) & 0x3FFFFF;
478
uint b_num= (*(uint*)b) & 0x3FFFFF;
484
a_part= (*(uint*)a) >> 22;
485
b_part= (*(uint*)b) >> 22;
493
bool check_num(uint num_part)
495
uint part= num_part >> 22;
496
uint num= num_part & 0x3FFFFF;
497
if (part == expected_part)
498
if (num == expected_num)
500
printf("Expect part %u Expect num 0x%x got part %u num 0x%x max_ind %u fix_used %u \n",
501
expected_part, expected_num, part, num, max_ind, fix_used);
506
void perform_insert(QUEUE *queue)
508
uint i= 1, no_parts= tot_no_parts;
509
uint backward_start= 0;
515
backward_start= 1 << 21;
519
uint num= (i + backward_start);
522
while (!is_divisible_by(num, i))
524
if (max_ind && (num > expected_num ||
525
(num == expected_num && i < expected_part)))
531
num_array[i]= num + (i << 22);
533
queue_element(queue, i-1)= (uchar*)&num_array[i];
535
queue_insert(queue, (uchar*)&num_array[i]);
536
} while (++i <= no_parts);
539
queue->elements= no_parts;
544
bool perform_ins_del(QUEUE *queue, bool max_ind)
546
uint i= 0, no_loops= tot_no_loops, j= tot_no_parts;
549
uint num_part= *(uint*)queue_top(queue);
550
uint part= num_part >> 22;
551
if (check_num(num_part))
555
calculate_end_next(part);
556
queue_remove(queue, (uint) 0);
562
num_array[part]-= part;
564
num_array[part]+= part;
565
queue_top(queue)= (uchar*)&num_array[part];
566
queue_replaced(queue);
568
} while (++i < no_loops);
572
bool do_test(uint no_parts, uint l_max_ind, bool l_fix_used)
577
fix_used= l_fix_used;
578
init_queue(&queue, no_parts, 0, max_ind, test_compare, NULL);
579
tot_no_parts= no_parts;
581
perform_insert(&queue);
582
if ((result= perform_ins_del(&queue, max_ind)))
583
delete_queue(&queue);
592
static void start_measurement()
594
start_time= my_getsystime();
597
static void stop_measurement()
599
uint64_t stop_time= my_getsystime();
601
stop_time-= start_time;
602
stop_time/= 10; /* Convert to microseconds */
603
time_in_micros= (uint)stop_time;
604
printf("Time expired is %u microseconds \n", time_in_micros);
607
static void benchmark_test()
610
QUEUE *queue= &queue_real;
615
init_queue(queue, tot_no_parts, 0, max_ind, test_compare, NULL);
617
First benchmark whether queue_fix is faster than using queue_insert
618
for sizes of 16 partitions.
620
for (tot_no_parts= 2, add=2; tot_no_parts < 128;
621
tot_no_parts+= add, add++)
623
printf("Start benchmark queue_fix, tot_no_parts= %u \n", tot_no_parts);
625
for (i= 0; i < 128; i++)
627
perform_insert(queue);
628
queue_remove_all(queue);
633
printf("Start benchmark queue_insert\n");
635
for (i= 0; i < 128; i++)
637
perform_insert(queue);
638
queue_remove_all(queue);
643
Now benchmark insertion and deletion of 16400 elements.
644
Used in consecutive runs this shows whether the optimised _downheap
645
is faster than the standard implementation.
647
printf("Start benchmarking _downheap \n");
649
perform_insert(queue);
650
for (i= 0; i < 65536; i++)
653
num= *(uint*)queue_top(queue);
656
num_array[part]= num;
657
queue_top(queue)= (uchar*)&num_array[part];
658
queue_replaced(queue);
660
for (i= 0; i < 16; i++)
661
queue_remove(queue, (uint) 0);
662
queue_remove_all(queue);
669
for (i= 1; i < 1024; i+=add, add++)
671
printf("Start test for priority queue of size %u\n", i);
672
if (do_test(i, 0, 1))
674
if (do_test(i, 1, 1))
676
if (do_test(i, 0, 0))
678
if (do_test(i, 1, 0))