radixsort.c revision 318152
1/*- 2 * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com> 3 * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/10/usr.bin/sort/radixsort.c 318152 2017-05-10 20:29:01Z marius $"); 30 31#include <errno.h> 32#include <err.h> 33#include <langinfo.h> 34#include <math.h> 35#if defined(SORT_THREADS) 36#include <pthread.h> 37#include <semaphore.h> 38#endif 39#include <stdlib.h> 40#include <string.h> 41#include <wchar.h> 42#include <wctype.h> 43#include <unistd.h> 44 45#include "coll.h" 46#include "radixsort.h" 47 48#define DEFAULT_SORT_FUNC_RADIXSORT mergesort 49 50#define TINY_NODE(sl) ((sl)->tosort_num < 65) 51#define SMALL_NODE(sl) ((sl)->tosort_num < 5) 52 53/* are we sorting in reverse order ? */ 54static bool reverse_sort; 55 56/* sort sub-levels array size */ 57static const size_t slsz = 256 * sizeof(struct sort_level*); 58 59/* one sort level structure */ 60struct sort_level 61{ 62 struct sort_level **sublevels; 63 struct sort_list_item **leaves; 64 struct sort_list_item **sorted; 65 struct sort_list_item **tosort; 66 size_t leaves_num; 67 size_t leaves_sz; 68 size_t level; 69 size_t real_sln; 70 size_t start_position; 71 size_t sln; 72 size_t tosort_num; 73 size_t tosort_sz; 74}; 75 76/* stack of sort levels ready to be sorted */ 77struct level_stack { 78 struct level_stack *next; 79 struct sort_level *sl; 80}; 81 82static struct level_stack *g_ls; 83 84#if defined(SORT_THREADS) 85/* stack guarding mutex */ 86static pthread_cond_t g_ls_cond; 87static pthread_mutex_t g_ls_mutex; 88 89/* counter: how many items are left */ 90static size_t sort_left; 91/* guarding mutex */ 92 93/* semaphore to count threads */ 94static sem_t mtsem; 95 96/* 97 * Decrement items counter 98 */ 99static inline void 100sort_left_dec(size_t n) 101{ 102 pthread_mutex_lock(&g_ls_mutex); 103 sort_left -= n; 104 if (sort_left == 0 && nthreads > 1) 105 pthread_cond_broadcast(&g_ls_cond); 106 pthread_mutex_unlock(&g_ls_mutex); 107} 108 109/* 110 * Do we have something to sort ? 111 * 112 * This routine does not need to be locked. 113 */ 114static inline bool 115have_sort_left(void) 116{ 117 bool ret; 118 119 ret = (sort_left > 0); 120 121 return (ret); 122} 123 124#else 125 126#define sort_left_dec(n) 127 128#endif /* SORT_THREADS */ 129 130/* 131 * Push sort level to the stack 132 */ 133static inline void 134push_ls(struct sort_level* sl) 135{ 136 struct level_stack *new_ls; 137 138 new_ls = sort_malloc(sizeof(struct level_stack)); 139 new_ls->sl = sl; 140 141#if defined(SORT_THREADS) 142 if (nthreads > 1) 143 pthread_mutex_lock(&g_ls_mutex); 144#endif 145 146 new_ls->next = g_ls; 147 g_ls = new_ls; 148 149#if defined(SORT_THREADS) 150 if (nthreads > 1) 151 pthread_cond_signal(&g_ls_cond); 152#endif 153 154#if defined(SORT_THREADS) 155 if (nthreads > 1) 156 pthread_mutex_unlock(&g_ls_mutex); 157#endif 158} 159 160/* 161 * Pop sort level from the stack (single-threaded style) 162 */ 163static inline struct sort_level* 164pop_ls_st(void) 165{ 166 struct sort_level *sl; 167 168 if (g_ls) { 169 struct level_stack *saved_ls; 170 171 sl = g_ls->sl; 172 saved_ls = g_ls; 173 g_ls = g_ls->next; 174 sort_free(saved_ls); 175 } else 176 sl = NULL; 177 178 return (sl); 179} 180 181#if defined(SORT_THREADS) 182 183/* 184 * Pop sort level from the stack (multi-threaded style) 185 */ 186static inline struct sort_level* 187pop_ls_mt(void) 188{ 189 struct level_stack *saved_ls; 190 struct sort_level *sl; 191 192 pthread_mutex_lock(&g_ls_mutex); 193 194 for (;;) { 195 if (g_ls) { 196 sl = g_ls->sl; 197 saved_ls = g_ls; 198 g_ls = g_ls->next; 199 break; 200 } 201 sl = NULL; 202 saved_ls = NULL; 203 204 if (have_sort_left() == 0) 205 break; 206 pthread_cond_wait(&g_ls_cond, &g_ls_mutex); 207 } 208 209 pthread_mutex_unlock(&g_ls_mutex); 210 211 sort_free(saved_ls); 212 213 return (sl); 214} 215 216#endif /* defined(SORT_THREADS) */ 217 218static void 219add_to_sublevel(struct sort_level *sl, struct sort_list_item *item, size_t indx) 220{ 221 struct sort_level *ssl; 222 223 ssl = sl->sublevels[indx]; 224 225 if (ssl == NULL) { 226 ssl = sort_malloc(sizeof(struct sort_level)); 227 memset(ssl, 0, sizeof(struct sort_level)); 228 229 ssl->level = sl->level + 1; 230 sl->sublevels[indx] = ssl; 231 232 ++(sl->real_sln); 233 } 234 235 if (++(ssl->tosort_num) > ssl->tosort_sz) { 236 ssl->tosort_sz = ssl->tosort_num + 128; 237 ssl->tosort = sort_realloc(ssl->tosort, 238 sizeof(struct sort_list_item*) * (ssl->tosort_sz)); 239 } 240 241 ssl->tosort[ssl->tosort_num - 1] = item; 242} 243 244static inline void 245add_leaf(struct sort_level *sl, struct sort_list_item *item) 246{ 247 248 if (++(sl->leaves_num) > sl->leaves_sz) { 249 sl->leaves_sz = sl->leaves_num + 128; 250 sl->leaves = sort_realloc(sl->leaves, 251 (sizeof(struct sort_list_item*) * (sl->leaves_sz))); 252 } 253 sl->leaves[sl->leaves_num - 1] = item; 254} 255 256static inline int 257get_wc_index(struct sort_list_item *sli, size_t level) 258{ 259 const struct key_value *kv; 260 const struct bwstring *bws; 261 262 kv = get_key_from_keys_array(&sli->ka, 0); 263 bws = kv->k; 264 265 if ((BWSLEN(bws) > level)) 266 return (unsigned char) BWS_GET(bws,level); 267 return (-1); 268} 269 270static void 271place_item(struct sort_level *sl, size_t item) 272{ 273 struct sort_list_item *sli; 274 int c; 275 276 sli = sl->tosort[item]; 277 c = get_wc_index(sli, sl->level); 278 279 if (c == -1) 280 add_leaf(sl, sli); 281 else 282 add_to_sublevel(sl, sli, c); 283} 284 285static void 286free_sort_level(struct sort_level *sl) 287{ 288 289 if (sl) { 290 if (sl->leaves) 291 sort_free(sl->leaves); 292 293 if (sl->level > 0) 294 sort_free(sl->tosort); 295 296 if (sl->sublevels) { 297 struct sort_level *slc; 298 size_t sln; 299 300 sln = sl->sln; 301 302 for (size_t i = 0; i < sln; ++i) { 303 slc = sl->sublevels[i]; 304 if (slc) 305 free_sort_level(slc); 306 } 307 308 sort_free(sl->sublevels); 309 } 310 311 sort_free(sl); 312 } 313} 314 315static void 316run_sort_level_next(struct sort_level *sl) 317{ 318 struct sort_level *slc; 319 size_t i, sln, tosort_num; 320 321 if (sl->sublevels) { 322 sort_free(sl->sublevels); 323 sl->sublevels = NULL; 324 } 325 326 switch (sl->tosort_num){ 327 case 0: 328 goto end; 329 case (1): 330 sl->sorted[sl->start_position] = sl->tosort[0]; 331 sort_left_dec(1); 332 goto end; 333 case (2): 334 if (list_coll_offset(&(sl->tosort[0]), &(sl->tosort[1]), 335 sl->level) > 0) { 336 sl->sorted[sl->start_position++] = sl->tosort[1]; 337 sl->sorted[sl->start_position] = sl->tosort[0]; 338 } else { 339 sl->sorted[sl->start_position++] = sl->tosort[0]; 340 sl->sorted[sl->start_position] = sl->tosort[1]; 341 } 342 sort_left_dec(2); 343 344 goto end; 345 default: 346 if (TINY_NODE(sl) || (sl->level > 15)) { 347 listcoll_t func; 348 349 func = get_list_call_func(sl->level); 350 351 sl->leaves = sl->tosort; 352 sl->leaves_num = sl->tosort_num; 353 sl->leaves_sz = sl->leaves_num; 354 sl->leaves = sort_realloc(sl->leaves, 355 (sizeof(struct sort_list_item *) * 356 (sl->leaves_sz))); 357 sl->tosort = NULL; 358 sl->tosort_num = 0; 359 sl->tosort_sz = 0; 360 sl->sln = 0; 361 sl->real_sln = 0; 362 if (sort_opts_vals.sflag) { 363 if (mergesort(sl->leaves, sl->leaves_num, 364 sizeof(struct sort_list_item *), 365 (int(*)(const void *, const void *)) func) == -1) 366 /* NOTREACHED */ 367 err(2, "Radix sort error 3"); 368 } else 369 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 370 sizeof(struct sort_list_item *), 371 (int(*)(const void *, const void *)) func); 372 373 memcpy(sl->sorted + sl->start_position, 374 sl->leaves, sl->leaves_num * 375 sizeof(struct sort_list_item*)); 376 377 sort_left_dec(sl->leaves_num); 378 379 goto end; 380 } else { 381 sl->tosort_sz = sl->tosort_num; 382 sl->tosort = sort_realloc(sl->tosort, 383 sizeof(struct sort_list_item*) * (sl->tosort_sz)); 384 } 385 } 386 387 sl->sln = 256; 388 sl->sublevels = sort_malloc(slsz); 389 memset(sl->sublevels, 0, slsz); 390 391 sl->real_sln = 0; 392 393 tosort_num = sl->tosort_num; 394 for (i = 0; i < tosort_num; ++i) 395 place_item(sl, i); 396 397 sort_free(sl->tosort); 398 sl->tosort = NULL; 399 sl->tosort_num = 0; 400 sl->tosort_sz = 0; 401 402 if (sl->leaves_num > 1) { 403 if (keys_num > 1) { 404 if (sort_opts_vals.sflag) { 405 mergesort(sl->leaves, sl->leaves_num, 406 sizeof(struct sort_list_item *), 407 (int(*)(const void *, const void *)) list_coll); 408 } else { 409 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 410 sizeof(struct sort_list_item *), 411 (int(*)(const void *, const void *)) list_coll); 412 } 413 } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) { 414 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 415 sizeof(struct sort_list_item *), 416 (int(*)(const void *, const void *)) list_coll_by_str_only); 417 } 418 } 419 420 sl->leaves_sz = sl->leaves_num; 421 sl->leaves = sort_realloc(sl->leaves, (sizeof(struct sort_list_item *) * 422 (sl->leaves_sz))); 423 424 if (!reverse_sort) { 425 memcpy(sl->sorted + sl->start_position, sl->leaves, 426 sl->leaves_num * sizeof(struct sort_list_item*)); 427 sl->start_position += sl->leaves_num; 428 sort_left_dec(sl->leaves_num); 429 430 sort_free(sl->leaves); 431 sl->leaves = NULL; 432 sl->leaves_num = 0; 433 sl->leaves_sz = 0; 434 435 sln = sl->sln; 436 437 for (i = 0; i < sln; ++i) { 438 slc = sl->sublevels[i]; 439 440 if (slc) { 441 slc->sorted = sl->sorted; 442 slc->start_position = sl->start_position; 443 sl->start_position += slc->tosort_num; 444 if (SMALL_NODE(slc)) 445 run_sort_level_next(slc); 446 else 447 push_ls(slc); 448 sl->sublevels[i] = NULL; 449 } 450 } 451 452 } else { 453 size_t n; 454 455 sln = sl->sln; 456 457 for (i = 0; i < sln; ++i) { 458 n = sln - i - 1; 459 slc = sl->sublevels[n]; 460 461 if (slc) { 462 slc->sorted = sl->sorted; 463 slc->start_position = sl->start_position; 464 sl->start_position += slc->tosort_num; 465 if (SMALL_NODE(slc)) 466 run_sort_level_next(slc); 467 else 468 push_ls(slc); 469 sl->sublevels[n] = NULL; 470 } 471 } 472 473 memcpy(sl->sorted + sl->start_position, sl->leaves, 474 sl->leaves_num * sizeof(struct sort_list_item*)); 475 sort_left_dec(sl->leaves_num); 476 } 477 478end: 479 free_sort_level(sl); 480} 481 482/* 483 * Single-threaded sort cycle 484 */ 485static void 486run_sort_cycle_st(void) 487{ 488 struct sort_level *slc; 489 490 for (;;) { 491 slc = pop_ls_st(); 492 if (slc == NULL) { 493 break; 494 } 495 run_sort_level_next(slc); 496 } 497} 498 499#if defined(SORT_THREADS) 500 501/* 502 * Multi-threaded sort cycle 503 */ 504static void 505run_sort_cycle_mt(void) 506{ 507 struct sort_level *slc; 508 509 for (;;) { 510 slc = pop_ls_mt(); 511 if (slc == NULL) 512 break; 513 run_sort_level_next(slc); 514 } 515} 516 517/* 518 * Sort cycle thread (in multi-threaded mode) 519 */ 520static void* 521sort_thread(void* arg) 522{ 523 run_sort_cycle_mt(); 524 sem_post(&mtsem); 525 526 return (arg); 527} 528 529#endif /* defined(SORT_THREADS) */ 530 531static void 532run_top_sort_level(struct sort_level *sl) 533{ 534 struct sort_level *slc; 535 536 reverse_sort = sort_opts_vals.kflag ? keys[0].sm.rflag : 537 default_sort_mods->rflag; 538 539 sl->start_position = 0; 540 sl->sln = 256; 541 sl->sublevels = sort_malloc(slsz); 542 memset(sl->sublevels, 0, slsz); 543 544 for (size_t i = 0; i < sl->tosort_num; ++i) 545 place_item(sl, i); 546 547 if (sl->leaves_num > 1) { 548 if (keys_num > 1) { 549 if (sort_opts_vals.sflag) { 550 mergesort(sl->leaves, sl->leaves_num, 551 sizeof(struct sort_list_item *), 552 (int(*)(const void *, const void *)) list_coll); 553 } else { 554 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 555 sizeof(struct sort_list_item *), 556 (int(*)(const void *, const void *)) list_coll); 557 } 558 } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) { 559 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 560 sizeof(struct sort_list_item *), 561 (int(*)(const void *, const void *)) list_coll_by_str_only); 562 } 563 } 564 565 if (!reverse_sort) { 566 memcpy(sl->tosort + sl->start_position, sl->leaves, 567 sl->leaves_num * sizeof(struct sort_list_item*)); 568 sl->start_position += sl->leaves_num; 569 sort_left_dec(sl->leaves_num); 570 571 for (size_t i = 0; i < sl->sln; ++i) { 572 slc = sl->sublevels[i]; 573 574 if (slc) { 575 slc->sorted = sl->tosort; 576 slc->start_position = sl->start_position; 577 sl->start_position += slc->tosort_num; 578 push_ls(slc); 579 sl->sublevels[i] = NULL; 580 } 581 } 582 583 } else { 584 size_t n; 585 586 for (size_t i = 0; i < sl->sln; ++i) { 587 588 n = sl->sln - i - 1; 589 slc = sl->sublevels[n]; 590 591 if (slc) { 592 slc->sorted = sl->tosort; 593 slc->start_position = sl->start_position; 594 sl->start_position += slc->tosort_num; 595 push_ls(slc); 596 sl->sublevels[n] = NULL; 597 } 598 } 599 600 memcpy(sl->tosort + sl->start_position, sl->leaves, 601 sl->leaves_num * sizeof(struct sort_list_item*)); 602 603 sort_left_dec(sl->leaves_num); 604 } 605 606#if defined(SORT_THREADS) 607 if (nthreads < 2) { 608#endif 609 run_sort_cycle_st(); 610#if defined(SORT_THREADS) 611 } else { 612 size_t i; 613 614 for(i = 0; i < nthreads; ++i) { 615 pthread_attr_t attr; 616 pthread_t pth; 617 618 pthread_attr_init(&attr); 619 pthread_attr_setdetachstate(&attr, PTHREAD_DETACHED); 620 621 for (;;) { 622 int res = pthread_create(&pth, &attr, 623 sort_thread, NULL); 624 if (res >= 0) 625 break; 626 if (errno == EAGAIN) { 627 pthread_yield(); 628 continue; 629 } 630 err(2, NULL); 631 } 632 633 pthread_attr_destroy(&attr); 634 } 635 636 for (i = 0; i < nthreads; ++i) 637 sem_wait(&mtsem); 638 } 639#endif /* defined(SORT_THREADS) */ 640} 641 642static void 643run_sort(struct sort_list_item **base, size_t nmemb) 644{ 645 struct sort_level *sl; 646 647#if defined(SORT_THREADS) 648 size_t nthreads_save = nthreads; 649 if (nmemb < MT_SORT_THRESHOLD) 650 nthreads = 1; 651 652 if (nthreads > 1) { 653 pthread_mutexattr_t mattr; 654 655 pthread_mutexattr_init(&mattr); 656 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP); 657 658 pthread_mutex_init(&g_ls_mutex, &mattr); 659 pthread_cond_init(&g_ls_cond, NULL); 660 661 pthread_mutexattr_destroy(&mattr); 662 663 sem_init(&mtsem, 0, 0); 664 665 } 666#endif 667 668 sl = sort_malloc(sizeof(struct sort_level)); 669 memset(sl, 0, sizeof(struct sort_level)); 670 671 sl->tosort = base; 672 sl->tosort_num = nmemb; 673 sl->tosort_sz = nmemb; 674 675#if defined(SORT_THREADS) 676 sort_left = nmemb; 677#endif 678 679 run_top_sort_level(sl); 680 681 free_sort_level(sl); 682 683#if defined(SORT_THREADS) 684 if (nthreads > 1) { 685 sem_destroy(&mtsem); 686 pthread_mutex_destroy(&g_ls_mutex); 687 } 688 nthreads = nthreads_save; 689#endif 690} 691 692void 693rxsort(struct sort_list_item **base, size_t nmemb) 694{ 695 696 run_sort(base, nmemb); 697} 698