11
votes

Améliorer le tri rapide

Si possible, comment puis-je améliorer le tri rapide suivant (performance sage). Aucune suggestion? xxx


5 commentaires

Deux choses qui pourraient aider à obtenir de meilleures réponses: Dites-nous quel type de données vous êtes susceptible de rencontrer (principalement triés, surtout non formé, à peu près tout ...). Aussi (bien qu'une réponse générique) exécutent un profileur et découvre où le plus de temps est gaspillé (bien que honnêtement, je ne l'ai pas fait avec Code C, donc aucune idée de ce que de bons profileurs existent et qu'ils vont vraiment aider ici).


Considérer des nombres non formés dans ce cas.


Êtes-vous certain que la performance nécessite une amélioration? Avez-vous mesuré les performances avec un profileur et déterminé que cette fonction est un point chaud de performance?


Eh bien, avec tout le respect de tous, comment définissez-vous la performance? Dans les algorithmes, le CLRS dit compte des opérations de base, par exemple: un échange, SIPSER trouve une complexité selon la machine Turing, vous avez maintenant un programme et vous voulez rendre cela plus vite que je suppose. Ensuite, vous devez savoir ce que vous devriez vous améliorer. Choisir le pivot comme médiane de 5 médiane partitionnera mieux. Il a été prouvé que la médiane de 3 ou 7 n'est pas aussi bonne que la médiane de 5. SCI.BROOKLYNN.CUNY.EDU/~AMOTZ/700-FALL09/SEARCH.PDF


Jetez un coup d'œil à ce qui suit: Triting-algorithms.com/static/QuickSortisOptimal.pdf Quicksort est optimal. Sauf que d'utiliser un pivot de médiane de 5 médianes, le reste doit déjà être optimisé pour vous dans les niveaux inférieurs, par compilateur.


14 Réponses :


6
votes

2 commentaires

... un tas de idées génériques théoriques , mais malheureusement pratiquement aucune pratique pratique (ce qui est compréhensible, puisque l'article doit être de la mise en œuvre / de la langue indépendante).


En fait, les développeurs d'applications devraient mieux utiliser des fonctions de cadre pour le tri. Au début, vous avez roulé le vôtre, mais ces temps dépassent environ 99% de tous les cas d'utilisation. De plus, lorsque les cadres vont mieux et mieux, tous les utilisateurs de la fonction de tri dans le code de l'application bénéficient de la prestation. (Même chose pour le code C ++ avec STL au lieu de créer votre propre classe B-Tree, etc.)



19
votes
  1. Choisissez un meilleur pivot: par exemple. En médian-de-trois, vous choisissez 3 éléments (aléatoires) et choisissez le pivot comme élément médian
  2. lorsque la longueur (a [])

3 commentaires

Le commentaire du code source de GCC 'Code> Qsort stipule que «Médian-of-trois» ne produisent aucune amélioration réelle de la pratique :) J'ai tendance à être d'accord avec cela. Sauf si votre contribution n'est pas spécifique de manière à ce qu'elle soit meilleure avec la méthode de sélection "médiane de trois", c'est-à-dire ...


Je pense que la médiane de l'arbre n'est qu'une défense contre des tableaux de tri, beaucoup plus communs que le schéma requis pour vaincre la médiane-de-trois. En moyenne (chaînes classées aléatoires?) Il ne fournit aucun avantage.


En fait, il a été prouvé que, la médiane de 5 est meilleure que la médiane de 3.



17
votes

La première suggestion serait: remplacez l'un des appels récursifs avec itération. Et je veux dire une véritable itération, pas une pile mise en œuvre manuelle pour la récursivité. C'est à dire. au lieu de faire deux "nouveaux" appels vers rapides code> à partir de rapides code>, "recycler" votre courant actuel em> appeler rapide code> à Processus Une branche de la récursion et appelez rapide code> récursivement pour traiter une autre branche.

MAINTENANT, si vous vous assurez que vous effectuez toujours un appel réel récursif pour la partition shorter em> et utiliser l'itération plus longue), il garantira que la profondeur de la récursivité ne dépassera jamais log n code> même dans le pire des cas, c'est-à-dire quelle que soit la qualité de votre médiane. P> Tout ce tout est implémenté dans qsort code> algorithme fourni avec la bibliothèque standard de la GCC. Jetez un coup d'œil à la source, cela devrait être utile. P>

À peu près, une implémentation plus pratique qui suit la suggestion ci-dessus pourrait être la suivante: P>

void quick(int a[], int lower, int upper)
{
  while (lower < upper)
  {
    int loc = partition(a, lower, upper);
    if (loc - lower < upper - loc)
    { /* Lower part is shorter... */
      quick(a, lower, loc - 1); /* ...process it recursively... */
      lower = loc + 1; /* ...and process the upper part on the next iteration */
    }
    else
    { /* Upper part is shorter... */
      quick(a, loc + 1, upper); /* ...process it recursively... */
      upper = loc - 1; /* ...and process the lower part on the next iteration */
    }
  }
}


4 commentaires

Désolé mais je suis en désaccord avec vous, la conversion de la récursion en itération ne fournit aucun avantage du tout. GCC et la plupart des compilateurs optimisent la récursion à l'aide de la table des symboles et du cache aux niveaux inférieurs, programmation dynamique. L'utilisation de l'itération sur la récursivité n'a aucun avantage. Pardonne-moi pour ça; L'informatique est basée sur la théorie de la récursivité et les fonctions récursifs.


@known: absolument incorrect. Apparemment, vous avez manqué le point entièrement. Le point principal de cette modification n'est pas une performance (qui pourrait ne pas s'améliorer), mais la limite dure de la profondeur de pile. La mise en œuvre initiale avait une profondeur de récursivité O (n) dans le pire des cas. Ce qui signifiait que sur une mauvaise contribution, cela se bloquerait à cause du débordement de la pile. La mise en œuvre que j'ai suggérée, encore une fois, a une limite garantie de log2 (n) sur la profondeur de la récursivité. C'est à dire. Il est garanti, par exemple, que sur une plate-forme 32 bits, il n'y aura jamais plus de 32 niveaux de récursivité et que la pile ne débordera jamais.


@known: note que la garantie log2 (n) s'applique quelles que soient la gravité de l'entrée et de la manière dont la méthode de choix médiane est la méthode de choix médiane. De toute évidence, c'est un avantage très précieux et très pratique. De plus, c'est la chose la plus importante que chacun devrait penser d'abord. La performance vient seconde. Votre verdict à ce sujet «Fournir aucun avantage du tout» est au mieux risible. Quant à la référence informatique de la science - je n'ai aucune idée du point de cette remarque totalement non pertinente.


+1, mais si vous ne ciblez pas les compilateurs de qualité DS9K, il peut être plus lisible pour utiliser la récursion de la queue pour la sous-carrée la plus longue au lieu de l'itération et que le compilateur optimise la récursion de la queue.



10
votes

Tri de petits blocs ( http://wiki.tcl.tk/ 4118

L'idée peut être utilisée pour générer des algorithmes de tri sans boucle pour 2,3,4,5 éléments en c. P>

EDIT: Je l'ai essayé sur un ensemble de données, et je mesuré 87% de temps d'exécution par rapport au code de la question. Utiliser l'insertion Trier sur

EDIT: Cet exemple de code Ecrire des fonctions de tri en boucle pour 2-6 éléments: P>

sort2():    8
sort3():   24
sort4():   96
sort5():  512
sort6(): 3072


5 commentaires

+1 pour la taille pure de vos cojones. Aimer ceux qui sont imbriquées #defines.


Je serais intéressé de voir la sortie de l'assembleur du compilateur. Un tri de boucle peut être fait très rapidement en utilisant les instructions de déplacement conditionnel disponibles sur la plupart des processeurs.


@Goz: Voir le code généré, je pense qu'il ne peut pas être compilé avec des instructions de déplacement conditionnel


J'ai imprimé ceci et montré à mon ami (C # _is_the_thest). Il cherche toujours sa mâchoire. Merci Sambowry: D


@ BEERERMAN: Je suis aussi - le code pourrait bien être extrêmement intelligent, mais c'est en termes de lisibilité et de compréhension extrêmement terrible.



24
votes

L'algorithme QuicksTort est facilement parallélédisé. Si vous avez plusieurs noyaux pour travailler avec, vous pouvez voir un peu d'effectif. Selon la taille de votre ensemble de données, cela pourrait facilement vous fournir plus de vitesse supérieure à toute autre optimisation. Cependant, si vous n'avez qu'un seul processeur ou un ensemble de données relativement petit, il n'y aura pas une grande vitesse.

Je pourrais élaborer plus s'il s'agit d'une possibilité.


5 commentaires

+1 pour suggérer quelque chose qui peut réellement améliorer considérablement les performances. Super!


Une bonne réponse, mais je ne pense pas que ce que c'est la recherche. et la plupart des algorithmes de division et de conquérir des algorithmes + des algorithmes de programmation dynamique peuvent s'exécuter en parallèle.


Honnêtement, je suis un peu déçu de ne jamais revenir pour clarifier ce qu'il cherchait.


De nos jours, la réponse à tout est "parallèle" ou "cloud computing".


Je suis désolé mais mon compte a été suspendu pendant sept jours. Dans la période moyenne, je n'ai pas pu effectuer une seule activité sur mon compte.hence le retard de réponse. À cause de votre réponse, votre réponse a été sélectionnée comme "meilleure" réponse.



9
votes

Essayez un autre algorithme de tri.

Selon vos données, vous pourrez peut-être échanger la mémoire pour la vitesse. P>

Selon Wikipedia P>

  • Quick Trit a un meilleur cas O (n log n) fort > performance et O (1) strong> espace li>
  • Merge Sort a un O (N journal N) fort> performance et O (n) strong> espace li>
  • Tri radix a une O (n. Nombre de chiffres>) forte> performance et O (n. ) strong> espace li> ul>

    edit em> p>

    Apparemment, vos données sont des entiers. Avec des entiers de 2,5 m dans la gamme [0, 0x0FFFFFFFF], ma mise en œuvre de la tri radix est environ 4 fois plus rapide que votre implémentation de tri rapide. P>

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define ARRAY_SIZE 2560000
    #define RANDOM_NUMBER (((rand() << 16) + rand()) & 0x0fffffff)
    
    int partition(int a[], int lower, int upper) {
      int pivot, i, j, temp;
      pivot = a[lower];
      i = lower + 1;
      j = upper;
      while (i < j) {
        while((i < upper) && (a[i] <= pivot)) i++;
        while (a[j] > pivot) j--;
        if (i < j) {
          temp = a[i];
          a[i] = a[j];
          a[j] = temp;
        }
      }
      if (pivot > a[j]) {
        temp = a[j];
        a[j] = a[lower];
        a[lower] = temp;
      }
      return j;
    }
    
    void quick(int a[], int lower, int upper) {
      int loc;
      if (lower < upper) {
        loc = partition(a, lower, upper);
        quick(a, lower, loc-1);
        quick(a, loc+1, upper);
      }
    }
    
    #define NBUCKETS 256
    #define BUCKET_SIZE (48 * (1 + ARRAY_SIZE / NBUCKETS))
    
    /* "waste" memory */
    int bucket[NBUCKETS][BUCKET_SIZE];
    
    void radix(int *a, size_t siz) {
      unsigned shift = 0;
      for (int dummy=0; dummy<4; dummy++) {
        int bcount[NBUCKETS] = {0};
        int *aa = a;
        size_t s = siz;
        while (s--) {
          unsigned v = ((unsigned)*aa >> shift) & 0xff;
          if (bcount[v] == BUCKET_SIZE) {
            fprintf(stderr, "not enough memory.\n");
            fprintf(stderr, "v == %u; bcount[v] = %d.\n", v, bcount[v]);
            exit(EXIT_FAILURE);
          }
          bucket[v][bcount[v]++] = *aa++;
        }
        aa = a;
        for (int k=0; k<NBUCKETS; k++) {
          for (int j=0; j<bcount[k]; j++) *aa++ = bucket[k][j];
        }
        shift += 8;
      }
    }
    
    int ar1[ARRAY_SIZE];
    int ar2[ARRAY_SIZE];
    
    int main(void) {
      clock_t t0;
    
      srand(time(0));
      for (int k=0; k<ARRAY_SIZE; k++) ar1[k] = ar2[k] = RANDOM_NUMBER;
    
      t0 = clock(); while (clock() == t0) /* void */; t0 = clock();
      do {
        quick(ar1, 0, ARRAY_SIZE - 1);
      } while (0);
      double qsort_time = (double)(clock() - t0) / CLOCKS_PER_SEC;
    
      t0 = clock(); while (clock() == t0) /* void */; t0 = clock();
      do {
        radix(ar2, ARRAY_SIZE);
      } while (0);
      double radix_time = (double)(clock() - t0) / CLOCKS_PER_SEC;
    
      printf("qsort time: %.2f secs\n", qsort_time);
      printf("radix time: %.2f secs\n", radix_time);
    
      /* compare sorted arrays by sampling */
      int evil = 0;
      int good = 0;
      for (int chk=0; chk<2000; chk++) {
        size_t index = RANDOM_NUMBER % ARRAY_SIZE;
        if (ar1[index] == ar2[index]) good++;
        else evil++;
      }
      printf("good: %d; evil: %d\n", good, evil);
    
      return 0;
    }
    


0 commentaires

1
votes

Si ce n'est pas seulement pour l'apprentissage d'apprentissage qsort de stdlib.h < / p>


0 commentaires

2
votes
  1. Vous pouvez éliminer la surcharge de récupérication en utilisant QuickSort avec une pile explicite XXX

  2. Vous pouvez utiliser une meilleure technique de sélection de pivot, telle que:

    1. médiane de 3
    2. médiane des médianes
    3. Pivot aléatoire

3 commentaires

Encore une fois, cette approche appuie inconditionnellement la partition inférieure sur la pile, tout en traitant la partition supérieure itérative. En général, la profondeur de la pile sera O (n) (sauf si vous garantir la médiane presque parfaite), ce qui n'est pas bon. Si vous ajoutez un autre si qui veille à pousser toujours la partition shorter sur la pile, la profondeur de pile ne dépassera jamais O (log n) même avec le pire choix de médiane .


Récursion n'a pas trop de tête, en fait. Voir en.wikipedia.org/wiki/dynamic_programmer


@ Andreyt, merci pour la suggestion :) @ inconnu (Google), comment DP est applicable? Récursion crée un grand nombre de cadres de pile (adresses de retour, pointeur de cadre, etc.) sur la pile de programmes qui est évidemment une overhead.Utilisation d'une pile explicite éliminera ce surcharge.



1
votes

par votre code, lors de la tri de la longueur de tri 10, la pile la plus profonde ressemble à xxx pré>

en plus de l'algo elle-même, si nous considérons le comportement du système tel que des activités de pile que, sauf Les coûts de pile d'appels normaux (PUSH / POP) pourraient déclasser les performances, par exemple Commutation de contexte Si le système multi-tâches, vous savez que la plupart des systèmes d'exploitation sont multi-tâches et plus la pile augmente coûte la commutation, ainsi que la limite de cache ou de cachline à travers. P>

Je crois en ce cas si la longueur devient Plus gros, vous perdrez lorsque vous comparez à d'autres algues de tri. p>

Par exemple, lorsque la longueur est comprise jusqu'à 40, la pile peut ressembler (peut-être plus d'entrées que représentées comme ci-dessous): P>

