35 votes

Le moyen le plus rapide de trier des tableaux d'entiers signés 32 bits en JavaScript?

_radixSort_0 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
/*
RADIX SORT
Use 256 bins
Use shadow array
- Get counts
- Transform counts to pointers
- Sort from LSB - MSB
*/
function radixSort(intArr) {
    var cpy = new Int32Array(intArr.length);
    var c4 = [].concat(_radixSort_0); 
    var c3 = [].concat(_radixSort_0); 
    var c2 = [].concat(_radixSort_0);
    var c1 = [].concat(_radixSort_0); 
    var o4 = 0; var t4;
    var o3 = 0; var t3;
    var o2 = 0; var t2;
    var o1 = 0; var t1;
    var x;
    for(x=0; x<intArr.length; x++) {
        t4 = intArr[x] & 0xFF;
        t3 = (intArr[x] >> 8) & 0xFF;
        t2 = (intArr[x] >> 16) & 0xFF;
        t1 = (intArr[x] >> 24) & 0xFF ^ 0x80;
        c4[t4]++;
        c3[t3]++;
        c2[t2]++;
        c1[t1]++;
    }
    for (x=0; x<256; x++) {
        t4 = o4 + c4[x];
        t3 = o3 + c3[x];
        t2 = o2 + c2[x];
        t1 = o1 + c1[x];
        c4[x] = o4;
        c3[x] = o3;
        c2[x] = o2;
        c1[x] = o1;
        o4 = t4;
        o3 = t3;
        o2 = t2;
        o1 = t1;
    }
    for(x=0; x<intArr.length; x++) {
        t4 = intArr[x] & 0xFF;
        cpy[c4[t4]] = intArr[x];
        c4[t4]++;
    }
    for(x=0; x<intArr.length; x++) {
        t3 = (cpy[x] >> 8) & 0xFF;
        intArr[c3[t3]] = cpy[x];
        c3[t3]++;
    }
    for(x=0; x<intArr.length; x++) {
        t2 = (intArr[x] >> 16) & 0xFF;
        cpy[c2[t2]] = intArr[x];
        c2[t2]++;
    }
    for(x=0; x<intArr.length; x++) {
        t1 = (cpy[x] >> 24) & 0xFF ^ 0x80;
        intArr[c1[t1]] = cpy[x];
        c1[t1]++;
    }
    return intArr;
}

EDIT:

Jusqu'à présent, le meilleur/seule grande optimisation de la mise au jour est JS tableaux typés. À l'aide d'un tableau typé pour la normale de la base de tri de l'ombre du tableau a donné les meilleurs résultats. J'ai aussi été en mesure de serrer un peu plus de la en place rapide de tri à l'aide de JS construit dans la pile push/pop.


dernière jsfiddle de référence

Intel i7 870, 4GB, FireFox 8.0
2mil
radixSort(intArr): 172 ms
radixSortIP(intArr): 1738 ms
quickSortIP(arr): 661 ms
200k
radixSort(intArr): 18 ms
radixSortIP(intArr): 26 ms
quickSortIP(arr): 58 ms

Il apparaît standard de tri radix est en effet le roi pour ce flux de travail. Si quelqu'un a le temps d'expérimenter avec boucle-de dérouler ou d'autres modifications pour cela que je l'apprécierais.

J'ai un cas d'utilisation spécifiques où je voudrais la manière la plus rapide possible de tri implémentation en JavaScript. Il sera grand (de 50 000 2mil), triée (essentiellement aléatoire), entier (32 bits signés) des tableaux que le script client aura accès, il est alors nécessaire de trier et de présenter ces données.

J'ai mis en place assez rapide en place de tri radix et mise en place de tri rapide jsfiddle de référence , mais pour ma limite supérieure de la matrice de longueur, elles sont encore assez lent. Le tri rapide fonctionne mieux sur ma borne supérieure sur la taille du tableau, tandis que la base de tri de meilleures performances sur le bas de mon lié.

defaultSort is the built-in JavaScript array.sort with an integer compare function

Intel C2Q 9650, 4GB, FireFox 3.6
2mil
radixSortIP(intArr): 5554 ms
quickSortIP(arr): 1796 ms
200k
radixSortIP(intArr): 139 ms
quickSortIP(arr): 190 ms
defaultSort(intArr): 354 ms

Intel i7 870, 4GB, FireFox 8.0
2mil
radixSortIP(intArr): 990 ms
quickSortIP(arr): 882 ms
defaultSort(intArr): 3632 ms
200k
radixSortIP(intArr): 28 ms
quickSortIP(arr): 68 ms
defaultSort(intArr): 306 ms

Questions

  • Est-il une meilleure mise en œuvre de tout algorithme de tri qui pourrait répondre à mon cas d'utilisation et des besoins?
  • Existe-il des optimisations qui peuvent être faites à ma place radix/tri rapide implémentations pour améliorer les performances?
    • Est-il un moyen efficace pour convertir mes en place radix tri à partir d'un récursif pour itératif de la fonction? La mémoire et la vitesse d'exécution.

