Angesichts des Interesses an dieser Frage hielt ich es für interessant, die Antworten durch einen Wettbewerbsvorschlag etwas objektiver und quantitativer zu gestalten.
Die Idee ist einfach: Ich habe eine Binärdatei generiert, die 50 Millionen Gauß-verteilte Double enthält (Durchschnitt: 0, stdev 1). Das Ziel ist es, ein Programm zu erstellen, das diese so schnell wie möglich im Speicher sortiert. Eine sehr einfache Referenzimplementierung in Python benötigt 1m4s. Wie tief können wir gehen?
Die Regeln lauten wie folgt: Beantworten Sie die Frage mit einem Programm, das die Datei "gaussian.dat" öffnet und die Nummern im Speicher sortiert (keine Ausgabe erforderlich) sowie Anweisungen zum Erstellen und Ausführen des Programms. Das Programm muss auf meinem Arch Linux-Computer ausgeführt werden können (dh Sie können eine beliebige Programmiersprache oder Bibliothek verwenden, die auf diesem System leicht zu installieren ist).
Das Programm muss einigermaßen lesbar sein, damit ich sicherstellen kann, dass es sicher gestartet werden kann (bitte keine Nur-Assembler-Lösung!).
Ich werde die Antworten auf meinem Computer ausführen (Quadcore, 4 Gigabyte RAM). Die schnellste Lösung erhält die akzeptierte Antwort und ein Kopfgeld von 100 Punkten :)
Das Programm zur Generierung der Zahlen:
#!/usr/bin/env python
import random
from array import array
from sys import argv
count=int(argv[1])
a=array('d',(random.gauss(0,1) for x in xrange(count)))
f=open("gaussian.dat","wb")
a.tofile(f)
Die einfache Referenzimplementierung:
#!/usr/bin/env python
from array import array
from sys import argv
count=int(argv[1])
a=array('d')
a.fromfile(open("gaussian.dat"),count)
print "sorting..."
b=sorted(a)
EDIT: nur 4 GB RAM, sorry
EDIT # 2: Beachten Sie, dass der Punkt des Wettbewerbs ist, um zu sehen, ob wir vorherige Informationen über die Daten verwenden können . Es soll kein Pissing Match zwischen verschiedenen Programmiersprachen-Implementierungen sein!
quelle
Antworten:
Hier ist eine Lösung in C ++, bei der zuerst die Zahlen in Gruppen mit der gleichen erwarteten Anzahl von Elementen aufgeteilt werden und dann jede Gruppe separat sortiert wird. Es berechnet eine Tabelle der kumulativen Verteilungsfunktion anhand einiger Formeln aus Wikipedia vor und interpoliert dann Werte aus dieser Tabelle, um eine schnelle Annäherung zu erhalten.
In mehreren Threads werden mehrere Schritte ausgeführt, um die vier Kerne zu nutzen.
Verwenden Sie zum Kompilieren und Ausführen diesen Befehl:
BEARBEITEN: Alle Buckets werden jetzt in dasselbe Array gestellt, damit die Buckets nicht wieder in das Array kopiert werden müssen. Auch die Größe der Tabelle mit vorberechneten Werten wurde reduziert, da die Werte genau genug sind. Wenn ich jedoch die Anzahl der Buckets über 256 ändere, dauert die Ausführung des Programms länger als bei dieser Anzahl von Buckets.
EDIT: Gleicher Algorithmus, andere Programmiersprache. Ich habe C ++ anstelle von Java verwendet und die Laufzeit auf meinem Computer von ~ 3,2 s auf ~ 2,35 s reduziert. Die optimale Anzahl von Eimern liegt immer noch bei 256 (wieder auf meinem Computer).
Übrigens, tbb ist wirklich großartig.
EDIT: Ich war von Alexandru's großartiger Lösung inspiriert und habe die std :: sort in der letzten Phase durch eine modifizierte Version seiner radix Sort ersetzt. Ich habe eine andere Methode verwendet, um mit den positiven / negativen Zahlen umzugehen, obwohl mehr Durchgänge durch das Array erforderlich sind. Ich habe auch beschlossen, das Array genau zu sortieren und die Einfügesortierung zu entfernen. Ich werde später einige Zeit damit verbringen, zu testen, wie sich diese Änderungen auf die Leistung auswirken und sie möglicherweise rückgängig machen. Bei Verwendung der Radix-Sortierung verringerte sich die Zeit jedoch von ~ 2,35 s auf ~ 1,63 s.
quelle
Ohne schlau zu werden, nur um einen viel schnelleren naiven Sortierer bereitzustellen, ist hier einer in C, der Ihrem Python-Sortierer ziemlich ähnlich sein sollte:
Kompiliert mit
gcc -O3
, auf meinem Rechner dauert dies mehr als eine Minute weniger als auf dem Python: ca. 11 s im Vergleich zu 87 s.quelle
Ich habe anhand der Standardabweichung in Segmente unterteilt, die es am besten in Vierteln aufteilen sollten. Bearbeiten: Auf Partition basierend auf dem x-Wert in http://en.wikipedia.org/wiki/Error_function#Table_of_values umgeschrieben
http://www.wolframalpha.com/input/?i=percentages+by++normal+distribution
Ich habe versucht, kleinere Eimer zu verwenden, aber es schien nur eine geringe Wirkung zu haben, sobald die Anzahl der verfügbaren Kerne überschritten wurde. Ohne parallele Sammlungen würde es 37 Sekunden auf meiner Box und 24 Sekunden bei den parallelen Sammlungen dauern. Wenn Sie über eine Distribution partitionieren, können Sie nicht nur ein Array verwenden, es entsteht also ein zusätzlicher Aufwand. Mir ist nicht klar, wann ein Wert in Scala ein- oder ausgepackt wird.
Ich verwende Scala 2.9 für die parallele Sammlung. Sie können einfach die tar.gz-Distribution herunterladen.
Zum Kompilieren: scalac SortFile.scala (Ich habe es gerade direkt in den Ordner scala / bin kopiert.
Zum Ausführen: JAVA_OPTS = "- Xmx4096M" ./scala SortFile (Ich habe es mit 2 Gigs RAM ausgeführt und ungefähr zur gleichen Zeit erhalten)
Bearbeiten: allocateDirect wurde entfernt, langsamer als nur das Zuweisen. Priming der Anfangsgröße für Array-Puffer entfernt. Eigentlich hat es die ganzen 50000000 Werte gelesen. Neu geschrieben, um hoffentlich Autoboxing-Probleme zu vermeiden (immer noch langsamer als naiv c)
quelle
Schreiben Sie dies einfach in eine cs-Datei und kompilieren Sie es theoretisch mit csc: (Benötigt Mono)
quelle
Da Sie die Verteilung kennen, können Sie eine direkte Indizierung nach O (N) verwenden. (Wenn Sie sich fragen, was das ist, nehmen Sie an, Sie haben ein Kartenspiel mit 52 Karten und möchten es sortieren. Haben Sie nur 52 Fächer und werfen Sie jede Karte in ihr eigenes Fach.)
Sie haben 5e7 Doppel. Ordnen Sie ein Ergebnisarray R mit 5e7-Doppelwerten zu. Nimm jede Zahl
x
und hol sie diri = phi(x) * 5e7
. Grundsätzlich tunR[i] = x
. Sie können mit Kollisionen umgehen, z. B. indem Sie die Nummer verschieben, mit der sie möglicherweise kollidiert (wie bei der einfachen Hash-Codierung). Alternativ können Sie R ein paar Mal größer machen und mit einem eindeutigen leeren Wert füllen . Am Ende fegen Sie einfach die Elemente von R.phi
ist nur die Gaußsche kumulative Verteilungsfunktion. Es konvertiert eine gaußsche verteilte Zahl zwischen +/- unendlich in eine gleichmäßige verteilte Zahl zwischen 0 und 1. Eine einfache Methode zur Berechnung ist das Nachschlagen und Interpolieren von Tabellen.quelle
Hier ist eine andere sequentielle Lösung:
Ich bezweifle, dass es die Multithread-Lösung schlägt, aber die Timings auf meinem i7-Laptop sind (stdsort ist die C ++ - Lösung, die in einer anderen Antwort bereitgestellt wird):
Beachten Sie, dass diese Lösung eine lineare Zeitkomplexität aufweist (da die spezielle Darstellung von Doppelwerten verwendet wird).
BEARBEITEN : Die Reihenfolge der Elemente wurde korrigiert.
EDIT : Geschwindigkeit um fast eine halbe Sekunde verbessert.
BEARBEITEN : Geschwindigkeit um weitere 0,7 Sekunden verbessert. Der Algorithmus wurde cachefreundlicher.
EDIT : Geschwindigkeit um 1 Sekunde erhöht. Da es nur 50.000.000 Elemente gibt, kann ich die Mantisse teilweise sortieren und Insert-Sort (das cachefreundlich ist) verwenden, um fehl am Platze liegende Elemente zu reparieren. Diese Idee entfernt ungefähr zwei Iterationen aus der letzten Radix-Sortierschleife.
EDIT : 0,16 Sekunden weniger. First std :: reverse kann bei umgekehrter Sortierreihenfolge entfallen.
quelle
Nehmen Sie die Lösung von Christian Ammer und parallelisieren Sie sie mit den Threaded Building Blocks von Intel
Wenn Sie Zugriff auf die IPP-Bibliothek (Performance Primitives) von Intel haben, können Sie deren Radix-Sortierung verwenden. Einfach austauschen
mit
und
mit
Auf meinem Dual-Core-Laptop sind die Timings
quelle
Wie wäre es mit einer Implementierung von parallelem QuickSort , bei der die Pivot-Werte basierend auf den Statistiken der Verteilung ausgewählt werden und dabei gleich große Partitionen sichergestellt werden? Der erste Pivot wäre der Mittelwert (in diesem Fall Null), das nächste Paar wäre das 25. und das 75. Perzentil (+/- 0,67449 Standardabweichungen) usw., wobei jede Partition den verbleibenden Datensatz mehr oder halbiert weniger perfekt.
quelle
Sehr hässlich (warum Arrays verwenden, wenn ich Variablen verwenden kann, die mit Zahlen enden), aber schneller Code (mein erster Versuch zu std :: threads), ganze Zeit (Zeit real) auf meinem System 1,8 s (im Vergleich zu std :: sort) () 4,8 s), kompiliere mit g ++ -std = c ++ 0x -O3 -march = native -pthread Übergebe einfach Daten über stdin (funktioniert nur für 50M).
// Bearbeiten geändert, um die Datei gaussian.dat zu lesen.
quelle
Eine C ++ - Lösung mit
std::sort
(möglicherweise schneller als qsort, in Bezug auf die Leistung von qsort gegenüber std :: sort )Ich kann nicht zuverlässig sagen, wie lange es dauert, da ich nur 1 GB auf meinem Computer habe und mit dem angegebenen Python-Code nur eine
gaussian.dat
Datei mit nur 25 Millionen Doppeln erstellen konnte (ohne einen Speicherfehler zu erhalten). Aber ich bin sehr interessiert, wie lange der std :: sort-Algorithmus läuft.quelle
sort.h
Datei vornehmen , um sie mit C ++ zu kompilieren. Es war ungefähr doppelt so langsam wiestd::sort
. Weiß nicht warum, vielleicht wegen Compiler-Optimierungen?Hier ist eine Mischung aus Alexandru's Radix-Sorte mit Zjareks gewundenem Smart-Pivoting. Kompiliere es mit
Sie können die Radixgröße ändern, indem Sie STEP definieren (z. B. -DSTEP = 11 hinzufügen). Ich fand das Beste für meinen Laptop 8 (die Standardeinstellung).
Standardmäßig wird das Problem in vier Teile aufgeteilt und auf mehreren Threads ausgeführt. Sie können dies ändern, indem Sie einen Tiefenparameter an die Befehlszeile übergeben. Also, wenn Sie zwei Kerne haben, führen Sie es als
und wenn du 16 Kerne hast
Die maximale Tiefe beträgt derzeit 6 (64 Fäden). Wenn Sie zu viele Ebenen setzen, verlangsamen Sie den Code.
Eine Sache, die ich auch ausprobiert habe, war die Radix-Sortierung aus der Intel Performance Primitives (IPP) -Bibliothek. Die Implementierung von Alexandru stützt IPP auf solide Weise, wobei IPP etwa 30% langsamer ist. Diese Variante ist auch hier enthalten (auskommentiert).
BEARBEITEN : Ich habe die Cache-Verbesserungen von Alexandru implementiert und das hat ungefähr 30% der Zeit auf meinem Computer gekürzt.
BEARBEITEN : Dies implementiert eine rekursive Sortierung, so dass es auf Alexandru's 16-Kern-Maschine gut funktionieren sollte. Es benutzt auch Alexandru's letzte Verbesserung und entfernt eine der Umkehrungen. Für mich ergab sich eine Verbesserung von 20%.
BEARBEITEN : Es wurde ein Vorzeichenfehler behoben, der zu Ineffizienz führte, wenn mehr als 2 Kerne vorhanden waren.
BEARBEITEN : Lambda wurde entfernt, so dass es mit älteren Versionen von gcc kompiliert werden kann. Es enthält die auskommentierte IPP-Codevariante. Ich habe auch die Dokumentation für das Laufen auf 16 Kernen korrigiert. Soweit ich das beurteilen kann, ist dies die schnellste Implementierung.
BEARBEITEN : Ein Fehler wurde behoben, wenn STEP nicht 8 ist. Die maximale Anzahl der Threads wurde auf 64 erhöht. Einige Timing-Informationen wurden hinzugefügt.
quelle
step
(11 war auf meinem Laptop optimal).int cnt[mask]
sollte seinint cnt[mask + 1]
. Verwenden Sie für bessere Ergebnisse einen festen Wertint cnt[1 << 16]
.Ich denke, das hängt wirklich davon ab, was Sie tun möchten. Wenn Sie eine Reihe von Gaußschen sortieren möchten, hilft Ihnen das nicht weiter. Aber wenn Sie einen Haufen sortierter Gaußscher wollen, ist dies der Fall. Auch wenn dies das Problem ein wenig verfehlt, halte ich es für interessant, die tatsächlichen Sortierroutinen mit denen zu vergleichen.
Wenn Sie schnell sein möchten, tun Sie weniger.
Anstatt eine Reihe von Zufallsstichproben aus der Normalverteilung zu generieren und anschließend zu sortieren, können Sie eine Reihe von Stichproben aus der Normalverteilung in sortierter Reihenfolge generieren.
Sie können die Lösung hier verwenden , um n einheitliche Zufallszahlen in sortierter Reihenfolge zu generieren. Dann können Sie die inverse cdf (scipy.stats.norm.ppf) der Normalverteilung verwenden, um die einheitlichen Zufallszahlen durch inverse Transformationsabtastung in Zahlen aus der Normalverteilung umzuwandeln .
Wenn Sie Ihre Hände schmutziger machen möchten, können Sie die vielen inversen cdf-Berechnungen möglicherweise beschleunigen, indem Sie eine iterative Methode verwenden und das vorherige Ergebnis als erste Schätzung verwenden. Da die Vermutungen sehr nahe beieinander liegen werden, erhalten Sie mit einer einzigen Iteration wahrscheinlich eine große Genauigkeit.
quelle
Probieren Sie diese wechselnde Guvante-Lösung mit dieser Main () aus. Sie beginnt zu sortieren, sobald das 1/4 IO-Lesen abgeschlossen ist. In meinem Test ist sie schneller:
quelle
Da Sie die Verteilung kennen, wäre meine Idee, k Buckets mit jeweils der gleichen erwarteten Anzahl von Elementen zu erstellen (da Sie die Verteilung kennen, können Sie diese berechnen). Fegen Sie dann in O (n) -Zeit das Array und legen Sie die Elemente in ihre Eimer.
Sortieren Sie dann gleichzeitig die Eimer. Angenommen, Sie haben k Eimer und n Elemente. Das Sortieren eines Eimers dauert (n / k) lg (n / k). Angenommen, Sie haben p Prozessoren, die Sie verwenden können. Da Eimer unabhängig voneinander sortiert werden können, müssen Sie einen Multiplikator für die Obergrenze (k / p) festlegen. Dies ergibt eine endgültige Laufzeit von n + ceil (k / p) * (n / k) lg (n / k), die viel schneller sein sollte als n lg n, wenn Sie k gut wählen.
quelle
std::sort()
, aber es ist viel langsamer als Alexandru's Radixsort-Lösung.Eine einfache Optimierungsidee besteht darin, zwei Double in ein SSE-Register einzufügen, sodass jeder Thread mit zwei Elementen gleichzeitig arbeiten würde. Dies kann für einige Algorithmen kompliziert sein.
Sie können das Array auch in cachefreundliche Blöcke sortieren und die Ergebnisse dann zusammenführen. Es sollten zwei Ebenen verwendet werden: zum Beispiel zuerst 4 KB für L1 und dann 64 KB für L2.
Dies sollte sehr cachefreundlich sein, da die Bucket-Sortierung nicht über den Cache hinausgeht und die endgültige Zusammenführung den Speicher sequentiell durchläuft.
Heutzutage ist die Berechnung viel billiger als Speicherzugriffe. Wir haben jedoch eine große Anzahl von Elementen, sodass es schwierig ist, die Arraygröße zu bestimmen, wenn die dumme cachebewusste Sortierung langsamer ist als eine nicht cachebewusste Version mit geringer Komplexität.
Aber ich werde keine Implementierung des oben genannten bereitstellen, da ich es in Windows (VC ++) tun würde.
quelle
Hier ist eine lineare Scan-Bucket-Sortierung. Ich denke, es ist schneller als alle aktuellen Single-Thread-Implementierungen mit Ausnahme der Radix-Sortierung. Die erwartete Laufzeit sollte linear sein, wenn ich die PDF-Datei genau genug einschätze (ich verwende die lineare Interpolation von Werten, die ich im Web gefunden habe) und keine Fehler gemacht habe, die zu übermäßigem Scannen führen würden:
quelle
Ich weiß nicht, warum ich meinen vorherigen Beitrag nicht bearbeiten kann. Deshalb gibt es hier eine neue Version, die 0,2 Sekunden schneller ist (aber ungefähr 1,5 Sekunden schneller in der CPU-Zeit (Benutzer)). Diese Lösung hat 2 Programme, berechnet zuerst Quantile für die Normalverteilung für die Bucket-Sortierung vor und speichert sie in einer Tabelle, t [double * scale] = Bucket-Index, wobei scale eine willkürliche Zahl ist, die das Casting auf double ermöglicht. Das Hauptprogramm kann diese Daten dann verwenden, um Doppelsätze in den richtigen Eimer zu legen. Es hat einen Nachteil: Wenn die Daten nicht gaußsch sind, funktionieren sie nicht richtig (und es gibt auch fast keine Chance, dass sie bei normaler Verteilung falsch funktionieren), aber die Änderung für Sonderfälle ist einfach und schnell (nur die Anzahl der Buckets wird überprüft und fällt auf std.) ::Sortieren()).
Kompilieren: g ++ => Hilfsprogramm http://pastebin.com/WG7pZEzH
g ++ -std = c ++ 0x -O3 -march = native -pthread => http://pastebin.com/T3yzViZP Hauptsortierprogramm
quelle
Hier ist eine andere sequentielle Lösung. Dieser nutzt die Tatsache, dass die Elemente normalverteilt sind, und ich denke, die Idee ist allgemein anwendbar, um eine Sortierung nahe der linearen Zeit zu erreichen.
Der Algorithmus sieht folgendermaßen aus:
phi()
Funktion in der Implementierung)size * phi(x)
Leider ist die versteckte Konstante ziemlich groß und diese Lösung ist doppelt so langsam wie der Radix-Sortieralgorithmus.
quelle
Mein persönlicher Favorit unter Verwendung der Threaded Building Blocks von Intel wurde bereits veröffentlicht, aber hier ist eine einfache parallele Lösung unter Verwendung von JDK 7 und seiner neuen Fork / Join-API:
Wichtiger Haftungsausschluss : Ich habe die Quick-Sort-Anpassung für fork / join von https://github.com/pmbauer/parallel/tree/master/src/main/java/pmbauer/parallel übernommen
Um dies auszuführen, benötigen Sie eine Betaversion von JDK 7 (http://jdk7.java.net/download.html).
Auf meinem 2,93 GHz Quad Core i7 (OS X):
Python-Referenz
Java JDK 7 Fork / Join
Ich habe auch versucht, mit parallelem Lesen und Konvertieren der Bytes in Doppelbytes zu experimentieren, aber ich habe dort keinen Unterschied festgestellt.
Aktualisieren:
Wenn jemand mit dem parallelen Laden der Daten experimentieren möchte, finden Sie unten die Version zum parallelen Laden. Theoretisch könnte dies dazu führen, dass es noch ein bisschen schneller geht, wenn Ihr IO-Device über genügend parallele Kapazität verfügt (SSDs normalerweise). Das Erstellen von Doubles aus Bytes ist außerdem mit einem gewissen Mehraufwand verbunden, sodass dies möglicherweise auch parallel schneller gehen kann. Auf meinen Systemen (Ubuntu 10.10 / Nehalem Quad / Intel X25M SSD und OS X 10.6 / i7 Quad / Samsung SSD) habe ich keinen wirklichen Unterschied festgestellt.
Update2:
Ich habe den Code auf einem unserer 12-Kern-Entwicklungscomputer mit einer geringfügigen Änderung ausgeführt, um eine feste Anzahl von Kernen festzulegen. Dies ergab die folgenden Ergebnisse:
Auf diesem System habe ich auch die Python-Version mit 1m2.994s und die C ++ - Version von Zjarek mit 1.925s ausprobiert (aus irgendeinem Grund scheint die C ++ - Version von Zjarek auf dem Computer von static_rtti relativ schneller zu laufen).
Ich habe auch versucht, was passiert ist, wenn ich die Dateigröße auf 100.000.000 verdoppelt habe:
In diesem Fall hat Zjareks C ++ - Version 3.968s gedauert. Python hat hier einfach zu lange gedauert.
150.000.000 Doppelbetten:
In diesem Fall war Zjareks C ++ - Version 6.044s. Ich habe es nicht einmal mit Python versucht.
Die C ++ - Version ist sehr konsistent mit ihren Ergebnissen, bei denen Java ein wenig schwankt. Zuerst wird es ein bisschen effizienter, wenn das Problem größer wird, aber dann wieder weniger effizient.
quelle
Eine Version mit traditionellen pthreads. Code zum Zusammenführen aus Guvantes Antwort kopiert. Kompilieren mit
g++ -O3 -pthread
.Auf meinem Laptop erhalte ich folgende Ergebnisse:
quelle
Hier ist eine sequentielle C99-Implementierung, die versucht, die bekannte Distribution wirklich zu nutzen. Grundsätzlich wird eine einzelne Runde der Bucket-Sortierung anhand der Verteilungsinformationen durchgeführt. Anschließend werden einige Runden der Quick-Sortierung für jeden Bucket durchgeführt, wobei eine gleichmäßige Verteilung innerhalb der Grenzen des Bucket und schließlich eine geänderte Auswahlsortierung vorausgesetzt werden, um die Daten zurück in den ursprünglichen Puffer zu kopieren. Die Schnellsortierung speichert die Teilungspunkte, sodass die Auswahlsortierung nur für kleine Gruppen ausgeführt werden muss. Und trotz all dieser Komplexität (wegen?) Ist es nicht einmal wirklich schnell.
Um Φ schnell auswerten zu können, werden die Werte in wenigen Punkten abgetastet und später wird nur eine lineare Interpolation verwendet. Es ist eigentlich egal, ob Φ genau ausgewertet wird, solange die Approximation streng monoton ist.
Die Behältergrößen werden so gewählt, dass die Wahrscheinlichkeit eines Behälterüberlaufs vernachlässigbar ist. Genauer gesagt beträgt die Wahrscheinlichkeit, dass ein Datensatz mit 50000000 Elementen einen Bin-Überlauf verursacht, bei den aktuellen Parametern 3,65e-09. (Dies kann mit der Überlebensfunktion der Poisson-Verteilung berechnet werden .)
Zum Kompilieren bitte verwenden
Da es erheblich mehr Berechnungen gibt als in den anderen Lösungen, werden diese Compiler-Flags benötigt, um es zumindest einigermaßen schnell zu machen. Ohne
-msse3
die Konvertierungen von werden wirklich langsam. Wenn Ihre Architektur SSE3 nicht unterstützt, können diese Konvertierungen auch mit der Funktion durchgeführt werden.double
int
lrint()
Der Code ist ziemlich hässlich - ich bin mir nicht sicher, ob dies die Anforderung erfüllt, "einigermaßen lesbar" zu sein ...
quelle
Dies verwendet erf (), um jedes Element entsprechend in eine Bin zu platzieren, und sortiert dann jede Bin. Es hält das Array vollständig an Ort und Stelle.
Erster Durchgang: docensus () zählt die Anzahl der Elemente in jeder Bin.
Zweiter Durchgang: partition () durchläuft das Array und platziert jedes Element in seinem richtigen Bin
Dritter Durchgang: sortbins () führt eine Q-Sortierung für jeden Bin durch.
Es ist ein bisschen naiv und ruft die teure erf () - Funktion zweimal für jeden Wert auf. Der erste und dritte Durchgang sind möglicherweise parallelisierbar. Die zweite ist sehr seriell und wird wahrscheinlich durch ihre sehr zufälligen Speicherzugriffsmuster verlangsamt. Abhängig vom Verhältnis von CPU-Leistung zu Speichergeschwindigkeit kann es auch sinnvoll sein, die Bin-Nummern der einzelnen Double zwischenzuspeichern.
Mit diesem Programm können Sie die Anzahl der zu verwendenden Fächer auswählen. Fügen Sie einfach eine zweite Zahl in die Befehlszeile ein. Ich habe es mit gcc -O3 kompiliert, aber meine Maschine ist so schwach, dass ich Ihnen keine guten Leistungszahlen nennen kann.
Bearbeiten: Poof! Mein C-Programm hat sich mit std :: sort auf magische Weise in ein C ++ - Programm verwandelt!
quelle
Schauen Sie sich die radix sort Implementierung von Michael Herf ( Radix Tricks ) an. Auf meiner Maschine war die Sortierung im Vergleich zum
std::sort
Algorithmus in meiner ersten Antwort fünfmal schneller . Der Name der Sortierfunktion lautetRadixSort11
.quelle