#0  partition (a=0x7fff24810cd0, lower=8, upper=9) 
#1  0x000000000040065d in quick (a=0x7fff24810cd0, lower=8, upper=9)  
#2  0x0000000000400672 in quick (a=0x7fff24810cd0, lower=8, upper=10) 
#3  0x0000000000400672 in quick (a=0x7fff24810cd0, lower=8, upper=14) 
#4  0x0000000000400684 in quick (a=0x7fff24810cd0, lower=4, upper=14) 
#5  0x0000000000400672 in quick (a=0x7fff24810cd0, lower=4, upper=18) 
#6  0x0000000000400684 in quick (a=0x7fff24810cd0, lower=0, upper=18) 
#7  0x0000000000400672 in quick (a=0x7fff24810cd0, lower=0, upper=26) 
#8  0x0000000000400672 in quick (a=0x7fff24810cd0, lower=0, upper=38) 
#9  0x0000000000400672 in quick (a=0x7fff24810cd0, lower=0, upper=39) 
#10 0x00000000004005ee in main  


0 commentaires

0
votes

La première chose à faire est la référence. Et repasser-le contre la standard qsort, et contre le C ++ STD :: Trier et STD :: Stable_sort.

Vos résultats, si votre ensemble de données sera suffisamment élevé, montrera que le STD :: Trier est supérieur à QSort dans tous les cas sauf les ceux où le STD :: Stable_sort est supérieur. C'est parce que le STD :: Tri est modélisé et donc la comparaison peut être inlinée.

