Externes Sortieren
Externes Sortieren beschreibt Sortieralgorithmen, welche auf sehr große Datenmengen ausgelegt sind. Algorithmen, die auf großen Datenmengen arbeiten, die nicht in den Hauptspeicher passen, werden allgemein als External-Memory-Algorithmen bezeichnet. In der Analyse klassischer Sortieralgorithmen (z. B. Quicksort) wird meist keine Speicherhierarchie bzw. der Zugriff auf Daten auf unterschiedlich schnellen Datenträgern berücksichtigt. Allerdings ist der Aufbau und die Hierarchie des Speichers sowie der Umgang der Algorithmen mit diesem für die Performance beim Sortieren von großen Datenmengen entscheidend.
Parallel Disk Model
BearbeitenEin gebräuchliches Modell zur Betrachtung von External Memory Algorithmen ist das Parallel Disk Model (PDM).[1] Dieses verfügt über einen Hauptspeicher der Größe und einen oder mehreren externen Speicher unbegrenzter Größe, die sogenannten Disks. Auf diesen finden die zu sortierenden Daten der Größe Platz. Oftmals wird zur Vereinfachung die Anzahl der Disks angenommen und das vereinfachte Modell als External Memory Model (EMM) bezeichnet. Auch Aggarwal und Vitter stellten das Modell ursprünglich mit einer einzelnen Disk, aber mehreren parallelen Schreib-Lese-Köpfen vor.[2] Der Hauptspeicher wie auch der externen Speicher verfügen über eine Blockgröße . Mit einer IO-Operation, kann ein Block pro Disk in den Hauptspeicher geladen oder in den externen Speicher zurückgeschrieben werden. Zur besseren Übersicht werden außerdem die Eingabegröße sowie die Größe des Hauptspeichers in Blöcken angegeben. Im Gegensatz zur Analyse klassischer Sortieralgorithmen wird die Performance eines Algorithmus nicht an der Anzahl aller Operationen gemessen, sondern nur durch die Anzahl der IO-Operationen angegeben.
Untere Schranke
BearbeitenFür das Sortieren im PDM kann eine allgemeingültige Schranke für die Anzahl der IO-Operationen angegeben werden.[1] Diese ist im Fall einer einzelnen oder mehrerer Disks ( ) gegeben durch:
In der Praxis ist eine kleine Konstante. Für den untypischen Fall gilt diese Schranke lediglich für vergleichsbasierte Verfahren.
Algorithmen
BearbeitenMerge Sort
BearbeitenAn den klassischen Merge Sort angelehnt ist der External Memory Merge Sort.[1][3] Zunächst soll der Algorithmus für betrachtet werden. Es werden nacheinander Datensegmente bestehend aus Blöcken in den Hauptspeicher geladen, dort sortiert und als sogenannter Run zurück in den externen Speicher geschrieben werden. Die Runs im externen Speicher werden anschließend mittels eines K-Way-Merges auf Rekursionsebenen zusammengefasst.
for j=1 to n/M Lade nächstes Datensegment der Größe in Hauptspeicher Sortiere Daten im Hauptspeicher Schreibe sortierte Daten als run auf externen Speicher end
for i=1 to for j=1 to Lade die jeweils ersten Blöcke der runs in Merge-Buffer while Merge-Buffer nicht leer Merge runs Schreibe Output-Buffer in externen Speicher falls voll Lade nächsten Block in Hauptspeicher falls ein Merge-Buffer bereits komplett gemerged end end end
Um mehrere parallele Disks möglichst effizient zu nutzen und die oben beschriebene Schranke einzuhalten, müssen in jedem IO-Schritt möglichst Blöcke bewegt werden. Zum effizienten Schreiben der Blöcke zurück auf die Disks wird daher die Größe des internen Buffers im Hauptspeicher, der die gemergten Elemente enthält, auf Blöcke erweitert. Um auch beim Lesen einen höheren Durchsatz zu gewährleisten, wird außerdem ein Prefetching-Buffer hinzugefügt. Wurden die Elemente in den vorherigen Schritten geschickt auf den einzelnen Disks verteilt und eine geeignete Prefetching-Strategie gewählt, genügt eine Buffergröße von Blöcken.
Distribution Sort
BearbeitenAnalog zum Merge Sort ist auch der Distribution Sort ein rekursiver Algorithmus.[1][3] Die Daten sollen nach Partitionierungselementen in Buckets eingeteilt werden. Dabei gilt, dass alle Elemente innerhalb eines Buckets kleiner sind als jedes Element im darauf Folgenden. Diese Partitionierung wird dann rekursiv für die einzelnen Buckets wiederholt. Sind die Buckets klein genug um jeweils komplett in den Hauptspeicher geladen werden zu können, werden sie dort sortiert. Die Konkatenation der sortierten Buckets bildet somit die sortierte Reihenfolge der gesamten Daten.
Die Wahl der Partitionierungselemente ist entscheidend um möglichst gleich große Buckets zu erhalten und somit eine gute Performance zu gewährleisten. Dies kann beispielsweise auf probabilistische Art und Weise erfolgen. Eine kleine, zufällige Menge an Elementen wird sortiert und die Partitionierungselemente aus dieser ausgewählt. Die Größe der Menge wird so gewählt, dass die Sortierung dieser für die Anzahl an IO-Operationen vernachlässigbar ist und keinen Einfluss auf die Schranke hat. Bei guter Partitionierung beträgt die Rekursionstiefe . Da für jedes Bucket ein Buffer im Hauptspeicher benötigt wird, kann durch nach oben abgeschätzt werden.
Um die oben gegebene Schranke zu erfüllen, dürfen somit auf jeder Rekursionsebene lediglich IO-Operationen ausgeführt werden. Um ein Bucket effizient in den Hauptspeicher zu laden, müssen dessen Blöcke zuvor gleichmäßig auf die verschiedenen Disks verteilt worden sein. Die zu den verschiedenen Buckets gehörenden Blöcke werden entweder gemeinsam als ein Stripe aus dem Output-Buffer im Hauptspeicher auf den externen Speicher geschrieben oder es wird ein Stripe pro Bucket erstellt. Im Falle eines Stripes pro Bucket kann mit Hilfe von Randomized Cycling dafür gesorgt werden, dass die nächsten Blöcke der verschiedenen Buckets auf möglichst unterschiedliche Disks geschrieben werden müssen. Diese Variante wird auch Randomized Cycling Distribution Sort genannt.
Anwendungen
BearbeitenGrundsätzlich nehmen Operationen zur Sortierung einen großen Teil des Rechenaufwands ein.[3] In den letzten Jahren sind die Datenmengen, auf denen gerechnet wird, stetig größer geworden.[1] Zur Bewältigung dieser Datenmengen sind External-Memory-Algorithmen nötig. Effizientes Externes Sortieren hat dabei eine besondere Bedeutung, da viele External-Memory-Algorithmen auf Sortierverfahren beruhen.
Einzelnachweise
Bearbeiten- ↑ a b c d e Jeffrey Scott Vitter: Algorithms and data structures for external memory. In: Foundations and Trends in Theoretical Computer Science. 2. Jahrgang, Nr. 4, 2008, S. 30–474.
- ↑ Alok Aggarwal, Jeffrey Scott Vitter: The input/output complexity of sorting and related problems. In: Communications of the ACM. 31. Jahrgang, Nr. 9, 1988, S. 1116–1127.
- ↑ a b c Donald Knuth: The Art of Programming, Vol. 3 (Sorting and Searching). Addison-Wesley, 1973.