Ich löse ein Problem und es geht darum, 10 Zahlen (int32) sehr schnell zu sortieren. Meine Anwendung muss 10 Zahlen millionenfach so schnell wie möglich sortieren. Ich probiere einen Datensatz mit Milliarden von Elementen aus und jedes Mal muss ich 10 Zahlen daraus auswählen (vereinfacht) und sortieren (und aus der Liste der sortierten 10 Elemente Schlussfolgerungen ziehen).
Derzeit verwende ich die Einfügesortierung, aber ich kann mir vorstellen, dass ich einen sehr schnellen benutzerdefinierten Sortieralgorithmus für mein spezifisches Problem von 10 Zahlen implementieren könnte, der die Einfügungssortierung übertreffen würde.
Hat jemand eine Idee, wie man dieses Problem angeht?
algorithm
sorting
insertion-sort
sorting-network
bodacydo
quelle
quelle
if
Anweisungen sollte am besten funktionieren. Vermeiden Sie Schleifen.Antworten:
(Befolgen Sie den Vorschlag von HelloWorld, sich mit dem Sortieren von Netzwerken zu befassen.)
Es scheint, dass ein 29-Vergleichs- / Swap-Netzwerk der schnellste Weg ist, eine Sortierung mit 10 Eingängen durchzuführen. Ich habe das 1969 von Waksman entdeckte Netzwerk für dieses Beispiel in Javascript verwendet, das direkt in C übersetzt werden sollte, da es nur eine Liste von
if
Aussagen, Vergleichen und Swaps ist.Hier ist eine grafische Darstellung des Netzwerks, unterteilt in unabhängige Phasen. Um die Parallelverarbeitung zu nutzen, kann die 5-4-3-4-4-4-3-2-Gruppierung in eine 4-4-4-4-4-4-3-2-Gruppierung geändert werden.
quelle
#define SORTPAIR(data, i1, i2) if (data[i1] > data[i2]) { int swap = data[i1]... }
Wenn Sie mit dieser festen Größe arbeiten, schauen Sie sich Sorting Networks an . Diese Algorithmen haben eine feste Laufzeit und sind unabhängig von ihrer Eingabe. Für Ihren Anwendungsfall haben Sie keinen solchen Overhead, den einige Sortieralgorithmen haben.
Die bitonische Sortierung ist eine Implementierung eines solchen Netzwerks. Dieser funktioniert am besten mit len (n) <= 32 auf einer CPU. Bei größeren Eingängen könnte man sich vorstellen, auf eine GPU umzusteigen. https://en.wikipedia.org/wiki/Sorting_network
Übrigens, eine gute Seite zum Vergleichen von Sortieralgorithmen ist diese hier (obwohl die fehlt
bitonic sort
.http://www.sorting-algorithms.com
quelle
Verwenden Sie ein Sortiernetzwerk mit Vergleichen in 4er-Gruppen, damit Sie dies in SIMD-Registern tun können. Ein Paar gepackter Min / Max-Anweisungen implementiert eine gepackte Komparatorfunktion. Es tut mir leid, dass ich momentan keine Zeit habe, nach einer Seite zu suchen, von der ich mich erinnere, dass ich sie gesehen habe, aber hoffentlich wird die Suche in SIMD- oder SSE-Sortiernetzwerken etwas ergeben.
x86 SSE verfügt über gepackte 32-Bit-Integer-Min- und Max-Befehle für Vektoren mit vier 32-Bit-Ints. AVX2 (Haswell und höher) hat das gleiche, jedoch für 256b-Vektoren von 8 Zoll. Es gibt auch effiziente Shuffle-Anweisungen.
Wenn Sie viele unabhängige kleine Sortierungen haben, können möglicherweise 4 oder 8 Sortierungen parallel mit Vektoren durchgeführt werden. Esp. Wenn Sie Elemente nach dem Zufallsprinzip auswählen (damit die zu sortierenden Daten ohnehin nicht zusammenhängend im Speicher sind), können Sie das Mischen vermeiden und einfach in der gewünschten Reihenfolge vergleichen. 10 Register für alle Daten aus 4 (AVX2: 8) Listen mit 10 Ints lassen noch 6 Register für den Arbeitsbereich übrig.
Vektorsortiernetzwerke sind weniger effizient, wenn Sie auch zugehörige Daten sortieren müssen. In diesem Fall scheint der effizienteste Weg darin zu bestehen, einen gepackten Vergleich zu verwenden, um eine Maske zu erhalten, deren Elemente geändert wurden, und diese Maske zu verwenden, um Vektoren von (Verweisen auf) zugeordneten Daten zu mischen.
quelle
Was ist mit einer ungerollten, verzweigungslosen Auswahlsorte?
http://coliru.stacked-crooked.com/a/71e18bc4f7fa18c6
Die einzigen relevanten Zeilen sind die ersten beiden
#define
.Es verwendet zwei Listen und überprüft die erste zehnmal vollständig, was eine schlecht implementierte Auswahlsortierung wäre. Es werden jedoch Verzweigungen und Schleifen variabler Länge vermieden, die mit modernen Prozessoren und einem so kleinen Datensatz kompensiert werden können.
Benchmark
Ich habe einen Vergleich mit dem Sortiernetzwerk durchgeführt, und mein Code scheint langsamer zu sein. Ich habe jedoch versucht, das Abrollen und die Kopie zu entfernen. Ausführen dieses Codes:
Ich erhalte durchweg ein besseres Ergebnis für die verzweigungslose Auswahlsortierung im Vergleich zum Sortiernetzwerk.
quelle
for ( ; i<10; i++) (m > a[i]) && (m = a[i], indx = i );
ist außergewöhnlich gut optimiert. (Kurzschluss ist normalerweise eine Form der Verzweigung)std::shuffle
mitfor (int n = 0; n<10; n++) a[n]=g();
. Die Ausführungszeit halbiert sich und das Netzwerk ist jetzt schneller.std::sort
?std::sort
aber es lief so schlecht, dass ich es nicht einmal in den Benchmark aufgenommen habe. Ich denke, dass mit winzigen Datensätzen ein ziemlicher Overhead verbunden ist.Die Frage besagt nicht, dass dies eine Art webbasierte Anwendung ist. Das einzige, was mir aufgefallen ist, war:
Als Software- und Hardware-Ingenieur schreit mir das absolut "FPGA" zu. Ich weiß nicht, welche Art von Schlussfolgerungen Sie aus dem sortierten Satz von Zahlen ziehen müssen oder woher die Daten stammen, aber ich weiß, dass es fast trivial wäre, irgendwo zwischen hundert Millionen und einer Milliarde dieser "Sort-and-" zu verarbeiten. analysieren "Operationen pro Sekunde . Ich habe in der Vergangenheit FPGA-gestützte DNA-Sequenzierungsarbeiten durchgeführt. Es ist fast unmöglich, die enorme Rechenleistung von FPGAs zu übertreffen, wenn das Problem für diese Art von Lösung gut geeignet ist.
In gewisser Weise ist der einzige einschränkende Faktor, wie schnell Sie Daten in ein FPGA schaufeln und wie schnell Sie sie herausholen können.
Als Referenz habe ich einen Hochleistungs-Echtzeit-Bildprozessor entwickelt, der 32-Bit-RGB-Bilddaten mit einer Rate von etwa 300 Millionen Pixel pro Sekunde empfängt. Die Daten werden durch FIR-Filter, Matrixmultiplikatoren, Nachschlagetabellen, räumliche Kantenerkennungsblöcke und eine Reihe anderer Operationen gestreamt, bevor sie am anderen Ende herauskommen. All dies auf einem relativ kleinen Xilinx Virtex2-FPGA mit interner Taktung von etwa 33 MHz bis, wenn ich mich richtig erinnere, 400 MHz. Oh ja, es hatte auch eine DDR2-Controller-Implementierung und zwei Bänke mit DDR2-Speicher.
Ein FPGA kann bei jedem Taktübergang eine Art von zehn 32-Bit-Zahlen ausgeben, während es mit Hunderten von MHz arbeitet. Zu Beginn des Vorgangs würde es eine kurze Verzögerung geben, wenn die Daten die Verarbeitungspipeline (n) füllen. Danach sollten Sie in der Lage sein, ein Ergebnis pro Uhr zu erhalten. Oder mehr, wenn die Verarbeitung durch Replizieren der Sortier- und Analyse-Pipeline parallelisiert werden kann. Die Lösung ist im Prinzip fast trivial.
Der Punkt ist: Wenn die Anwendung nicht an einen PC gebunden ist und der Datenstrom und die Verarbeitung mit einer FPGA-Lösung (entweder eigenständig oder als Co-Prozessor-Karte in der Maschine) "kompatibel" sind, gibt es keine Möglichkeit in der Lage zu sein, das erreichbare Leistungsniveau mit Software zu übertreffen, die in einer beliebigen Sprache geschrieben ist, unabhängig vom Algorithmus.
BEARBEITEN:
Ich habe gerade eine schnelle Suche durchgeführt und ein Papier gefunden, das für Sie von Nutzen sein könnte. Es sieht so aus, als ob es aus dem Jahr 2012 stammt. Sie können heute (und sogar damals) eine VIEL bessere Leistung erzielen. Hier ist es:
Sortieren von Netzwerken auf FPGAs
quelle
Ich habe kürzlich eine kleine Klasse geschrieben , die den Bose-Nelson-Algorithmus verwendet, um beim Kompilieren ein Sortiernetzwerk zu generieren.
Es kann verwendet werden, um eine sehr schnelle Sortierung für 10 Zahlen zu erstellen.
Beachten Sie, dass
if (compare) swap
wir anstelle einer Anweisung explizit ternäre Operatoren für min und max codieren. Dies soll dem Compiler helfen, verzweigungslosen Code zu verwenden.Benchmarks
Die folgenden Benchmarks wurden mit clang -O3 kompiliert und auf meinem Macbook Air Mitte 2012 ausgeführt.
Zufällige Daten sortieren
Im Vergleich zum DarioP-Code ist hier die Anzahl der Millisekunden angegeben, die zum Sortieren von 1 Million 32-Bit-Int-Arrays der Größe 10 benötigt werden:
Hardcoded Sort Net 10: 88.774 ms
Templated Bose-Nelson sort 10: 27.815 ms
Mit diesem Vorlagenansatz können wir beim Kompilieren auch Sortiernetzwerke für eine andere Anzahl von Elementen generieren.
Zeit (in Millisekunden) zum Sortieren von 1 Million Arrays unterschiedlicher Größe.
Die Anzahl der Millisekunden für Arrays der Größe 2, 4, 8 beträgt 1,943, 8,655 bzw. 20,246.
Dank an Glenn Teitelbaum für die abgewickelte Einfügungssorte.
Hier sind die durchschnittlichen Uhren pro Sortierung für kleine Arrays mit 6 Elementen. Der Benchmark-Code und die Beispiele finden Sie bei dieser Frage:
Schnellste Art von 6-int-Array mit fester Länge
Es ist so schnell wie das schnellste Beispiel in der Frage für 6 Elemente.
Leistung zum Sortieren sortierter Daten
Oft sind die Eingabearrays bereits oder größtenteils sortiert.
In solchen Fällen kann die Einfügesortierung die bessere Wahl sein.
Abhängig von den Daten möchten Sie möglicherweise einen geeigneten Sortieralgorithmus auswählen.
Den für die Benchmarks verwendeten Code finden Sie hier .
quelle
v1 = v0 < v1 ? v1 : v0; // Max
kann sich noch verzweigen, in diesem Fall kann es durch ersetzt werden,v1 += v0 - t
denn wennt
esv0
dannv1 + v0 -t == v1 + v0 - v0 == v1
anderst
istv1
undv1 + v0 -t == v1 + v0 - v1 == v0
maxss
oder einerminss
Anweisung für moderne Compiler kompiliert. In Fällen, in denen dies nicht funktioniert, können andere Arten des Austauschs verwendet werden. :)Obwohl eine Netzwerksortierung gute Chancen hat, auf kleinen Arrays schnell zu sein, können Sie die Einfügesortierung manchmal nicht übertreffen, wenn sie richtig optimiert ist. Zum Beispiel Batch-Insert mit 2 Elementen:
quelle
in[y+2]= in[y];
, Tippfehler?Sie können sich vollständig abrollen
insertion sort
Um dies zu vereinfachen, können rekursive
template
s ohne Funktionsaufwand verwendet werden. Da es bereits ein isttemplate
,int
kann es auch eintemplate
Parameter sein. Dies macht es auch trivial, andere Array-Größen als 10 zu erstellen.Beachten Sie, dass zum Sortieren
int x[10]
des Aufrufsinsert_sort<int, 9>::sort(x);
die Klasse den Index des letzten Elements verwendet. Dies könnte verpackt werden, aber das wäre mehr Code zum Durchlesen.In meinen Tests war dies schneller als die Beispiele für das Sortiernetzwerk.
quelle
Aus ähnlichen Gründen wie den hier beschriebenen funktionieren die folgenden Sortierfunktionen
sort6_iterator()
undsort10_iterator_local()
sollten gut funktionieren , wenn das Sortiernetzwerk von hier übernommen wurde :Um diese Funktion aufzurufen, habe ich einen
std::vector
Iterator übergeben.quelle
Eine Einfügungssortierung erfordert durchschnittlich 29,6 Vergleiche, um 10 Eingaben mit einem besten Fall von 9 und einem schlechtesten von 45 zu sortieren (bei einer Eingabe in umgekehrter Reihenfolge).
Eine {9,6,1} Shellsort erfordert durchschnittlich 25,5 Vergleiche, um 10 Eingaben zu sortieren. Der beste Fall sind 14 Vergleiche, der schlechteste 34 und das Sortieren einer umgekehrten Eingabe erfordert 22.
Die Verwendung von Shellsort anstelle von Insertionssortierung reduziert den durchschnittlichen Fall um 14%. Obwohl der beste Fall um 56% erhöht wird, wird der schlechteste Fall um 24% reduziert, was bei Anwendungen von Bedeutung ist, bei denen es wichtig ist, die Leistung im schlechtesten Fall in Schach zu halten. Der umgekehrte Fall wird um 51% reduziert.
Da Sie mit der Einfügungssortierung vertraut zu sein scheinen, können Sie den Algorithmus als Sortiernetzwerk für {9,6} implementieren und anschließend die Einfügungssortierung ({1}) aktivieren:
quelle