Votre code aligne la comparaison car il n'est pas générique. Si votre code est plus lent que QSort, vous avez un problème ici.

Le tri plus rapide serait de trier les pièces en parallèle, par ex. OpenMP, puis les fusionner ensemble.


0 commentaires

0
votes

Copié de ma réponse à la question de réponse.

Edit:. Ce poste suppose que vous faites déjà des choses évidentes comme prendre avantage de récursion de queue pour se débarrasser de la surcharge d'appels inutiles

Les gens aiment critiquer le quicksort pour la mauvaise performance avec certaines entrées, en particulier lorsque l'utilisateur a le contrôle de l'entrée. Les rendements d'approche suite à une sélection de mi-parcours correspondant de la performance, mais la complexité attendue de façon exponentielle approches O ( n log n ) que la liste grandit en taille. Dans mon expérience, il surclasse nettement plus de 3 sélection en raison de beaucoup moins frais généraux dans le cas de la majorité. Il faut effectuer régulièrement avec la sélection mi-parcours pour les entrées « attendues », mais ne sont pas vulnérables aux intrants pauvres.

  • Initialiser le tri rapide avec un nombre entier positif aléatoire I . Cette valeur sera utilisée tout au long du processus de tri (ne pas générer de multiples numéros).
  • Pivot est sélectionné comme I mod SectionSize .

    Pour des performances supplémentaires, vous devez toujours passer votre quicksort à shell tri pour les segments de la liste des « petits » -. Je l'ai vu des longueurs de 15-100 choisies comme seuil


