Verteilte Algorithmen und Datenstrukturen Kapitel 6: Informationsorientierte Datenstrukturen Prof. Dr. Christian Scheideler SS 2012 12.07.2012 VADS - Kapitel 6 1 Informationsorientierte Datenstrukturen • dynamische Menge an Ressourcen (Prozesse) • dynamische Menge an Informationen (uniforme Datenobjekte) Beispiel: Operationen auf Prozessen: • Join(v): neuer Prozess v kommt hinzu • Leave(v): Prozess v verlässt das System Operationen auf Daten: abhängig von Datenstruktur 12.07.2012 VADS - Kapitel 6 2 Informationsorientierte Datenstrukturen Grundlegende Ziele: • Monotone Stabilisierung der Prozessstruktur (so dass monotone DS-Erreichbarkeit aus beliebigem schwachen Zusammenhang gewährleistet ist) • Lokale Konsistenz für Datenzugriffe Definition 6.1: Die Abarbeitung einer Folge von Operationen Op1,Op2,…,Opn auf den Daten heißt lokal konsistent, falls Op1,Op2,…,Opn in der vorgegebenen Reihenfolge vom System ausgeführt werden. Wir wollen sicherstellen, dass für jeden Knoten v des Systems die Operationsfolge von v auf den Daten lokal konsistent ausgeführt wird. Das erlaubt es allerdings, dass die Operationsfolgen der Knoten beliebig ineinander verzahnt werden dürfen. 12.07.2012 VADS - Kapitel 6 3 Informationsorientierte Datenstrukturen Beispiel: v Insert(x,A), Search(x), Search(y) Datenstruktur w Insert(y,B), Insert(x,C), Delete(x) Eine lokal konsistente Ausgabe für v wäre C, B, da diese durch Insert(x,A), Insert(y,B), Insert(x,C) Search(x), Search(y) zustande käme. SS 2011 VADS - Kapitel 5 4 Informationsorientierte Datenstrukturen Strategien für lokale Konsistenz: • Routingorientiert: stelle sicher, dass sich Anfragen derselben Quelle während des Routings nicht überholen können. • Quellorientiert: Quelle startet erst dann neue Anfrage, wenn all ihre vorigen Anfragen ausgeführt worden sind. 12.07.2012 VADS - Kapitel 6 5 Informationsorientierte Datenstrukturen Quellorientierte lokale Konsistenz: • Stelle für jede Datenanfrage zunächst (über Lookup) fest, mit welchen Prozessen Daten ausgetauscht werden. Das kann oft parallel geschehen. • Führe dann den Datenaustausch sequentiell in der vorgegebenen Reihenfolge aus. Anfragen : Suche 4 3 2 1 : Ausführung Zeit 12.07.2012 VADS - Kapitel 6 6 Übersicht • • • • • Verteilte Hashtabelle Verteilte Suchstruktur Verteilter Stack Verteilte Queue Verteilter Heap 12.07.2012 VADS - Kapitel 6 7 Wörterbuch 8 4 11 20 18 3 12.07.2012 VADS - Kapitel 6 8 Wörterbuch insert(15) 8 4 11 20 15 12.07.2012 18 3 VADS - Kapitel 6 9 Wörterbuch delete(20) 8 4 11 20 15 12.07.2012 18 3 VADS - Kapitel 6 10 Wörterbuch lookup(8) ergibt 8 8 4 15 12.07.2012 11 18 3 VADS - Kapitel 6 11 Wörterbuch lookup(7) ergibt ? 8 4 15 12.07.2012 11 18 3 VADS - Kapitel 6 12 Wörterbuch-Datenstruktur S: Menge von Elementen Jedes Element e identifiziert über key(e). Operationen: • S.insert(e: Element): S:=S∪{e} • S.delete(k: Key): S:=S\{e}, wobei e das Element ist mit key(e)=k • S.lookup(k: Key): Falls es ein e∈S gibt mit key(e)=k, dann gib e aus, sonst gib ⊥ aus Effiziente Lösung: Hashing 12.07.2012 VADS - Kapitel 6 13 Klassisches Hashing 1 3 5 10 14 19 zeit- und speichereffiziente Hashfunktion h:U→T 14 5 3 19 1 10 Hashtabelle T 12.07.2012 VADS - Kapitel 6 14 Klassisches Hashing 1 3 5 10 14 19 Aufgabe der Hashfunktion: Gute Streuung der Elemente in T 14 5 3 19 1 10 Hashtabelle T 12.07.2012 VADS - Kapitel 6 15 Klassisches Hashing 1 3 5 10 14 19 Gute Streuung der Elemente in T: insert, delete, lookup nur O(1) Zeit 14 5 3 19 1 10 Hashtabelle T 12.07.2012 VADS - Kapitel 6 16 Verteiltes Hashing Hashing auch für verteilte Speicher anwendbar: 1 3 5 10 14 19 Hashfunktion h Problem: Menge der Speichermedien verändert sich (Erweiterungen, Ausfälle,…) 12.07.2012 VADS - Kapitel 6 17 Verteiltes Wörterbuch Grundlegende Operationen: • insert(d): fügt Datum d mit Schlüssel key(d) ein (wodurch eventuell der alte zu key(d) gespeicherte Inhalt überschrieben wird) • delete(k): löscht Datum d mit key(d)=k • lookup(k): gibt Datum d zurück mit key(d)=k • join(v): Knoten (Speicher) v kommt hinzu • leave(v): Knoten v wird rausgenommen 12.07.2012 VADS - Kapitel 6 18 Verteiltes Wörterbuch Anforderungen: 1. Fairness: Jedes Speichermedium mit c% der Kapazität speichert (erwartet) c% der Daten. 2. Effizienz: Die Speicherstrategie sollte zeit- und speichereffizient sein. 3. Redundanz: Die Kopien eines Datums sollten unterschiedlichen Speichern zugeordnet sein. 4. Adaptivität: Für jede Kapazitätsveränderung von c% im System sollten nur O(c%) der Daten umverteilt werden, um 1.-3. zu bewahren. 12.07.2012 VADS - Kapitel 6 19 Verteiltes Wörterbuch Uniforme Speichersysteme: jeder Knoten (Speicher) hat dieselbe Kapazität. Nichtuniforme Speichersysteme: Kapazitäten können beliebig unterschiedlich sein Vorgestellte Strategien: • Uniforme Systeme: konsistentes Hashing • Nichtuniforme Speichersysteme: SHARE • Combine & Split 12.07.2012 VADS - Kapitel 6 20 Konsistentes Hashing Wähle zwei zufällige Hashfunktionen h, g d Daten g:U→[0,1) 0 1 h:V→[0,1) v Speicher Region, für die Speicher v zuständig ist 12.07.2012 VADS - Kapitel 6 21 Konsistentes Hashing • V: aktuelle Knotenmenge • succ(v): nächster Nachfolger von v in V bzgl. Hashfunktion h (wobei [0,1) als Kreis gesehen wird) • pred(v): nächster Vorgänger von v in V bzgl. Hashfunktion h Zuordnungsregeln: • Eine Kopie pro Datum: Jeder Knoten v speichert alle Daten d mit g(d)∈I(v) mit I(v)=[h(v), h(succ(v))). • k>1 Kopien pro Datum: speichere jedes Datum d im Knoten v oben und seinen k-1 nächsten Nachfolgern bzgl. h 12.07.2012 VADS - Kapitel 6 22 Verteilte Hashtabelle Fall 1: Server verwaltet Speicherknoten insert, delete, lookup, join, leave 12.07.2012 VADS - Kapitel 6 23 Verteilte Hashtabelle, Fall 1 Effiziente Datenstruktur für Server: • Verwende interne Hashtabelle T mit m=Θ(n) Positionen. • Jede Position T[i] mit i∈{0,…,m-1} ∈ ist für die Region R(i)=[i/m, (i+1)/m) in [0,1) zuständig und speichert alle Speicherknoten v mit I(v)∩R(i) ≠∅. 2 5 3 8 7 1 4 6 2 0 1 2,5 5,3 R(0) R(1) 12.07.2012 3,8,7 7,1 1,4 VADS - Kapitel 6 4,6 6,2 2 24 Verteilte Hashtabelle, Fall 1 Effiziente Datenstruktur für Server: • Jede Position T[i] mit i∈{0,…,m-1} ist für die Region R(i)=[i/m, (i+1)/m) in [0,1) zuständig und speichert alle Speicherknoten v mit I(v)∩R(i) ≠∅. • Lookup(k): ermittle das R(i), das g(k) enthält, und bestimme dasjenige v in T[i], dessen h(v) der nächste Vorgänger von g(k) ist. Dieses v ist dann verantwortlich für k und erhält die lookup Anfrage. 2,5 5,3 R(0) R(1) 12.07.2012 3,8,7 7,1 1,4 VADS - Kapitel 6 4,6 6,2 2 25 Verteilte Hashtabelle, Fall 1 Effiziente Datenstruktur für Server: • Jede Position T[i] mit i∈{0,…,m-1} ist für die Region R(i)=[i/m, (i+1)/m) in [0,1) zuständig und speichert alle Speicherknoten v mit I(v)∩R(i) ≠∅. • Insert(d): ermittle zunächst wie bei Lookup dasjenige v, das für d verantwortlich ist und leite dann Insert(d) an dieses v weiter. • Delete(k): analog 2,5 5,3 R(0) R(1) 12.07.2012 3,8,7 7,1 1,4 VADS - Kapitel 6 4,6 6,2 2 26 Verteilte Hashtabelle, Fall 1 Effiziente Datenstruktur für Server: • Verwende interne Hashtabelle T mit m=Θ(n) Positionen. • Jede Position T[i] mit i∈{0,…,m-1} ∈ ist für die Region R(i)=[i/m, (i+1)/m) in [0,1) zuständig und speichert alle Speicherknoten v mit I(v)∩R(i)≠∅. Einfach zu zeigen: • T[i] enthält für m≥n erwartet konstant viele Elemente und höchstens O(log n / log log n) mit hoher W.keit. • D.h. Lookup(k) (die Ermittlung des zuständigen Knotens für einen Schlüssel) benötigt erwartet konstante Zeit 12.07.2012 VADS - Kapitel 6 27 Verteilte Hashtabelle, Fall 1 Operationen: • join(v): ermittle Intervall für v (durch Zugriff auf T) und informiere Vorgänger von v, Daten, die nun v gehören, zu v zu leiten Beispiel: v=9 h(9) 2 5 3 8 7 1 4 6 2 0 1 2 5 3 8 7 1 0 12.07.2012 4 6 9 2 1 VADS - Kapitel 6 28 Verteilte Hashtabelle, Fall 1 Operationen: • leave(v): errechne über T Knoten w, der Intervall von v beerbt, und weise v an, alle Daten an w zu leiten Beispiel: v=8 2 5 3 8 7 1 4 6 2 0 1 2 5 3 7 1 0 12.07.2012 4 6 2 1 VADS - Kapitel 6 29 Verteilte Hashtabelle, Fall 1 Satz 6.2: • Konsistentes Hashing ist effizient und redundant. • Jeder Knoten speichert im Erwartungswert 1/n der Daten, d.h. konsistentes Hashing ist fair. • Bei Entfernung/Hinzufügung eines Speichers nur Umplatzierung von erwartungsgemäß 1/n der Daten Beweis: Effizienz und Redundanz: siehe Protokoll Fairness: • Für jede Wahl von h gilt, dass Σv∈V |I(v)| = 1 und damit Σv∈V E[|I(v)|] = 1 (E[]: Erwartungswert). • Weiterhin gibt es für jedes Paar v,w∈V eine Bijektion auf der Menge H der Hashfunktionen, f:H→H, so dass für alle h∈H gilt, fass |I(v)| bzgl. h = |I(w)| bzgl. f(h). Daraus folgt, dass E[|I(v)|] = E[|I(w)|]. • Die Kombinierung der beiden Gleichungen ergibt, dass E[|I(v)|] = 1/n für alle v∈V. 12.07.2012 VADS - Kapitel 6 30 Verteilte Hashtabelle, Fall 1 Satz 6.2: • Konsistentes Hashing ist effizient und redundant. • Jeder Knoten speichert im Erwartungswert 1/n der Daten, d.h. konsistentes Hashing ist fair. • Bei Entfernung/Hinzufügung eines Speichers nur Umplatzierung von erwartungsgemäß 1/n der Daten Problem: Schwankung um 1/n hoch! Lösung: verwende Θ(log n) viele Hashfunktionen für die Knoten (d.h. jeder Knoten hat Θ(log n) Punkte in [0,1) ). Aber stelle sicher, dass Redundanzforderung noch gilt! 12.07.2012 VADS - Kapitel 6 31 Verteilte Hashtabelle, Fall 1 Problem: Schwankung um 1/n hoch! Alternative Lösung: Anpassung des eigenen Wertes • Standard: x(v) = h(v) • Jetzt kontinuierliche Updates: x(pred(v)) + x(succ(v)) + h(v)/(c log n) x(v) = 2 + 1/(c log n) x(pred(v)) h(v) x(succ(v)) Tendenz zu h(v) x(v) Damit Ausgleich der Intervallgrößen, aber noch nicht analysiert! 12.07.2012 VADS - Kapitel 6 32 Verteilte Hashtabelle Fall 2: verteiltes System Jeder Knoten kann Anfragen (insert, delete, lookup, join, leave) generieren. 12.07.2012 VADS - Kapitel 6 33 Verteilte Hashtabelle, Fall 2 Konsistentes Hashing: Zerlegung in zwei Probleme. 1. Abbildung der Daten auf die Knoten 2. Vernetzung der Knoten 12.07.2012 VADS - Kapitel 6 34 Verteilte Hashtabelle, Fall 2 Vernetzung der Knoten: • Jedem Knoten v wird zufälliger Wert id(v)∈[0,1) zugewiesen. • Verwende dynamischen de Bruijn Graph, um Knoten (hier im Kreis!) zu vernetzen. 0 12.07.2012 1 VADS - Kapitel 6 35 Verteilte Hashtabelle, Fall 2 Abbildung der Daten auf die Knoten: • Verwende konsistentes Hashing 0 v 1 • insert(d): führe search(g(key(d))) im de Bruijn Graph aus und speichere d im nächsten reellen Vorgänger von g(key(d)) im de Bruijn Graph 12.07.2012 VADS - Kapitel 6 36 Verteilte Hashtabelle, Fall 2 Abbildung der Daten auf die Knoten: • Verwende konsistentes Hashing 0 v 1 • delete(k): führe search(g(k)) im de Bruijn Graph aus und lösche Datum d mit key(d)=k (falls da) im nächsten reellen Vorgänger von g(k) 12.07.2012 VADS - Kapitel 6 37 Verteilte Hashtabelle, Fall 2 Abbildung der Daten auf die Knoten: • Verwende konsistentes Hashing 0 v 1 • lookup(k): führe search(g(k)) im de Bruijn Graph aus und liefere Datum d mit key(d)=k (falls da) im nächsten reellen Vorgänger von g(k) zurück 12.07.2012 VADS - Kapitel 6 38 Verteilte Hashtabelle, Fall 2 Satz 6.3: Im stabilen Zustand ist der Zeitaufwand für die Operationen • Insert(d): erwartet O(log n) • Delete(k): erwartet O(log n) • Lookup(k): erwartet O(log n) • Join(v): O(1) (verbinde v mit beliebigem Knoten im de Bruijn Graph, Rest durch Build-deBruijn) • Leave(v): O(1) (verlasse de Bruijn Graph, Rest durch Build-deBruijn) Beweis: Folgt aus Analyse des de Bruijn Graphen Mögliche Verbesserungen: • im stabilen Zustand Join über Lookup realisieren, um Knoten gleich richtig zu platzieren • Jeden (virtuellen) Knoten mit den zwei nächsten Vorgängern und Nachfolgern im Ring verbinden, um Leave schneller zu reparieren 12.07.2012 VADS - Kapitel 6 39 Verteiltes Wörterbuch Uniforme Speichersysteme: jeder Knoten (Speicher) hat dieselbe Kapazität. Nichtuniforme Speichersysteme: Kapazitäten können beliebig unterschiedlich sein Vorgestellte Strategien: • Uniforme Systeme: konsistentes Hashing • Nichtuniforme Speichersysteme: SHARE • Combine & Split 12.07.2012 VADS - Kapitel 6 40 SHARE Situation hier: wir haben Knoten mit beliebigen relativen Kapazitäten c1,…,cn, d.h. ∑i ci = 1. Problem: konsistentes Hashing funktioniert nicht gut, da Knoten nicht einfach in virtuelle Knoten gleicher Kapazität aufgeteilt werden können. Lösung: SHARE 12.07.2012 VADS - Kapitel 6 41 SHARE Datenabbildung: wie bei konsistentem Hashing d Daten g:U→[0,1) 0 1 Zuordnung der Speicher: andere Strategie 1 12.07.2012 2 3 VADS - Kapitel 6 4 5 6 42 SHARE Zuordnung zu Speichern: zweistufiges Verfahren. 1. Stufe: Jedem Knoten v wird ein Intervall I(v)⊆[0,1) der Länge s⋅cv zugeordnet, wobei s=Θ(log n) ein fester Stretch-Faktor ist. Die Startpunkte der Intervalle sind durch eine Hashfunktion h:V→[0,1) gegeben. 0 1 1 12.07.2012 2 3 4 VADS - Kapitel 6 5 6 43 SHARE Zuordnung zu Speichern: zweistufiges Verfahren. 1. Stufe: Jedem Datum d wird mittels einer Hashfunktion g:U→[0,1) ein Punkt x∈[0,1) zugewiesen und die Multimenge Vx aller Knoten v bestimmt mit x∈I(v) (für |I(v)|>1 zählt v sooft wie I(v) x enthält). Vx={1,4} x 0 1 12.07.2012 2 3 4 VADS - Kapitel 6 1 5 6 44 SHARE Zuordnung zu Speichern: zweistufiges Verfahren. 2. Stufe: Für Datum d wird mittels konsistentem Hashing mit Hashfunktionen h’ und g’ (die für alle Punkte x∈[0,1) gleich sind) ermittelt, welcher Knoten in Vx Datum d speichert. Vx={1,4} x 0 1 12.07.2012 2 3 4 VADS - Kapitel 6 1 5 6 45 SHARE Realisierung: 1 2 4 3 insert, delete, lookup, join, leave und Kapazitätsveränderungen 12.07.2012 VADS - Kapitel 6 46 SHARE Effiziente Datenstruktur im Server: • 1. Stufe: verwende Hashtabelle wie für konsistentes Hashing, um alle möglichen Multimengen für alle Bereiche [i/m,(i+1)/m) zu speichern. • 2. Stufe: verwende separate Hashtabelle der Größe Θ(k) für jede mögliche Multimenge aus der 1. Stufe mit k Elementen (es gibt maximal 2n Multimengen, da es nur n Intervalle mit jeweils 2 Endpunkten gibt) Laufzeit: • 1. Stufe: O(1) erw. Zeit zur Bestimmung der Multimenge • 2. Stufe: O(1) erw. Zeit zur Bestimmung des Knotens 12.07.2012 VADS - Kapitel 6 47 SHARE Satz 6.4: 1. SHARE ist effizient. 2. Jeder Knoten i speichert im Erwartungswert ciAnteil der Daten, d.h. SHARE ist fair. 3. Bei jeder relativen Kapazitätsveränderung um c∈[0,1) ∈ nur Umplatzierung eines erwarteten c-Anteils der Daten notwendig Problem: Redundanz nicht einfach zu garantieren! Lösung: SPREAD (recht komplex) 12.07.2012 VADS - Kapitel 6 48 SHARE Beweis: Punkt 2: • s=Θ(log n): Da dann Σv∈V |I(v)|=s, ist die erwartete Anzahl Intervalle über jeden Punkt in [0,1) gleich s, und Abweichungen davon sind klein mit hoher W.keit falls s genügend groß. • Knoten i hat Intervall der Länge s⋅ci • Erwarteter Anteil Daten in Knoten i: (s⋅ci) ⋅ (1/s) = ci Phase 1 Phase 2 12.07.2012 VADS - Kapitel 6 49 SHARE Punkt 3: • Betrachte Kapazitätsveränderung von (c1,…,cn) nach (c’1,…,c’n) • Differenz: c = ∑i |ci-c’i| • Optimale Strategie, die Fairness sicherstellt: Mindestaufwand c/2 • SHARE: Intervalldifferenz ∑i |s(ci-c’i)| = s⋅c • Erwarteter Anteil umverteilter Daten: (s⋅c) / s = c, also maximal doppelt so viel wie optimal 12.07.2012 VADS - Kapitel 6 50 SHARE Gibt es auch eine effiziente verteilte Variante? Jeder Knoten kann Anfragen (insert, delete, lookup, join, leave) generieren und Kapazität beliebig verändern. 12.07.2012 VADS - Kapitel 6 51 Verteiltes Wörterbuch Uniforme Speichersysteme: jeder Knoten (Speicher) hat dieselbe Kapazität. Nichtuniforme Speichersysteme: Kapazitäten können beliebig unterschiedlich sein Vorgestellte Strategien: • Uniforme Systeme: konsistentes Hashing • Nichtuniforme Speichersysteme: SHARE • Combine & Split 12.07.2012 VADS - Kapitel 6 52 Verteilte Hashtabelle Probleme bei vielen Anfragen auf dasselbe Datum: Subjekt, das Datum speichert, wird überlastet. Lösung: Combine & Split 12.07.2012 VADS - Kapitel 6 53 Verteilte Hashtabelle Combine & Split: • Treffen sich zwei lookup Anfragen zu demselben Datum, werden sie zu einer kombiniert, das als neues Rückantwortsziel den Knoten v wählt, in dem die Anfragen kombiniert worden sind. v merkt sich dann die kombinierten Anfragen, so dass diese bei Erhalt des Datums bedient werden können. x Anfragen x x x x x Antworten x x 12.07.2012 … VADS - Kapitel 6 54 Verteilte Hashtabelle Combine & Split: • Treffen sich zwei insert oder delete Anfragen zu demselben Datum, werden sie zu einer kombiniert. Bei insert gewinnt dabei eine beliebige der beiden Anfragen. x x … x x Beobachtung: Mit der Combine & Split Regel ist die Congestion in der Größenordnung der Congestion, falls nur Anfragen auf verschiedene Daten unterwegs sind. 12.07.2012 VADS - Kapitel 6 55 Übersicht • • • • • Verteilte Hashtabelle Verteilte Suchstruktur Verteilter Stack Verteilte Queue Verteilter Heap 12.07.2012 VADS - Kapitel 6 56 Präfix Suche • Alle Schlüssel kodiert als binäre Folgen {0,1}W • Präfix eines Schlüssels x∈{0,1}W: eine beliebige Teilfolge von x, die mit dem ersten Bit von x startet (z.B. ist 101 ein Präfix von 10110100) Problem: finde für einen Schlüssel x∈{0,1}W einen Schlüssel y∈S mit größtem gemeinsamen Präfix Lösung: Trie Hashing 12.07.2012 VADS - Kapitel 6 57 Trie Ein Trie ist ein Suchbaum über einem Alphabet Σ, der die folgenden Eigenschaften erfüllt: • Jede Baumkante hat einen Label c∈Σ • Jeder Schlüssel x∈Σk ist von der Wurzel des Tries über den eindeutigen Pfad der Länge k zu erreichen, dessen Kantenlabel zusammen x ergeben. Hier: alle Schlüssel aus {0,1}W Beispiel: (0,2,3,5,6) mit W=3 ergibt (000,010,011,101,110) 12.07.2012 VADS - Kapitel 6 58 Trie Trie aus Beispielzahlen: 0 0 0 1 0 0 000 010 12.07.2012 1 1 1 1 011 101 VADS - Kapitel 6 0 110 59 Trie Präfixsuche für Zahl 4 (4 entspricht 100): 0 0 1 0 1 0 0 000 010 1 1 1 011 101 0 110 Ausgabe: 5 (größter gem. Präfix) 12.07.2012 VADS - Kapitel 6 60 Trie Insert(1) (1 entspricht 001): 0 0 0 000 12.07.2012 1 001 1 0 1 0 010 1 1 1 011 101 VADS - Kapitel 6 0 110 61 Trie Delete(5): 0 0 0 000 12.07.2012 1 001 1 0 1 0 010 1 1 1 011 101 VADS - Kapitel 6 0 110 62 Patricia Trie Probleme: • Präfixsuche im Trie mit Schlüsseln aus x∈{0,1}W kann Θ(W) Zeit dauern. • Insert und delete benötigen bis zu Θ(W) Restrukturierungen Verbesserung: verwende Patricia Tries Ein Patricia Trie ist ein Trie, in dem alle Knotenketten (d.h. Folgen von Knoten mit Grad 1) zu einer Kante verschmolzen werden. 12.07.2012 VADS - Kapitel 6 63 Trie Trie aus Beispielzahlen: 0 0 0 1 0 0 000 010 12.07.2012 1 1 1 1 011 101 VADS - Kapitel 6 0 110 64 Patricia Trie Jeder Baumknoten hat Grad 2. 0 1 1 00 01 0 000 12.07.2012 010 10 1 011 VADS - Kapitel 6 101 110 65 Patricia Trie Search(4): 0 1 1 00 01 0 000 12.07.2012 010 10 1 011 VADS - Kapitel 6 101 110 66 Patricia Trie Insert(1): 0 0 1 1 00 0 000 12.07.2012 01 1 001 0 010 10 1 011 VADS - Kapitel 6 101 110 67 Patricia Trie Delete(5): 0 0 1 110 1 01 0 000 12.07.2012 1 001 0 010 10 1 011 VADS - Kapitel 6 101 110 68 Patricia Trie • Search, insert und delete wie im einfachen binären Suchbaum, nur dass wir Labels an den Kanten haben. • Suchzeit immer noch O(W), aber Restrukturierungsaufwand nur noch O(1) Idee: Um Suchzeit zu verringern, hashen wir Patricia Trie auf [0,1) (damit konsistentes Hashing oder SHARE anwendbar). 12.07.2012 VADS - Kapitel 6 69 Patricia Trie Hashing auf [0,1): • Knotenlabel: Konkatenation der Kantenlabel von Wurzel • Jeder Knoten wird gemäß seines Knotenlabels in [0,1) gehasht. 0 10 010 g(010) 0 1 • Dann kann auf jeden Patricia Knoten mit einer DHT-Anfrage zugegriffen werden, falls sein Label bekannt. 12.07.2012 VADS - Kapitel 6 70 Patricia Trie Hashing auf [0,1): • Knotenlabel: Konkatenation der Kantenlabel von Wurzel • Jeder Knoten wird gemäß seines Knotenlabels in [0,1) gehasht. 0 1 Problem: Binäre Suche über Knotenlabel wäre dann denkbar für Zeitreduktion O(W) → O(log W), aber noch nicht machbar. 12.07.2012 VADS - Kapitel 6 71 Patricia Trie Hashing auf [0,1): • Knotenlabel: Konkatenation der Kantenlabel von Wurzel • Jeder Knoten wird gemäß seines Knotenlabels in [0,1) gehasht. 0 1 Lösung: Füge geeignete Stützknoten ein (msd-Knoten). 12.07.2012 VADS - Kapitel 6 72 Trie Hashing • Für eine Bitfolge x sei |x| die Länge von x. • Für einen Baumknoten v sei b(v) die konkatenierte Bitfolge der Kanten des eindeutigen Weges von der Wurzel zu v (d.h. der gem. Präfix aller Elemente unter v). • msd(f,f´) für zwei Bitfolgen 0*◦f und 0*◦f´: höchstes Bit, in dem sich 0*◦f und 0*◦f´ unterscheiden (0*=0000…). • Betrachte eine Bitfolge b mit Binärdarstellung von |b| gleich (xk,…,x0) . Sei b’ ein Präfix von b. Die msd-Folge m(b’,b) von b’ und b ist das Präfix von b der Länge ∑i=jk xi 2i mit j=msd(|b|,|b’|). Beispiel: Betrachte b=01101001010 und b’=011010. Dann ist |b|=11 oder binär 1011 und |b’|=6 oder binär 110, d.h. msd(|b|,|b’|)=3. Also ist von m(b’,b)= 01101001. 12.07.2012 VADS - Kapitel 6 73 Trie Hashing Zentrale Idee: Wir ersetzen jede Kante e={v,w} im Patricia Trie durch zwei Kanten {v,u} und {u,w} mit b(u)=m(b(v),b(w)) und bilden resultierenden Patricia Trie auf [0,1) ab. v v p w 12.07.2012 p1 u p1 ∘ p2 = p p2 msd-Knoten von v und w VADS - Kapitel 6 w 74 Trie Hashing p’ v p g(b(v)) 0 12.07.2012 p VADS - Kapitel 6 p’ 1 75 Trie Hashing Datenstruktur: Jeder Hasheintrag zu einem Baumknoten v speichert: 1. Bitfolge b(v) zu v 2. Schlüssel key(v) eines Elements e, falls v Knoten im Original Patricia Trie ist (für Suchergebnis) 3. Bitfolgen px(v) der Kanten bzgl. nächstem Bit x∈{0,1} ∈ zu Kindern von v (für Suche) 4. Bitfolge p-(v) zum Vaterknoten (für Restrukturierung) Jeder Hasheintrag zu einem Element e speichert: 1. Schlüssel von e 2. Ort des Schlüssels von e im Baum (siehe 2. oben) 12.07.2012 VADS - Kapitel 6 76 Trie Hashing Beispiel: p-(u)=101 b(u)=10110101 u key(u)=101101010010011 p1(u)=10 p0(u)=0010 p1(v)=0 w 12.07.2012 v b(v)=101101010010 b(w)=1011010100100 VADS - Kapitel 6 77 Trie Hashing Wir vergessen zunächst die Suchproblematik und illustrieren insert und delete. k3 k1 k4 k2 k1 12.07.2012 k2 k3 VADS - Kapitel 6 k4 ∞ 78 Trie Hashing Forderung: jedes Element in exakt einem inneren Knoten vom Patricia Trie. (Möglich mit Dummy ∞.) k3 k1 k4 k2 k1 12.07.2012 k2 k3 VADS - Kapitel 6 k4 ∞ 79 Trie Hashing Insert(e) mit key(e)=k5: wie im bin. Suchbaum k3 k1 k4 k5 k1 12.07.2012 k2 k5 k2 k3 VADS - Kapitel 6 k4 ∞ 80 Trie Hashing Delete(k3): wie im binären Suchbaum k23 k1 k4 k2 k1 12.07.2012 k2 k3 VADS - Kapitel 6 k4 ∞ 81 Trie Hashing Search(x): (W Zweierpotenz) Phase 1: binäre Suche über msd-Knoten 0 W/2 3W/4 W 12.07.2012 x VADS - Kapitel 6 y 82 Trie Hashing Search(x): (W Zweierpotenz) Phase 2: lies Schlüssel aus Baumknoten Fall 1: Ende Phase 1 Antwort: y y z x 12.07.2012 y z VADS - Kapitel 6 83 Trie Hashing Search(x): (W Zweierpotenz) Phase 2: lies Schlüssel aus Baumknoten Fall 2: Ende Phase 1 Antwort: z y z y 12.07.2012 x z VADS - Kapitel 6 84 Trie Hashing • x∈{0,1}W habe Darstellung (x1,…,xW) • Hashfunktion: h:U→[0,1) search(x): // gleich gefunden, dann fertig if key(T[h(x)])=x then return T[h(x)] // Phase 1: binäre Suche auf x s:=⌊log W⌋; k:=0; v:=T[h(ε)]; p:=px1(v) // v: Wurzel des Patricia Tries while s >= 0 do // gibt es Element mit Präfix (x1,…,xk+2s) ? if (x1,…,xk+2s) = b(T[h(x1,…,xk+2s)]) // (msd-)Knoten existiert then k:=k+2s; v:=T[h(x1,…,xk)]; p:= (x1,…,xk) ∘ pxk+1(v) else if (x1,…,xk+2s) ist Präfix von p // Kante aus v deckt (x1,…,xk+2s) ab then k:=k+2s s:=s-1 // weiter mit Phase 2… 12.07.2012 VADS - Kapitel 6 85 Trie Hashing search(x): (Fortsetzung) // Phase 2: Laufe zu Knoten mit größtem Präfix while b(v) ist Präfix von (x1,…,xk) do if pxk+1(v) existiert then k:=k+|pxk+1(v)| v:=T[h(b(v) ∘ pxk+1(v))] else k:=k+|pxk+1(v)| v:=T[h(b(v) ∘ pxk+1(v))] if v ist msd-Knoten then v:=T[h(b(v) ∘ p)] für Bitfolge p aus v return key(v) 12.07.2012 VADS - Kapitel 6 86 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 1: v ist Patricia Knoten v 0 12.07.2012 |p| Binärdarstellung von |b(v)| habe Einsen an den Positionen i1,i2,… (i1 maximal) VADS - Kapitel 6 87 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 1: v ist Patricia Knoten w 0 12.07.2012 v |b(w)|<2i1 |p| msd-Knoten muss bei 2i1 existieren, wird bei binärer Suche gefunden VADS - Kapitel 6 88 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 1: v ist Patricia Knoten w 2i1 12.07.2012 pw u v |p| 2i1+2i2 a) kein msd-Knoten bei 2i1+2i2 : nur dann, wenn kein Patricia-Knoten u mit 2i1<|b(u)|≤2i1+2i2, aber das wird durch pw erkannt und abgedeckt VADS - Kapitel 6 89 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 1: v ist Patricia Knoten v 2i1 12.07.2012 |p| <2i1+2i2 b) msd-Knoten bei 2i1+2i2: wird durch binäre Suche gefunden VADS - Kapitel 6 90 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 1: v ist Patricia Knoten v ∑j 2ij usw. bis msd-Knoten vor v gefunden 12.07.2012 VADS - Kapitel 6 |p| 91 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 1: v ist Patricia Knoten w ∑j 2ij pw v |p| Durch Beachtung von pw im Algorithmus wird dann auch v gefunden. 12.07.2012 VADS - Kapitel 6 92 Trie Hashing Korrektheit von Phase 1: • Sei p der größte gemeinsame Präfix von x und einem Element y∈S ∈ und sei |p|=(zk,…,z0). • Patricia Trie enthält Route für Präfix p • Sei v letzter Knoten auf Route bis p • Fall 2: v ist msd-Knoten v |p| 0 In diesem Fall wird v gefunden (Argumente wie für Fall 1) 12.07.2012 VADS - Kapitel 6 93 Trie Hashing Aufwand für search: • Phase 1 (binäre Suche): O(log W) Lesezugriffe auf DHT • Phase 2 (entlang Patricia Trie): O(1) Lesezugriffe auf DHT Insgesamt O(log W) Lesezugriffe auf DHT. Aufwand für insert: • O(log W) Lesezugriffe auf DHT für search-Operation • O(1) Restrukturierungen im Patricia Trie Also O(log W) Lese- und O(1) Schreibzugriffe auf DHT Aufwand von delete: O(1) Lese- und Schreibzugriffe auf DHT (nur Restrukturierung) 12.07.2012 VADS - Kapitel 6 94 Trie Hashing Datenuniversum: U • Wortgröße: W=⌈log |U|⌉ • • Anzahl Lesezugriffe (search, insert): O(log W) = O(log log |U|) Anzahl Schreibzugriffe (insert, delete): O(1) Beispiel: U={a,…,z}10 Anzahl Zugriffe ~6 12.07.2012 VADS - Kapitel 6 95 Trie Hashing Probleme bei vielen Anfragen auf dieselben Patricia Trie Knoten: Subjekt, das Knoten speichert, wird überlastet. Auch hier Lösung: Combine & Split 12.07.2012 VADS - Kapitel 6 96 Nachfolgersuche • Alle Schlüssel kodiert als binäre Folgen {0,1}W • S: gegebene Schlüsselmenge • Nächster Nachfolger eines Schlüssels x∈{0,1}W: der lexikographisch nächste Schlüssel von x in S Problem: finde für einen Schlüssel x∈{0,1}W den nächsten Nachfolger y∈S Einfache Lösung: auch hier Trie Hashing, aber wir verwenden nur normalen Patricia Trie (ohne msdKnoten) 12.07.2012 VADS - Kapitel 6 97 Nachfolgersuche Search(4) ergibt 5 (bzw. 101) 0 1 1 00 01 0 000 12.07.2012 010 10 1 011 VADS - Kapitel 6 101 110 98 Nachfolgersuche Aufwand für search: • Suche entlang Patricia Trie: im worst case O(W) Lesezugriffe auf DHT Aufwand für insert: • O(W) Lesezugriffe auf DHT für search-Operation • O(1) Restrukturierungen im Patricia Trie Also O(W) Lese- und O(1) Schreibzugriffe auf DHT Aufwand von delete: O(1) Lese- und Schreibzugriffe auf DHT (nur Restrukturierung) Kann Suche nach nächstem Nachfolger beschleunigt werden? 12.07.2012 VADS - Kapitel 6 99 Nachfolgersuche Kann Suche nach nächstem Nachfolger beschleunigt werden? • Wir nutzten Patricia Trie mit msd-Knoten mit zusätzlichen Pointern • Jeder Original Patricia Knoten v speichert zusätzlich die Bitfolge rmin des kleinsten Knoten im rechten Subtrie von v. Damit bekommt jedes Blatt w maximal einen weiteren Patricia Knoten zugewiesen, nämlich den kleinsten gemeinsamen Vorfahr von w und seinem nächsten Vorgänger. Die Wurzel merkt sich zusätzlich das minimale Blatt. • Alle Blätter bilden eine doppelt verkettete Liste. 12.07.2012 VADS - Kapitel 6 100 Nachfolgersuche • Sei u Knoten, der in Phase 1 zurückgegeben wird. b(u) ist Präfix von x. • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Suche dann über binäre Suche nach Vorfahr w von v, so dass rmin von w der Nachfolger von x ist. w u Antwort: z v x 12.07.2012 VADS - Kapitel 6 y z 101 Nachfolgersuche • Sei u Knoten, der in Phase 1 zurückgegeben wird. b(u) ist Präfix von x. • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Suche dann über binäre Suche nach Vorfahr w von v, so dass rmin von w der Nachfolger von x ist. w u Antwort: z v y 12.07.2012 x VADS - Kapitel 6 z 102 Nachfolgersuche • Sei u Knoten, der in Phase 1 zurückgegeben wird. b(u) ist Präfix von x. • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Suche dann über binäre Suche nach Vorfahr w von v, so dass rmin von w der Nachfolger von x ist. w u Antwort: z v y 12.07.2012 x z VADS - Kapitel 6 103 Nachfolgersuche • Sei u Knoten, der in Phase 1 zurückgegeben wird. b(u) ist Präfix von x. • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Suche dann über binäre Suche nach Vorfahr w von v, so dass rmin von w der Nachfolger von x ist. w u Antwort: ∞ v 12.07.2012 VADS - Kapitel 6 y x ∞ 104 Nachfolgersuche • Sei u Knoten, der in Phase 1 zurückgegeben wird. b(u) ist Präfix von x. • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Suche dann über binäre Suche nach Vorfahr w von v, so dass rmin von w der Nachfolger von x ist. w u Antwort: y v x 12.07.2012 y VADS - Kapitel 6 105 Nachfolgersuche • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Binäre Suche bei w nach unten: z < x < z´ bzw. z´<x<z w v z 12.07.2012 x VADS - Kapitel 6 z´ 106 Nachfolgersuche • Sei v Knoten, der in Phase 2 zurückgegeben wird. key(v) und b(v) haben längsten gemeinsamen Präfix mit x. • Binäre Suche bei w nach oben: x<z, x<z´ bzw. x>z´, x>z w v x 12.07.2012 z VADS - Kapitel 6 z´ 107 Nachfolgersuche Aufwand für search: • Binäre Suche im Patricia Trie: im worst case O(log W) Lesezugriffe auf DHT Aufwand für insert und delete: • O(log W) Lesezugriffe auf DHT für search-Operation • O(1) Restrukturierungen im Patricia Trie (auf jedes Blatt zeigen maximal ein zusätzlicher Pointer) Also O(log W) Lese- und O(1) Schreibzugriffe auf DHT Übung: gib Details der insert und delete Operationen an. 12.07.2012 VADS - Kapitel 6 108 Übersicht • • • • • Verteilte Hashtabelle Verteilte Suchstruktur Verteilter Stack Verteilte Queue Verteilter Heap 12.07.2012 VADS - Kapitel 6 109 Konventioneller Stack Ein Stack S unterstützt folgende Operationen: • push(S,x): legt Element x oben auf dem Stack ab. • pop(S): holt das oberste Element aus dem Stack S heraus und gibt es zurück D.h. ein Stack S implementiert die LIFO-Regel (LIFO: last in first out). 12.07.2012 VADS - Kapitel 6 110 Verteilter Stack Viele Prozesse agieren auf Stack: 12.07.2012 VADS - Kapitel 6 111 Verteilter Stack Probleme: • Speicherung des Stacks • Realisierung von push und pop 12.07.2012 VADS - Kapitel 6 112 Verteilter Stack Speicherung des Stacks: • Jedes Element x besitzt eindeutige Position pos(x)≥1 im Stack (das oberste hat die größte Position). • Verwende eine verteilte Hashtabelle, um die Elemente x gleichmäßig mit Schlüsselwert pos(x) zu speichern. • Topologie für Hashtabelle: dynamischer der Bruijn Graph. 12.07.2012 VADS - Kapitel 6 113 Verteilter Stack Realisierung von push(S,x): 1. Stelle push(S,1)-Anfrage, um eine Nummer pos zu erhalten. 2. Führe insert(pos,x) auf der verteilten Hashtabelle aus, um x unter pos zu speichern. Realisierung von pop(S): 1. Stelle pop(S,1)-Anfrage, um eine Nummer pos zu erhalten. 2. Führe delete(pos) auf der verteilten Hashtabelle aus, um das unter pos gespeicherte Element x zu löschen und zu erhalten. Noch zu klären: Punkt 1. Hier kommt uns die de Bruijn Topologie zu Hilfe. 12.07.2012 VADS - Kapitel 6 114 Verteilter Stack Realisierung von push(S,1): • Schicke alle push(S,1) Anfragen zu Punkt 0 im [0,1)-Raum mit Hilfe des de Bruijn Routings. 0 • • • • 1 Der für Punkt 0 zuständige Knoten v0 merkt sich einen Zähler pos. Wenn sich zwei push(S,i) und push(S,j) Anfragen in einem Knoten v treffen, kombiniere diese zu push(S,i+j) mit Rückadresse v (und einer internen Nummer, über die v die ursprünglichen Anfragen speichert, um sie bei einer Rückantwort zu bedienen). Erreicht eine push(S,i) Anfrage v0, dann schickt v0 das Intervall [pos+1,pos+i] an die Rückadresse und setzt pos:=pos+i. Erhält ein Knoten v ein Intervall [pos,pos+i+j-1], das zu zwei push(S,i) und push(S,j) Anfragen gehört, schickt v das Intervall [pos,pos+i-1] an die Rückadresse von push(S,i) und das Intervall [pos+i,pos+i+j-1] an die Rückadresse von push(S,j). 12.07.2012 VADS - Kapitel 6 115 Verteilter Stack Realisierung von pop(S,1): • Schicke alle pop(S,1) Anfragen zu Punkt 0 im [0,1)-Raum mit Hilfe des de Bruijn Routings. • Der für Punkt 0 zuständige Knoten v0 merkt sich einen Zähler pos. • Wenn sich zwei pop(S,i) und pop(S,j) Anfragen in einem Knoten v treffen, kombiniere diese zu pop(S,i+j) mit Rückadresse v (und einer internen Nummer). • Erreicht eine pop(S,i) Anfrage v0, dann schickt v0 das Intervall [max{1,pos-i+1},pos] zurück und setzt pos:=max{0,pos-i}. • Erhält ein Knoten v ein Intervall [pos,pos+i+j-1], das zu zwei pop(S,i) und pop(S,j) Anfragen gehört, schickt v das Intervall [pos,pos+i-1] an die Rückadresse von pop(S,i) und das Intervall [pos+i,pos+i+j-1] an die Rückadresse von pop(S,j). (Ist das Intervall kleiner als i+j, wird entsprechend bei den zwei Intervallen gekürzt. Ist eines dieser beiden Intervalle leer, heißt das, dass keine Elemente im Stack für diesen pop Befehl zur Verfügung stehen.) 12.07.2012 VADS - Kapitel 6 116 Verteilter Stack Satz 6.4: Der verteilte Stack benötigt (mit einer verteilten Hashtabelle auf Basis eines dynamischen de Bruijn Netzwerks) für die Operationen • push(S,x): erwartete Zeit O(log n) • pop(S): erwartete Zeit O(log n) Verwaltung mehrerer Stacks in derselben Hashtabelle: weise jedem Stack statt Punkt 0 einen (pseudo-) zufälligen Punkt in [0,1) zu. 12.07.2012 VADS - Kapitel 6 117 Verteilter Stack Lokale Konsistenz: • quellorientiert: - einfachste Strategie: jeder Knoten wartet erst ab, dass die aktuelle Operation beendet ist, bevor er die nächste initiiert - Übung: geht es auch besser? • routingorientiert: Über periodische Wellenfronten, die von den Knoten mit minimaler ID (für Routing von Zuweisungen) und maximaler ID (für Routing von Anfragen) gestartet werden und über lineare und de Bruijn Kanten propagiert werden. Beispiel: Wellenfront mit zwei Bits initiiert vom Knoten mit max. ID. 01 0 0 1 1 0 12.07.2012 1 0 1 0 1 0 1 0 1 VADS - Kapitel 6 1 Zeit 118 Übersicht • • • • • Verteilte Hashtabelle Verteilte Suchstruktur Verteilter Stack Verteilte Queue Verteilter Heap 12.07.2012 VADS - Kapitel 6 119 Konventionelle Queue Eine Queue Q unterstützt folgende Operationen: • enqueue(Q,x): fügt Element x hinten an die Queue Q an. • dequeue(Q): holt das vorderste Element aus der Queue Q heraus und gibt es zurück D.h. eine Queue Q implementiert die FIFO-Regel (FIFO: first in first out). 12.07.2012 VADS - Kapitel 6 120 Verteilte Queue Viele Subjekte agieren auf Queue: 12.07.2012 VADS - Kapitel 6 121 Verteilte Queue Probleme: • Speicherung der Queue • Realisierung von enqueue und dequeue 12.07.2012 VADS - Kapitel 6 122 Verteilte Queue Speicherung der Queue: • Jedes Element x besitzt eindeutige Position pos(x)≥1 in der Queue (das vorderste hat die kleinste und das hinterste die größte Position). • Verwende eine verteilte Hashtabelle, um die Elemente x gleichmäßig mit Schlüsselwert pos(x) zu speichern. • Topologie für Hashtabelle: dynamischer der Bruijn Graph. 12.07.2012 VADS - Kapitel 6 123 Verteilte Queue Realisierung von enqueue(Q,x): 1. Stelle enqueue(Q,1)-Anfrage, um eine Nummer pos zu erhalten. 2. Führe insert(pos,x) auf der verteilten Hashtabelle aus, um x unter pos zu speichern. Realisierung von dequeue(Q): 1. Stelle dequeue(S,1)-Anfrage, um eine Nummer pos zu erhalten. 2. Führe delete(pos) auf der verteilten Hashtabelle aus, um das unter pos gespeicherte Element x zu löschen und zu erhalten. Noch zu klären: Punkt 1. Hier kommt uns die de Bruijn Topologie zu Hilfe. 12.07.2012 VADS - Kapitel 6 124 Verteilte Queue Realisierung von enqueue(Q,1) und dequeue(Q,1): • Schicke alle enqueue und dequeue Anfragen zu Punkt 0 im [0,1)Raum mit Hilfe des de Bruijn Routings. • Der für Punkt 0 zuständige Knoten v0 merkt sich zwei Zähler first und last. first speichert die Position des ersten und last die Position des letzten Elements in Q. (Da der Wertebereich {1,…,m} der möglichen Positionen endlich ist, werden first und last im ModuloRing aktualisiert. Wir können aber annehmen, dass m sehr groß ist.) • Wenn sich zwei enqueue oder dequeue Anfragen in einem Knoten v treffen, kombiniere diese wie die push und pull Anfragen im Stack. • Erreicht eine enqueue(Q,i) Anfrage v0, dann schickt v0 das Intervall [last+1,last+i] an die Rückadresse und setzt last:=last+i. • Erreicht eine dequeue(Q,i) Anfrage v0, dann schickt v0 das Intervall [first, min{first+i-1,last}] an die Rückadresse und setzt first:=first+i • Auf dem Rückweg werden die enqueue und dequeue Anfragen wie die push und pull Anfragen behandelt. 12.07.2012 VADS - Kapitel 6 125 Verteilte Queue Satz 6.5: Die verteilte Queue benötigt (mit einer verteilten Hashtabelle auf Basis eines dynamischen de Bruijn Netzwerks) für die Operationen • enqueue(Q,x): erwartete Zeit O(log n) • dequeue(Q): erwartete Zeit O(log n) Verwaltung mehrer Queues in derselben Hashtabelle: weise jeder Queue statt Punkt 0 einen (pseudo-) zufälligen Punkt in [0,1) zu. 12.07.2012 VADS - Kapitel 6 126 Übersicht • • • • • Verteilte Hashtabelle Verteilte Suchstruktur Verteilter Stack Verteilte Queue Verteilter Heap 12.07.2012 VADS - Kapitel 6 127 Konventioneller Heap Ein Heap H unterstützt folgende Operationen: • insert(H,x): fügt Element x in den Heap H ein (die Priorität wird über key(x) bestimmt). • deleteMin(H): entfernt das Element x mit kleinstem key(x) aus H und gibt es zurück Zwei Invarianten: • Form-Invariante:vollständiger Binärbaum bis auf unterste Ebene • Heap-Invariante: key(e1)≤min{key(e2),key(e3)} 12.07.2012 VADS - Kapitel 6 e1 e2 e3 128 Verteilter Heap Viele Subjekte agieren auf Heap: 12.07.2012 VADS - Kapitel 6 129 Verteilter Heap Probleme: • Speicherung des Heaps • Realisierung von insert und deleteMin 12.07.2012 VADS - Kapitel 6 130 Verteilter Heap Speicherung des Heaps: • Jedes Element x besitzt eine eindeutige Position pos(x)≥1 im Heap wie bei der Feldrealisierung des konventionellen Heaps e1 e2 e4 e8 e1 12.07.2012 e3 e5 e6 e7 e9 e2 e3 e4 e5 e6 VADS - Kapitel 6 e7 e8 e9 131 Verteilter Heap Speicherung des Heaps: • Jedes Element x besitzt eine eindeutige Position pos(x)≥1 im Heap wie bei der Feldrealisierung des konventionellen Heaps • Verwende eine verteilte Hashtabelle, um die Elemente x gleichmäßig mit Schlüsselwert pos(x) zu speichern. • Topologie für Hashtabelle: dynamischer der Bruijn Graph. 12.07.2012 VADS - Kapitel 6 132 Verteilter Heap Realisierung von insert(H,x): 1. Stelle insert(H,1)-Anfrage, um eine Nummer pos zu erhalten. 2. Führe insert(pos,x) auf der verteilten Hashtabelle aus, um x unter pos zu speichern. Realisierung von deleteMin(H): 1. Stelle deleteMin(H,1)-Anfrage, um eine Nummer pos zu erhalten. 2. Führe delete(pos) auf der verteilten Hashtabelle aus, um das unter pos gespeicherte Element x zu löschen und zu erhalten. Problem: Punkt 2. in deleteMin nicht gut parallelisierbar! 12.07.2012 VADS - Kapitel 6 133 Verteilter Heap Idee: • Verwende Trie Hashing (ohne msd-Knoten), um neue Elemente einzufügen. • D.h. die Elemente werden in einem verteilten Suchbaum gehalten. • Sollen also über deleteMin k Elemente gelöscht werden, so sind dies die vordersten k im Suchbaum. • Die Wurzel des Tries ist zunächst der Anker für alle Anfragen (die dorthin wie beim verteilten Stack kombiniert werden). Diese delegiert die Anfragen dann an die Kinder weiter. Jeder Knoten im Trie merkt sich die Anzahl der Knoten in seinem Subtrie, damit insert und deleteMin Anfragen massiv parallel nach unten weitergegeben werden können. 12.07.2012 VADS - Kapitel 6 134 Verteilter Heap Verteilte Datenstruktur: Patricia Trie Jeder Knoten u kennt die Anzahl der Blätter seines Subtries und der Subtries seiner Kinder (v und w) und speichert diese in s, s0 und s1, d.h. s(v)=s0(v)+s1(v). s0 v 12.07.2012 s u s1 w VADS - Kapitel 6 135 Verteilter Heap Insert Operation: • Schicke die Insert(H,i) Operationen durch das de Bruijn Netzwerk und kombiniere sie auf dem Weg zur Wurzel des Tries. Schicke die Informationen in der Wurzel (über dessen Kinderlabel) zurück und setze s(r) auf s(r)+i. i1 i4 12.07.2012 r i i2 i3 i=i3+i4 VADS - Kapitel 6 136 Verteilter Heap Insert Operation: • Schicke dann die Insert(H,i) Operationen durch das de Bruijn Netzwerk zu den entsprechenden Kindern der Wurzel und kombiniere sie auf dem Weg dahin. Schicke die Informationen in den Kindern zurück und update deren s(v)–Werte auf s(v)+i´ für das entsprechende i´. i1 i2 i3 12.07.2012 i4 i5 r i6 VADS - Kapitel 6 137 Verteilter Heap Insert Operation: • Leite so die Insert Operationen durch den Trie, bis sie ihre richtige Stelle gefunden haben, und integriere sie dort. • Übung: wie können wir mehrere Elemente an der gleichen Stelle einfügen? i1 i2 i3 12.07.2012 i4 i5 r i6 VADS - Kapitel 6 138 Verteilter Heap deleteMin Operation: • Kombiniere zunächst Anfragen deleteMin(H,i) auf dem Weg zur Wurzel r des Tries und setze s(r) auf max{s(r)-i,0}. Wir nehmen für die weiteren Erläuterungen an, dass i≤s(r) ist. i1 i4 12.07.2012 r i i2 i3 i=i3+i4 VADS - Kapitel 6 139 Verteilter Heap deleteMin Operation: • Leite dann Anfrage deleteMin(H,i) durch den Trie und merke für jeden Knoten u, zu welchen Knoten im Combine-Baum die Anfrage gehört. Initial ist das die Wurzel r. i1 i4 r i i2 i3 u v 12.07.2012 w VADS - Kapitel 6 140 Verteilter Heap deleteMin Operation: • Ist s0(u)≥i für die Anfrage deleteMin(H,i), leite die Anfrage an das linke Kind v weiter, behalte die Referenz auf die Combine-BaumKnoten bei, und setze s(v) auf s(v)-i. i1 i4 r i i2 i3 u v 12.07.2012 w VADS - Kapitel 6 141 Verteilter Heap deleteMin Operation: • Ist s0(u)<i für die Anfrage deleteMin(H,i), splitte die Anfrage in zwei auf: deleteMin(H,s0(u)) für v und deleteMin(H,i-s0(u)) für w. i1 i2 r´ i4 r i i3 u v 12.07.2012 w VADS - Kapitel 6 142 Verteilter Heap deleteMin Operation: • Ist nun für eine dieser Anfragen deleteMin(H,j) j≤i3, dann wird diese auf Combine-Knoten r´ verwiesen und i3 reduziert auf i3–j. Das gleiche Verfahren wird auch für i4 angewendet. i1 i2 r´ i4 r i i3 u v 12.07.2012 w VADS - Kapitel 6 143 Verteilter Heap deleteMin Operation: • Wenn die deleteMin Anfragen am Ende die Blätter erreicht haben, wird jedem Blatt im Combine-Baum genau ein Blatt im Patricia Trie zugewiesen. i1 i2 r´ i4 r i i3 u v 12.07.2012 w VADS - Kapitel 6 144 Verteilter Heap Satz 6.6: Der verteilte Heap benötigt (mit einer verteilten Hashtabelle auf Basis eines dynamischen de Bruijn Netzwerks) für die Operationen • insert(H,x): erwartete Zeit O(log2 n) • deleteMin(Q): erwartete Zeit O(log n) Verwaltung mehrer Heaps in derselben Hashtabelle: weise jeder Queue statt Punkt 0 einen (pseudo-) zufälligen Punkt in [0,1) zu. 12.07.2012 VADS - Kapitel 6 145 Selbststabilisierung • Verteiltes Hashing: Lediglich kontinuierliche Überprüfung, ob Schlüssel noch dem richtigen Prozess zugeordnet ist. Falls nicht, dann leite diesen an den richtigen weiter. • Verteilter Stack, verteilte Queue: Wie beim verteilten Hashing. Sollte zusätzlich der/die Zähler in der Wurzel korrumpiert sein, gehen eventuell Elemente verloren bzw. es wird auf undefinierte Elemente verwiesen. Um den/die Zähler zu reparieren, könnte die Wurzel über binäre Suche kontinuierlich den korrekten Stand des Zählers überprüfen. 12.07.2012 VADS - Kapitel 6 146 Selbststabilisierung Verteilter Heap: • Wie beim verteilten Hashing, um die Trie-Knoten im richtigen Prozess zu speichern. Jeder Trie-Knoten überprüft weiterhin periodisch, ob die benachbarten Trie-Knoten noch existieren und die Werte von s(v), s0(v) und s1(v) korrekt sind. • Vaterknoten existiert nicht: führe die gewöhnliche insert-Operation für Patricia Tries (die Anfrage läuft den entsprechenden Pfad von der Wurzel aus hinunter) zur Wiedereinfügung des Knotens durch. • Kindknoten existiert nicht: Trie kann komprimiert werden, da Knoten nicht mehr notwendig ist. Führe dazu delete-Operation für den Knoten durch. • Für Knoten v ist s(v)≠s0(v)+s1(v): setze s(v) auf s0(v)+s1(v). • Für den standard Patricia Trie werden keine Blattzuordnungen der inneren Knoten benötigt. Diese Zuordnungen müssen daher auch nicht repariert werden. 12.07.2012 VADS - Kapitel 6 147 Fragen? SS 2012 VADS - Kapitel 6 148