7. Parallele Algorithmen für FPGA-Implementierungen · Odd-Even-Transposition-Sort für 8...

28
7. Parallele Algorithmen für FPGA-Implementierungen

Transcript of 7. Parallele Algorithmen für FPGA-Implementierungen · Odd-Even-Transposition-Sort für 8...

7. Parallele Algorithmen für FPGA-Implementierungen

Odd-Even-Transposition-Sort für 8 Elemente

Ein sinnvolles Werkzeug bei der Entwicklung und dem Beweis von Sortierverfahren ist das 0-

1-Pinzip.

0-1-Prinzip (Knuth, 1973)

Ein Sortiernetz sortiert genau dann eine Folge beliebiger Daten, wenn es alle Folgen von

Nullen und Einsen sortiert.

Beweis:

Sei S ein Sortiernetz, das alle Folgen von Nullen und Einsen sortiert, aber eine Folge x0, x1,

...,xN-1 nicht sortiert. Der zu S gehörende Algorithmus werde mit AS bezeichnet. Sei π die

Permutation, die die sortierte Folge repräsentiert (xπ(0) ≤ xπ(1) ≤ xπ(2) ... ≤ xπ(N-1) und seiσ die

Permutation, die AS produziert. xσ(0), xσ(1), xσ(2) ..., xσ(N-1) . Sei k der kleinste Index, für den

xπ(k) ≠ xσ(k). Also xπ(i) = xσ(i) für alle i<k und xπ(k) < xσ(k). Also gibt es ein r > k mit xπ(k) = xσ(r).

Wir definieren eine Folge x*i aus Nullen und Einsen:

x* i = 0, falls xi ≤ xπ(k) und x*i = 1, falls xi > xπ(k)

Was macht AS mit der Folge x*i , 0 ≤ i ≤ N-1? Es gilt: xi > xj => x*i ≥ x* j für alle i und j. Daher

macht AS in (x* i) genau dann eine Vertauschung, wenn er eine in (xi) macht. Daher ist die

Ausgabe die Folge x*σ(0), x*σ(1), x*σ(2) ..., x*σ(N) = xσ(0), xσ(1), xσ(2),..., xσ(k-1), xσ(k), ..., xσ(r), ...,

xσ(N) . Aber xσ(k) ist 1 während xσ(r) 0 ist. Die Ergebnisfolge ist also nicht sortiert, d.h. die

Annahme einer Folge (xi) wie oben war falsch.

Das 0-1-Pinzip ist nützlich zum Beweis von Sortierverfahren.

1. Anstelle der möglichen n! Eingangskombinationen muß man nur 2n überprüfen.

2. Durch Darstellung der Nullen in Weiß und der Einsen in Schwarz lassen sich

Sortierverfahren einfach graphisch beweisen.

Merge Verfahren

Als wir über Sortieren gesprochen haben, ist das Verfahren Merge-Sort als ein Verfahren genannt worden, dessen (Zeit-) Komplexität optimal ist. Und zwar im Gegensatz zu Quicksortbenötigt Merge-Sort im Durchschnittsfall und im schlechtesten Fall O(n*log n) Schritte.

Wir wollen uns jetzt das Verhalten der uns mittlerweile bekannten Parallelrechnermodelle am Beispiel des Verschmelzen (Merge) zweier sortierter Listen ansehen.

Merge auf einem shared memory Parallelrechner

Wir wollen eine Parallelrechner mit n Prozessoren verwenden, um zwei sortierte Listen der Länge n/2 zu einer sortierten Liste der Länge n zu verschmelzen. Der Gedanke des Algorithmus besteht darin, daß jeder Prozessor für ein Datum verantwortlich ist, und für dieses die Zielposition in der sortierten Liste berechnet. Dazu macht er eine binäre Suche in der jeweils anderen Liste, wodurch er die Anzahl der kleineren Elemente in der anderen Liste kennt. Andererseits kennt er auch aus der originalen Position seines Datums in der eigenen Liste die Anzahl der kleineren Elemente aus der eigenen Liste. Die Summe ist also die Gesamtanzahl der kleineren Elemente und damit die Zielposition.

Algorithmus MERGE_ParallelEingabe: Zwei sortierte Listen A[0 .. n/2-1] und A[n/2 .. N-1] im globalen Speicher.Ausgabe: Eine sortierte Liste B[0 .. N-1] mit den Elementen von A im globalen Speicher.Methode:Global A[0..n-1], B[0..n-1];Local x, low, high, index;for_all P i , where 0 ≤ i ≤ n-1 pardo

if i < n/2 then low:=n/2; high:=n-1else low:=0; high:=n/2-1

endif;x := A[i]; {jeder Prozessor holt sich sein A[i]}repeat

index := (low+high)/2 ;if x ≤ A[index] then high:= index-1

else low:= index+1endif;

until low > high;{ jetzt schreibt jeder Prozessor den Wert an die St elle, andie er gehört }B[high+i-n/2+1] := x

endfor_all;

Beispiel:

n = 16

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15A = 1, 3, 7, 11, 12, 14, 15, 22, 4, 5, 6,10, 1 3, 16, 18, 21

P0: low = 8

high = 15 10 8 7 B[7+0-8+1] = B[0] := 1

index= 11 9 8x = 1

P6: low = 8 12 13

high = 15 12 B[12+6-8+1] = B[11] := 15

index= 11 13 12x = 15

P11: low = 0 2 3

high = 7 2 B[2+11-8+1] = B[6] := 10

index= 3 1 2x = 10

Komplexität:Jeder Prozessor macht zunächst eine Operation konstanter Länge und läuft dann durch die repeat-Schleife. Mit jedem Durchlauf wird der Suchraum halbiert, also sind es insgesamt log n Durchläufe. Die Zuweisung an B ist wieder konstant. Dabei ist zu beachten, daß jeder Prozessor auf eine andere Speicherstelle im gemeinsamen Speicher zugreift. Wenn in den Listen mehrmals derselbe Wert vorkommt, muß man beim Vergleich eine Vereinbarung treffen, z.B. der Wert der linken Liste wird als kleiner gewertet als derjenige der rechten Liste.

Also Gesamtkomplexität:

O(log n)

Bitonisches Mergen

Das Sortierverfahren Bitonic Sort (Batcher, 1968) [Ba, Kn, CLR] ist eines der schnellsten Sortiernetze. Bei einem Sortiernetz ist die Reihenfolge der Vergleiche nichtvon den Daten abhängig, anders als bei Sortierverfahren wie z.B. Quicksort oder Heapsort. Das Verfahren ist daher besonders für eine Implementierung in Hardwaregeeignet. Es ist darüber hinaus Grundlage vieler paralleler Sortierverfahren auf zweidimensionalen Gittern [TK, LSSS, SS].

Im folgenden wird das Sortiernetz Bitonic Sort auf Basis des 0-1-Prinzips entwickelt.

Grundlagen

Def.:Eine 0-1-Folge a = a0, ..., an-1 heißt bitonisch, wenn sie höchstens zwei Wechsel zwischen 0 und 1 enthält, d.h. wenn Indexpositionen k, m ∈{0,1, ..., n-1} existieren mita0, ..., ak-1 = 0 , ak, ..., am-1 = 1 , am, ..., an-1 = 0 odera0, ..., ak-1 = 1 , ak, ..., am-1 = 0 , am, ..., an-1 = 1

Im folgenden Bild sind verschiedene Beispiele bitonischer 0-1-Folgen schematisch dargestellt (Nullen weiß, Einsen schattiert).

0 k-1 k m-1 m n-1

0 k-1 k m-1 m n-1

0=k m-1 m n-1

0=k m-1 m n-1

0=k n-1, m=n

0=k n-1, m=n

Def.:Sei n ∈ N , n gerade. Das Vergleichernetz Bn ist wie folgt definiert:Bn = [0 : n/2] [1 : n/2+1] ... [n/2-1 : n-1]

Beispiel:Vergleichernetz B8 :

01234567

Satz:

Sei n ∈ N , n gerade und a = a0, ..., an-1 eine bitonische Folge. Die

Anwendung des Vergleichernetzes Bn auf a ergibt dann

Bn(a) = b0, ..., bn/2-1 c0, ..., cn/2-1

wobei die bi die kleineren Elemente und die cj die größeren Elemente sind,

d.h. bi ≤ cj für alle i, j ∈ {0, ..., n/2-1}

und darüber hinaus gilt

b0, ..., bn/2-1 ist bitonische Folge und

c0, ..., cn/2-1 ist bitonische Folge.

Beweis: Sei a0, ..., an-1 eine bitonische 0-1-Folge. Schreibt man a in zwei Zeilen, dann

ergibt sich folgendes Bild (Nullen sind wieder weiß, Einsen schattiert dargestellt):

0 n/2-1

n/2 n-1

Die Folge beginnt mit Nullen, dann kommen Einsen und dann wieder Nullen. Oder die Folge beginnt mit Einsen, dann kommen Nullen und dann wieder Einsen, wobeisich die Bereiche der Einsen auch überlappen können:

Es sind noch eine ganze Reihe anderer Variationen möglich, einige sind im Bild unten dargestellt. Eine Anwendung des Vergleichernetzes Bn entspricht einem Vergleichzwischen oberer und unterer Zeile; hierdurch wird in allen Fällen die im Satz angegebene Form hergestellt, d.h. alle bi sind kleiner oder gleich allen cj und b ist bitonisch und c ist bitonisch:

ab

c

Ausganssituation Anwendung des EndsituationVergleichernetzes

ab

c

ab

c

ab

c

Sortieren

Beginnen wir wieder mit der PRAM. Wie können wir unser Merge-Verfahren nutzen, um zu sortieren?

Iterativ:

for i:=1 to log n dofor_all subsequences S of length 2 i pardo

sort S by merging two sorted halfs

Komplexität:Tsort(n) = Tsort(n/2) + Tmerge(n) Tsort(1) = 0Tmerge(n) = c * (log n)

Dieses Gleichungssystem hat die Lösung:Tsort(n) = c * (log n) + c * (log n/2) + c * (log n/4) + ... + c * 1 + 0

= c * ( (log n) + (log n - 1) + (log n - 2) + ... + 1)= c * (log n) * (log n +1)/2 = O (log2 n)

Geht das auch schneller?

Ja. Idee: Man benutzt n2 Prozessoren, um n Daten zu sortieren. Seien die zu sortierenden Daten im Feld A[0..n-1]. B[0..n-1] soll durch den Algorithmus die sortierte Folge bekommen.Wir stellen uns die Prozessoren in einer nxn-Matrixangeordnet vor. Alle Prozessoren in der i-ten Zeile bekommen das Element ai. Alle Prozessoren in der j-ten Spalte bekommen das Element aj. Nun vergleicht jeder Prozessor sein ai mit seinem aj. Wenn ai größer ist als aj, setzt er einen Wert g auf 1, sonst auf 0. In der i-ten Zeile ist jetzt aimit allen anderen Werten verglichen worden. Und in den g-Werten der i-ten Zeile stehen soviele Einsen, wie es kleinere Elemente als ai gibt. Nun werden diese g-Werte addiert und die Summe wird im Prozessor (i,0) gesammelt. Sei die Summe si. Schließlich schreibt Prozessor (i,0) den Wert ai nach B[si]. Das Feld B ist sortiert.

Komplexität:Das Verteilen der Elemente in den Zeilen und Spalten kostet für eineCREW PRAM Zeit O(1). Wir haben überigens in einer Übungsaufgabe gezeigt, wie es mit einer EREW PRAM in O(log n) geht.Das Vergleichen im Prozessor braucht ebenfalls O(1) Zeit.Das Addieren der g-Werte in den Zeilen dauert O(log n).Das Wegschreiben der Ergebnisse braucht wieder konstante Zeit O(1).Gesamtaufwand: O(log n)

Sortieren auf dem Mesh:

Zunächst geht natürlich wieder derselbe Trick wie beim Übergang vom Mergen zum Sortieren auf der PRAM.

Wir beginnen, eine Spalte des Mesh zu sortieren. Mit unserem Merge-Algorithmusmachen wir aus zwei sortierten Spalten eine sortierte Doppelspalte. Zwei davon Mergen wir zu einem sortierten Block aus vier Spalten usw. bis wir schließlich zwei sortierte Hälften des Mesh zu einem ganzen sortierten Mesh mergen.

Die Komplexität hierfür ist offenbar O(log n * √n).

Komplexitätsrekurrenz:

Tsort(n,k) = Tsort(n, k/2) + Tmerge(n,k)Tmerge(n,k) = n + 2kTsort(n,1) = n

Für ein Feld der Kantenlänge n ergibt sich:Tsort(n,n) = Tsort(n, n/2) + Tmerge(n,n) = Tsort(n, n/2) + (n+2n)= Tsort(n, n/4) +(n+2n) + (n+2n/2)= Tsort(n, n/8) +(n+2n) +(n+2n/2) +(n+2n/4)=...= Tsort(n, 1) +(n+2n) +(n+2n/2) +(n+2n/4)+…+(n+2n/(n/2))≤ n + n*logn + 4n = n*logn +5n

Die Komplexität für ein Feld der Kantenlänge √n ist also O(log n * √n).

Dieselbe Komplexität wird mit einem einfacheren Verfahren erreicht, daß den Satz von Batcher ausnutzt: Shear-Sort.

Algorithmus Shear-Sort

for i := 0 to log n - 1 dobeginsort_rows_alternating;sort_columnsend

sort_rows

Die Komplexität hierfür ist offenbar O(log n * √n).

Warum ist das ein Sortierverfahren? In jedem Durchlauf der Schleife wird die Anzahl der schmutzigen Zeilen halbiert, denn zwei aufeinanderfolgende Zeilen, eine nach rechts und eine nach links sortiert, bilden eine bitonische Folge. Durch das Sortieren der Spalten wird dabei eine Hälfte sauber, d.h. besteht nur aus 0en oder 1en.Nach log n Durchläufen ist also nur noch eine schmutzige Zeile vorhanden; diese wird im letzten Schritt sortiert.

Beispiel Shear-sort:

15 3 9 8 Zeilen 3 8 9 15 Spalte n 0 1 4 213 14 12 11 sortieren: 14 13 12 11 sortie ren: 3 7 5 6

4 1 6 0 0 1 4 6 10 8 9 11 10 2 7 5 10 7 5 2 14 13 12 15

Zeilen 0 1 2 4 Spalten 0 1 2 3sortieren: 7 6 5 3 sortieren: 7 6 5 4

8 9 10 11 8 9 10 11 15 14 13 12 15 14 13 12

Zeilen 0 1 2 3 sortieren: 4 5 6 7

8 9 10 1112 13 14 15

Geht das auch schneller?

Ja. Idee: Man führt das Merge-Verfahren zwei-dimensional aus, d.h. man „merged“ in einem Schritt vier Teilfelder der Größe n/2 x n/2 zu einem Feld der Größe n x n.

Algorithmus Merge_neu:

sort_rows_alternating;sort_columns;sort_rows_alternating;sort_columns;sort_rows

Jeder dieser Schritte kann mit Odd-even-transposition-sort in n Schritten ausgeführt werden. Auf der folgenden Folie sieht man die Wirkung dieses Algorithmus an einem mit 0en und 1en besetzten Feld.

Komplexität des Sortierens:

Für einen Merge Schritt (vier Felder der Größe k x k zu einem Feld der Größe 2k x 2k) benötigt man c * 2k Schritte. Die Felder der Größe 1 x 1 sind per se sortiert. Also gilt:

Tsort(√n) = Tsort((√n)/2) + Tmerge(√n)Tmerge(√n) = c * √nTsort(1) = 0

Dieses Gleichungssystem hat die Lösung

Tsort(√n) = Tmerge(√n) + Tmerge((√n)/2) + Tmerge((√n)/4) + ... + Tmerge(2) + 0= c * √n + c * (√n)/2 + c * (√n)/4 + ... + c * 1= c * (2 √n - 1) = O(√n)

Sortieren auf dem Hypercube: Sortiernetz Bitonic Sort

Was brauchen wir, um aus dem Bitonic Merge ein Sortiernetz zu machen? Wir müssen nur dafür sorgen, daß unsere Eingangsfolge zu einer bitonischen Folge umsortiert wird. Nun sind insbesondere solche Folgen bitonisch, die aus zwei gegenläufig sortierten Hälften bestehen. D. h. wir können aus der Ursprungsfolge eine bitonische Folge machen, indem wir die erste Hälfte aufsteigend und die zweite Hälfte absteigend sortieren.

Sort (n/2)(aufsteigend)

Sort (n/2)(absteigend)

Merge (n)

Merge(n/2)

Merge(n/2)

Merge(n/4)

Merge(n/4)

Merge(n/4)

Merge(n/4)

Merge (2)

Merge (2)

Merge (2)

Merge (2)

Bitonic Sort für n Elemente

Das Sortiernetz Bitonic Sort ist rekursiv aus Vergleichernetzen Bk für verschiedene Zweierpotenzen k aufgebaut. Dazu nimmt man in obiger Darstellung zum Sortieren der Folgen der Länge n/2 wieder ein bitonic-sort-Netz.

Man beginnt also mit allen Folgen der Länge 1. Diese sind per se sortiert. Je zwei solche Folgen bilden eine bitonische Folge der Länge 2. Diese wird durch einen Vergleich zu einer sortierten Folge der Länge zwei zusammengefaßt. Dabei wird jeweils die erste, dritte, fünfte,... aufsteigend, die zweite, vierte, sechste,... absteigendsortiert. Somit hat man lauter bitonische Folgen der Länge vier erzeugt. Durch ein bitonic-merge-Netz werden diese sortiert. Wieder geht man so vor, daß die erste, dritte, fünfte, ... Folge aufsteigend, die zweite, vierte, sechste,... absteigend sortiert werden. Auf diese Weise fährt man fort, bis man zwei gegenläufig sortierte Folgen der Länge n/2 hat. Diese bilden eine bitonische Folge der Länge n, die mit einem bitonic-merge-Netz sortiert wird.

Die folgende Folie zeigt ein bitonic-sort-Netz für n=8.

Bitonischer Sortierer für 8 Elemente

Komplexität

Um aus zwei (gegenläufig) sortierten Folgen der Länge n/2 eine sortierte Folge der Länge n herzustellen, sind log(n) Vergleicherstufen erforderlich (z.B. die 3 = log(8)Vergleicherstufen im Bild oben). Die Anzahl der Vergleicherstufen T(n) des gesamten Sortiernetzes ergibt sich also wie folgt:

T(n) = log(n) + T(n/2)

Ferner ist jede einelementige Folge natürlich sortiert, somit gilt

T(1) = 0

Die Lösung dieses Rekursionsgleichungssystems ist

T(n) = log(n) + log(n)-1 + log(n)-2 + ... + 1 = log(n) · (log(n)+1) / 2

Wir brauchen also O(log2 n) Vergleicherstufen.

Jede Vergleicherstufe des Sortiernetzes besteht aus n/2 Vergleichern; insgesamt sind dies also O(n · log2(n)) Vergleicher.