0 commentaires

0
votes

multithreading?

/*
 * multiple-thread quick-sort.
 * Works fine on uniprocessor machines as well.
 */

#include <unistd.h>
#include <stdlib.h>
#include <thread.h>

/* don't create more threads for less than this */
#define SLICE_THRESH   4096

/* how many threads per lwp */
#define THR_PER_LWP       4

/* cast the void to a one byte quanitity and compute the offset */
#define SUB(a, n)      ((void *) (((unsigned char *) (a)) + ((n) * width)))

typedef struct {
  void    *sa_base;
  int      sa_nel;
  size_t   sa_width;
  int    (*sa_compar)(const void *, const void *);
} sort_args_t;

/* for all instances of quicksort */
static int threads_avail;

#define SWAP(a, i, j, width)
{ 
  int n; 
  unsigned char uc; 
  unsigned short us; 
  unsigned long ul; 
  unsigned long long ull; 

  if (SUB(a, i) == pivot) 
    pivot = SUB(a, j); 
  else if (SUB(a, j) == pivot) 
    pivot = SUB(a, i); 

  /* one of the more convoluted swaps I've done */ 
  switch(width) { 
  case 1: 
    uc = *((unsigned char *) SUB(a, i)); 
    *((unsigned char *) SUB(a, i)) = *((unsigned char *) SUB(a, j)); 
    *((unsigned char *) SUB(a, j)) = uc; 
    break; 
  case 2: 
    us = *((unsigned short *) SUB(a, i)); 
    *((unsigned short *) SUB(a, i)) = *((unsigned short *) SUB(a, j)); 
    *((unsigned short *) SUB(a, j)) = us; 
    break; 
  case 4: 
    ul = *((unsigned long *) SUB(a, i)); 
    *((unsigned long *) SUB(a, i)) = *((unsigned long *) SUB(a, j)); 
    *((unsigned long *) SUB(a, j)) = ul; 
    break; 
  case 8: 
    ull = *((unsigned long long *) SUB(a, i)); 
    *((unsigned long long *) SUB(a,i)) = *((unsigned long long *) SUB(a,j)); 
    *((unsigned long long *) SUB(a, j)) = ull; 
    break; 
  default: 
    for(n=0; n<width; n++) { 
      uc = ((unsigned char *) SUB(a, i))[n]; 
      ((unsigned char *) SUB(a, i))[n] = ((unsigned char *) SUB(a, j))[n]; 
      ((unsigned char *) SUB(a, j))[n] = uc; 
    } 
    break; 
  } 
}

