radixsort.c revision 313321
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 313321 2017-02-06 05:27:05Z pfg $"); 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 bwstring *bws; 260 261 bws = sli->ka.key[0].k; 262 263 if ((BWSLEN(bws) > level)) 264 return (unsigned char) BWS_GET(bws,level); 265 return (-1); 266} 267 268static void 269place_item(struct sort_level *sl, size_t item) 270{ 271 struct sort_list_item *sli; 272 int c; 273 274 sli = sl->tosort[item]; 275 c = get_wc_index(sli, sl->level); 276 277 if (c == -1) 278 add_leaf(sl, sli); 279 else 280 add_to_sublevel(sl, sli, c); 281} 282 283static void 284free_sort_level(struct sort_level *sl) 285{ 286 287 if (sl) { 288 if (sl->leaves) 289 sort_free(sl->leaves); 290 291 if (sl->level > 0) 292 sort_free(sl->tosort); 293 294 if (sl->sublevels) { 295 struct sort_level *slc; 296 size_t sln; 297 298 sln = sl->sln; 299 300 for (size_t i = 0; i < sln; ++i) { 301 slc = sl->sublevels[i]; 302 if (slc) 303 free_sort_level(slc); 304 } 305 306 sort_free(sl->sublevels); 307 } 308 309 sort_free(sl); 310 } 311} 312 313static void 314run_sort_level_next(struct sort_level *sl) 315{ 316 struct sort_level *slc; 317 size_t i, sln, tosort_num; 318 319 if (sl->sublevels) { 320 sort_free(sl->sublevels); 321 sl->sublevels = NULL; 322 } 323 324 switch (sl->tosort_num){ 325 case 0: 326 goto end; 327 case (1): 328 sl->sorted[sl->start_position] = sl->tosort[0]; 329 sort_left_dec(1); 330 goto end; 331 case (2): 332 if (list_coll_offset(&(sl->tosort[0]), &(sl->tosort[1]), 333 sl->level) > 0) { 334 sl->sorted[sl->start_position++] = sl->tosort[1]; 335 sl->sorted[sl->start_position] = sl->tosort[0]; 336 } else { 337 sl->sorted[sl->start_position++] = sl->tosort[0]; 338 sl->sorted[sl->start_position] = sl->tosort[1]; 339 } 340 sort_left_dec(2); 341 342 goto end; 343 default: 344 if (TINY_NODE(sl) || (sl->level > 15)) { 345 listcoll_t func; 346 347 func = get_list_call_func(sl->level); 348 349 sl->leaves = sl->tosort; 350 sl->leaves_num = sl->tosort_num; 351 sl->leaves_sz = sl->leaves_num; 352 sl->leaves = sort_realloc(sl->leaves, 353 (sizeof(struct sort_list_item *) * 354 (sl->leaves_sz))); 355 sl->tosort = NULL; 356 sl->tosort_num = 0; 357 sl->tosort_sz = 0; 358 sl->sln = 0; 359 sl->real_sln = 0; 360 if (sort_opts_vals.sflag) { 361 if (mergesort(sl->leaves, sl->leaves_num, 362 sizeof(struct sort_list_item *), 363 (int(*)(const void *, const void *)) func) == -1) 364 /* NOTREACHED */ 365 err(2, "Radix sort error 3"); 366 } else 367 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 368 sizeof(struct sort_list_item *), 369 (int(*)(const void *, const void *)) func); 370 371 memcpy(sl->sorted + sl->start_position, 372 sl->leaves, sl->leaves_num * 373 sizeof(struct sort_list_item*)); 374 375 sort_left_dec(sl->leaves_num); 376 377 goto end; 378 } else { 379 sl->tosort_sz = sl->tosort_num; 380 sl->tosort = sort_realloc(sl->tosort, 381 sizeof(struct sort_list_item*) * (sl->tosort_sz)); 382 } 383 } 384 385 sl->sln = 256; 386 sl->sublevels = sort_malloc(slsz); 387 memset(sl->sublevels, 0, slsz); 388 389 sl->real_sln = 0; 390 391 tosort_num = sl->tosort_num; 392 for (i = 0; i < tosort_num; ++i) 393 place_item(sl, i); 394 395 sort_free(sl->tosort); 396 sl->tosort = NULL; 397 sl->tosort_num = 0; 398 sl->tosort_sz = 0; 399 400 if (sl->leaves_num > 1) { 401 if (keys_num > 1) { 402 if (sort_opts_vals.sflag) { 403 mergesort(sl->leaves, sl->leaves_num, 404 sizeof(struct sort_list_item *), 405 (int(*)(const void *, const void *)) list_coll); 406 } else { 407 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 408 sizeof(struct sort_list_item *), 409 (int(*)(const void *, const void *)) list_coll); 410 } 411 } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) { 412 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 413 sizeof(struct sort_list_item *), 414 (int(*)(const void *, const void *)) list_coll_by_str_only); 415 } 416 } 417 418 sl->leaves_sz = sl->leaves_num; 419 sl->leaves = sort_realloc(sl->leaves, (sizeof(struct sort_list_item *) * 420 (sl->leaves_sz))); 421 422 if (!reverse_sort) { 423 memcpy(sl->sorted + sl->start_position, sl->leaves, 424 sl->leaves_num * sizeof(struct sort_list_item*)); 425 sl->start_position += sl->leaves_num; 426 sort_left_dec(sl->leaves_num); 427 428 sort_free(sl->leaves); 429 sl->leaves = NULL; 430 sl->leaves_num = 0; 431 sl->leaves_sz = 0; 432 433 sln = sl->sln; 434 435 for (i = 0; i < sln; ++i) { 436 slc = sl->sublevels[i]; 437 438 if (slc) { 439 slc->sorted = sl->sorted; 440 slc->start_position = sl->start_position; 441 sl->start_position += slc->tosort_num; 442 if (SMALL_NODE(slc)) 443 run_sort_level_next(slc); 444 else 445 push_ls(slc); 446 sl->sublevels[i] = NULL; 447 } 448 } 449 450 } else { 451 size_t n; 452 453 sln = sl->sln; 454 455 for (i = 0; i < sln; ++i) { 456 n = sln - i - 1; 457 slc = sl->sublevels[n]; 458 459 if (slc) { 460 slc->sorted = sl->sorted; 461 slc->start_position = sl->start_position; 462 sl->start_position += slc->tosort_num; 463 if (SMALL_NODE(slc)) 464 run_sort_level_next(slc); 465 else 466 push_ls(slc); 467 sl->sublevels[n] = NULL; 468 } 469 } 470 471 memcpy(sl->sorted + sl->start_position, sl->leaves, 472 sl->leaves_num * sizeof(struct sort_list_item*)); 473 sort_left_dec(sl->leaves_num); 474 } 475 476end: 477 free_sort_level(sl); 478} 479 480/* 481 * Single-threaded sort cycle 482 */ 483static void 484run_sort_cycle_st(void) 485{ 486 struct sort_level *slc; 487 488 for (;;) { 489 slc = pop_ls_st(); 490 if (slc == NULL) { 491 break; 492 } 493 run_sort_level_next(slc); 494 } 495} 496 497#if defined(SORT_THREADS) 498 499/* 500 * Multi-threaded sort cycle 501 */ 502static void 503run_sort_cycle_mt(void) 504{ 505 struct sort_level *slc; 506 507 for (;;) { 508 slc = pop_ls_mt(); 509 if (slc == NULL) 510 break; 511 run_sort_level_next(slc); 512 } 513} 514 515/* 516 * Sort cycle thread (in multi-threaded mode) 517 */ 518static void* 519sort_thread(void* arg) 520{ 521 run_sort_cycle_mt(); 522 sem_post(&mtsem); 523 524 return (arg); 525} 526 527#endif /* defined(SORT_THREADS) */ 528 529static void 530run_top_sort_level(struct sort_level *sl) 531{ 532 struct sort_level *slc; 533 534 reverse_sort = sort_opts_vals.kflag ? keys[0].sm.rflag : 535 default_sort_mods->rflag; 536 537 sl->start_position = 0; 538 sl->sln = 256; 539 sl->sublevels = sort_malloc(slsz); 540 memset(sl->sublevels, 0, slsz); 541 542 for (size_t i = 0; i < sl->tosort_num; ++i) 543 place_item(sl, i); 544 545 if (sl->leaves_num > 1) { 546 if (keys_num > 1) { 547 if (sort_opts_vals.sflag) { 548 mergesort(sl->leaves, sl->leaves_num, 549 sizeof(struct sort_list_item *), 550 (int(*)(const void *, const void *)) list_coll); 551 } else { 552 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 553 sizeof(struct sort_list_item *), 554 (int(*)(const void *, const void *)) list_coll); 555 } 556 } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) { 557 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num, 558 sizeof(struct sort_list_item *), 559 (int(*)(const void *, const void *)) list_coll_by_str_only); 560 } 561 } 562 563 if (!reverse_sort) { 564 memcpy(sl->tosort + sl->start_position, sl->leaves, 565 sl->leaves_num * sizeof(struct sort_list_item*)); 566 sl->start_position += sl->leaves_num; 567 sort_left_dec(sl->leaves_num); 568 569 for (size_t i = 0; i < sl->sln; ++i) { 570 slc = sl->sublevels[i]; 571 572 if (slc) { 573 slc->sorted = sl->tosort; 574 slc->start_position = sl->start_position; 575 sl->start_position += slc->tosort_num; 576 push_ls(slc); 577 sl->sublevels[i] = NULL; 578 } 579 } 580 581 } else { 582 size_t n; 583 584 for (size_t i = 0; i < sl->sln; ++i) { 585 586 n = sl->sln - i - 1; 587 slc = sl->sublevels[n]; 588 589 if (slc) { 590 slc->sorted = sl->tosort; 591 slc->start_position = sl->start_position; 592 sl->start_position += slc->tosort_num; 593 push_ls(slc); 594 sl->sublevels[n] = NULL; 595 } 596 } 597 598 memcpy(sl->tosort + sl->start_position, sl->leaves, 599 sl->leaves_num * sizeof(struct sort_list_item*)); 600 601 sort_left_dec(sl->leaves_num); 602 } 603 604#if defined(SORT_THREADS) 605 if (nthreads < 2) { 606#endif 607 run_sort_cycle_st(); 608#if defined(SORT_THREADS) 609 } else { 610 size_t i; 611 612 for(i = 0; i < nthreads; ++i) { 613 pthread_attr_t attr; 614 pthread_t pth; 615 616 pthread_attr_init(&attr); 617 pthread_attr_setdetachstate(&attr, PTHREAD_DETACHED); 618 619 for (;;) { 620 int res = pthread_create(&pth, &attr, 621 sort_thread, NULL); 622 if (res >= 0) 623 break; 624 if (errno == EAGAIN) { 625 pthread_yield(); 626 continue; 627 } 628 err(2, NULL); 629 } 630 631 pthread_attr_destroy(&attr); 632 } 633 634 for (i = 0; i < nthreads; ++i) 635 sem_wait(&mtsem); 636 } 637#endif /* defined(SORT_THREADS) */ 638} 639 640static void 641run_sort(struct sort_list_item **base, size_t nmemb) 642{ 643 struct sort_level *sl; 644 645#if defined(SORT_THREADS) 646 size_t nthreads_save = nthreads; 647 if (nmemb < MT_SORT_THRESHOLD) 648 nthreads = 1; 649 650 if (nthreads > 1) { 651 pthread_mutexattr_t mattr; 652 653 pthread_mutexattr_init(&mattr); 654 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP); 655 656 pthread_mutex_init(&g_ls_mutex, &mattr); 657 pthread_cond_init(&g_ls_cond, NULL); 658 659 pthread_mutexattr_destroy(&mattr); 660 661 sem_init(&mtsem, 0, 0); 662 663 } 664#endif 665 666 sl = sort_malloc(sizeof(struct sort_level)); 667 memset(sl, 0, sizeof(struct sort_level)); 668 669 sl->tosort = base; 670 sl->tosort_num = nmemb; 671 sl->tosort_sz = nmemb; 672 673#if defined(SORT_THREADS) 674 sort_left = nmemb; 675#endif 676 677 run_top_sort_level(sl); 678 679 free_sort_level(sl); 680 681#if defined(SORT_THREADS) 682 if (nthreads > 1) { 683 sem_destroy(&mtsem); 684 pthread_mutex_destroy(&g_ls_mutex); 685 } 686 nthreads = nthreads_save; 687#endif 688} 689 690void 691rxsort(struct sort_list_item **base, size_t nmemb) 692{ 693 694 run_sort(base, nmemb); 695} 696