Objectif

  • Je suis en espérant que ces réponses vous m'aider à ~20% à 30% d'amélioration de la performance dans mon test de référence.

Clarifications/Notes

  • "DÉFINIR un RAPIDE" je préfère un cas général où il fonctionne bien sur tous les navigateurs modernes, mais si il y a un navigateur spécifique d'optimisation qui permet une amélioration notable, qui peut être acceptable.
  • Le tri PEUT être fait côté serveur, mais je préfère éviter ce parce que le JS application peut devenir autonome (jumelés, au large du plateau, propriétaire de l'application qui vont capteur de flux de données dans un fichier).
  • JavaScript peut-être pas le meilleur langage pour cela, mais c'est une exigence.
  • J'ai déjà posé cette question la façon la plus Rapide pour trier entier tableaux en JavaScript une réponse incorrecte a été voté et la question a été fermé.
  • J'ai tenté à l'aide de plusieurs fenêtre du navigateur instances comme une fortune multi-threading; il n'a pas concrétisées. Je serais intéressé par l'information utile concernant la ponte plusieurs fenêtres à la simultanéité.

12voto

galambalazs Points 24393

J'ai testé les tableaux typés , la version QSIP semble être bonne sur les navigateurs modernes:

2 000 000 éléments

           QSIP_TYPED | RDXIP_TYPED |  QSIP_STD | RDXIP_STD
----------------------------------------------------------
Chrome  |    300          1000          600        1300
Firefox |    550          1500          800        1600    
 

http://jsfiddle.net/u8t2a/35/

Support ( source: http://caniuse.com/typedarrays ):

  IE 10+   |   FF 4+  |  Chrome 7+  |  Safari 5.1+  |  Opera 11.6+   
 

2voto

Tudor Points 39539

Avez-vous envisagé une combinaison d'algorithmes pour maximiser l'utilisation du cache? J'ai vu dans la référence que vous passez au type d'insertion lorsque les sous-matrices deviennent petites. Une approche intéressante consiste plutôt à passer à Heapsort. Utilisé conjointement avec quicksort, il peut réduire le pire des cas à O (nlog (n)) au lieu de O (n ^ 2). Regardez Introsort .

1voto

herby Points 2792

Je jouait avec votre référence et ajouté ma propre fonction de tri. Il effectue même que radixsort, mais c'est l'idée (et la mise en œuvre) est plus simple, c'est comme un radixsort, mais en binaire, de sorte que vous n'avez que deux seau et peut le faire sur place. Regarder http://jsfiddle.net/KaRV9/7/.

J'ai mis mon code à la place de "Quicksort en place" (car il est très similaire à quicksort, juste pivot est sélectionné autre façon). Exécuter plusieurs fois, dans mon Chrome 15 ils effectuent si proche qu'il est incapable de les distinguer.

1voto

Laurent Zuijdwijk Points 493

Je ne vais pas faire de commentaire sur votre algorithmes de tri. vous en savez plus sur ces que moi.

Mais une bonne idée serait d'utiliser des web workers. Cela permet à votre sorte à s'exécuter en arrière-plan dans son propre thread et donc pas de blocage de l'interface. Ce serait bon de pratiquer n'importe quoi. Il est bien pris en charge pour Chrome et Firefox. L'opéra est un non-version letée. Pas sûr au sujet de la prise en charge dans IE, mais il serait facile de contourner cela. Il est bien entendu que les coûts de l'utilisation de plusieurs threads.

Fusion de tri peut être fait facilement en une version multi-thread, qui vous donnera des performances. La messagerie est livré avec une pénalité de temps, donc ça va vraiment dépendre de votre situation spécifique, s'il va courir plus vite. N'oubliez pas, cependant, que le non-blocage de la nature pourrait lui faire sentir comme l'application est en cours d'exécution plus rapide pour l'utilisateur final.

0voto

orip Points 28225

EDIT: je vois que vous êtes déjà en utilisant le tri par insertion pour les plus petits sous-réseaux. J'ai manqué.

La bonne du monde réel approche avec quicksort est de vérifier la taille de la subarray, et si elle est assez courte, faites un petit peu de frais généraux de tri qui est trop lent pour des matrices plus grandes, telles que le tri par insertion.

Pseudo-code est quelque chose le long des lignes de:

quicksort(array, start, end):
  if ( (end-start) < THRESHOLD ) {
    insertion_sort(array, start, end);
    return;
  }
  // regular quicksort here
  // ...

Pour déterminer le SEUIL, vous avez besoin de temps sur les plates-formes que vous souhaitez, dans votre cas, - éventuellement les différents navigateurs. Mesurer le temps au hasard des tableaux avec des seuils différents pour trouver un proche de l'optimal. Vous pouvez également choisir des seuils différents pour les différents navigateurs si vous trouvez des différences significatives.

Maintenant, si vos données ne sont pas vraiment aléatoires (ce qui est assez commun), vous pouvez voir si le meilleur pivot de la sélection améliore les performances. Une méthode courante est la médiane de trois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X