static void *
_quicksort(void *arg)
{
  sort_args_t *sargs = (sort_args_t *) arg;
  register void *a = sargs->sa_base;
  int n = sargs->sa_nel;
  int width = sargs->sa_width;
  int (*compar)(const void *, const void *) = sargs->sa_compar;
  register int i;
  register int j;
  int z;
  int thread_count = 0;
  void *t;
  void *b[3];
  void *pivot = 0;
  sort_args_t sort_args[2];
  thread_t tid;

  /* find the pivot point */
  switch(n) {
  case 0:
  case 1:
    return 0;
  case 2:
    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {
      SWAP(a, 0, 1, width);
    }
    return 0;
  case 3:
    /* three sort */
    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {
      SWAP(a, 0, 1, width);
    }
    /* the first two are now ordered, now order the second two */
    if ((*compar)(SUB(a, 2), SUB(a, 1)) < 0) {
      SWAP(a, 2, 1, width);
    }
    /* should the second be moved to the first? */
    if ((*compar)(SUB(a, 1), SUB(a, 0)) < 0) {
      SWAP(a, 1, 0, width);
    }
    return 0;
  default:
    if (n > 3) {
      b[0] = SUB(a, 0);
      b[1] = SUB(a, n / 2);
      b[2] = SUB(a, n - 1);
      /* three sort */
      if ((*compar)(b[0], b[1]) > 0) {
        t = b[0];
        b[0] = b[1];
        b[1] = t;
      }
      /* the first two are now ordered, now order the second two */
      if ((*compar)(b[2], b[1]) < 0) {
        t = b[1];
        b[1] = b[2];
        b[2] = t;
      }
      /* should the second be moved to the first? */
      if ((*compar)(b[1], b[0]) < 0) {
        t = b[0];
        b[0] = b[1];
        b[1] = t;
      }
      if ((*compar)(b[0], b[2]) != 0)
        if ((*compar)(b[0], b[1]) < 0)
          pivot = b[1];
        else
          pivot = b[2];
    }
    break;
  }
  if (pivot == 0)
    for(i=1; i<n; i++) {
      if (z = (*compar)(SUB(a, 0), SUB(a, i))) {
        pivot = (z > 0) ? SUB(a, 0) : SUB(a, i);
        break;
      }
    }
  if (pivot == 0)
    return;

  /* sort */
  i = 0;
  j = n - 1;
  while(i <= j) {
    while((*compar)(SUB(a, i), pivot) < 0)
      ++i;
    while((*compar)(SUB(a, j), pivot) >= 0)
      --j;
    if (i < j) {
      SWAP(a, i, j, width);
      ++i;
      --j;
    }
  }

  /* sort the sides judiciously */
  switch(i) {
  case 0:
  case 1:
    break;
  case 2:
    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {
      SWAP(a, 0, 1, width);
    }
    break;
  case 3:
    /* three sort */
    if ((*compar)(SUB(a, 0), SUB(a, 1)) > 0) {
      SWAP(a, 0, 1, width);
    }
    /* the first two are now ordered, now order the second two */
    if ((*compar)(SUB(a, 2), SUB(a, 1)) < 0) {
      SWAP(a, 2, 1, width);
    }
    /* should the second be moved to the first? */
    if ((*compar)(SUB(a, 1), SUB(a, 0)) < 0) {
      SWAP(a, 1, 0, width);
    }
    break;
  default:
    sort_args[0].sa_base          = a;
    sort_args[0].sa_nel           = i;
    sort_args[0].sa_width         = width;
    sort_args[0].sa_compar        = compar;
    if ((threads_avail > 0) && (i > SLICE_THRESH)) {
      threads_avail--;
      thr_create(0, 0, _quicksort, &sort_args[0], 0, &tid);
      thread_count = 1;
    } else
      _quicksort(&sort_args[0]);
    break;
  }
  j = n - i;
  switch(j) {
  case 1:
    break;
  case 2:
    if ((*compar)(SUB(a, i), SUB(a, i + 1)) > 0) {
      SWAP(a, i, i + 1, width);
    }
    break;
  case 3:
    /* three sort */
    if ((*compar)(SUB(a, i), SUB(a, i + 1)) > 0) {
      SWAP(a, i, i + 1, width);
    }
    /* the first two are now ordered, now order the second two */
    if ((*compar)(SUB(a, i + 2), SUB(a, i + 1)) < 0) {
      SWAP(a, i + 2, i + 1, width);
    }
    /* should the second be moved to the first? */
    if ((*compar)(SUB(a, i + 1), SUB(a, i)) < 0) {
      SWAP(a, i + 1, i, width);
    }
    break;
  default:
    sort_args[1].sa_base          = SUB(a, i);
    sort_args[1].sa_nel           = j;
    sort_args[1].sa_width         = width;
    sort_args[1].sa_compar        = compar;
    if ((thread_count == 0) && (threads_avail > 0) && (i > SLICE_THRESH)) {
      threads_avail--;
      thr_create(0, 0, _quicksort, &sort_args[1], 0, &tid);
      thread_count = 1;
    } else
      _quicksort(&sort_args[1]);
    break;
  }
  if (thread_count) {
    thr_join(tid, 0, 0);
    threads_avail++;
  }
  return 0;
}

