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