void
quicksort(void *a, size_t n, size_t width,
          int (*compar)(const void *, const void *))
{
  static int ncpus = -1;
  sort_args_t sort_args;

  if (ncpus == -1) {
    ncpus = sysconf( _SC_NPROCESSORS_ONLN);

    /* lwp for each cpu */
    if ((ncpus > 1) && (thr_getconcurrency() < ncpus))
      thr_setconcurrency(ncpus);

    /* thread count not to exceed THR_PER_LWP per lwp */
    threads_avail = (ncpus == 1) ? 0 : (ncpus * THR_PER_LWP);
  }
  sort_args.sa_base = a;
  sort_args.sa_nel = n;
  sort_args.sa_width = width;
  sort_args.sa_compar = compar;
  (void) _quicksort(&sort_args);
}


0 commentaires

1
votes

réponse complètement muette, mais ... compilez votre code en mode de sortie et activez les optimisations!


0 commentaires

2
votes

Actuellement, le plus avancé rapide utilisé largement utilisé est implémenté dans Java's Dualpivotquicksort.java Vous pouvez donc simplement suivre cette approche et vous verrez une bonne amélioration de la performance:

  • Utilisez le tri d'insertion pour les petits tableaux (47 est le numéro utilisé dans Java)
  • Utilisez ce quicksort Dual-Pivot Choisir les 2e et 4ème éléments de 5 comme les deux pivots
  • envisagez d'utiliser Mergesort pour les tableaux avec des numéros triés

    ou si vous souhaitez ajouter un peu plus de complexité, code A 3-Pivot Quicksort . < / p>


0 commentaires