Motivation

Der Smart Growing Cells Algorithmus ist ein Ansatz zur Oberflächenrekonstruktion aus Punktwolken mittels eines künstlichen neuronalen Netzes. Er wird zum Beispiel eingesetzt um Messdaten, wie sie z.B. von einem Laserscanner erzeugt werden, in ein für die Computergrafik typisches Netz aus Dreiecken zu überführen.

Das Verfahren wurde von Hendrik Annuth im Rahmen seiner Dissertation implementiert und verfeinert. In meinem 5. Semester habe ich, zusammen mit einem Kommilitonen, den Versuch unternommen die damals bestehende Implementierung zu parallelisieren.

Das reale Objekt, eine sich ergebende Punktwolke und die rekonstruierte Oberfläche.

Ablauf einer Rekonstruktion

Das Verfahren beginnt mit einem relativ einfachen Grundobjekt, im konkreten Fall typischerweise einer einfachen Pyramide. Im Laufe der Rekonstruktion gibt es dann drei Basisoperationen, die für jeden Punkt des Dreieckgeflechts eine von drei Operationen ausführt. Die genaue Art der Operation hängt dabei von den umgebenden Punkten ab, welche als Eingabe für den Algorithmus fungieren.

Zwischenergebnisse der Rekonstruktion

Bei diesen Grundoperationen handelt es sich um Glätten, Aufteilen und Verschmelzen. Anschaulich kann man sich vorstellen, dass versucht wird die bestehenden Punkte des Meshs möglichst nahe an die Punktwolke anzupassen. Ist das gegenwärtige Mesh nicht fein genug, es besteht also aus zu wenig Dreiecken, wird an geeigneter Stelle ein neues Dreieck hinzugefügt. Ist das aktuelle Mesh zu fein, werden einzelne Dreiecke entfernt.

Glätten
Aufteilen
Verschmelzen

Eignung des Verfahrens für die Parallelisierung

Das Verfahren ist relativ gleichgültig bezüglich der exakten Reihenfolge, in der die Grundoperationen durchgeführt werden. Außerdem hat jede dieser Operationen nur relativ lokale Auswirkungen, ein Großteil des Meshes bleibt von den Änderungen also unberührt. Wir haben für die Parallelisierung also die Theorie aufgestellt, dass wird diesen "Standardprozess" der drei Grundoperationen einfach räumlich getrennt mehrfach auf dem gleichen Mesh ausführen können. Außerdem sollte die Anzahl der möglichen Threads irgendwie von der Größe des Meshes abhängen.

Räumlich getrennte durchgeführte Operationen

Eignung der Implementierung für die Parallelisierung

Die von uns vorgefundene Implementierung war hochgradig optimiert, speziell was die Speicherverwaltung angeht. Der Speicher umgeht die in C++ übliche Speicherverwaltung mit new und delete zugunsten eines eigens implementierten Speicherpools. Diese Implementierung steigert die Cache Lokalität und verringert den Overhead bei häufigen kleineren Allokationen. Da die Klassen für den Pool sowohl als Speichermanager als auch als eigene Datenstruktur genutzt wurden war es schwierig zu bestimmen, wann und unter welchen Umständen auf welche Speicherbereiche lesend oder schreibend zugegriffen wird.

Wir haben uns daher als Lösungsansatz für einen Octree entschieden, bei dem die Unterbäume von einzelnen Threads gesperrt werden können. Der Octree garantiert einen gewissen räumlichen Zusammenhang, allerdings keine sehr granulare Unterscheidung. Es könnten z.B. auch voneinander unabhängige Teilmeshes Teil des gleichen Octrees sein. Bei sehr kleinen Meshes ist zudem schlichtweg nicht genug Raum vorhanden, hier lohnt sich keine Parallelisierung.

Die Implementierung

Übersicht über die entstandene Softwarestruktur.

Die konkrete softwaretechnische Umsetzung folgt dem Master-Worker Muster. Es existiert also ein "Hauptthread" in Form des WorkerManager der die konkreten Aufgaben bestimmt und diese an einzelne Worker verteilt. Es werden also Job Instanzen auf Vorrat erzeugt und auf einen Stack gelegt.

Einen Job zu entnehmen entspricht einer Pop Operation, das Hinzufügen einem Push.

Der WorkerManager erzeugt die Aufgaben allerdings unabhängig von den Sperrbereichen, da er nicht vorhersehen kann wann welcher Bereich nicht zur Verfügung steht. Für jede Pop Operation muss also geprüft werden ob die betroffenen Bereiche gerade frei sind, das ist in unserer Implementierung die Aufgabe des SpatialLocker.

Die Ergebnisse

Wir hatten leider wenig Zeit die Implementierung noch zu optimieren, weil wir mehr Zeit als ursprünglich erhofft mit der Besprechung von Detailfragen bezüglich der Umsetzung. Im Endeffekt haben wir einen Verbesserung der Geschwindigkeit von etwa 10% je zusätzlichem Kern erreicht. Bei den relativ langen Laufzeiten die der Algorithmus zur Durchführung benötigt, typischerweise im Bereich von einigen Stunden für reale Modelle, ist das dennoch eine relevante Verbesserung.

Als Hauptproblem konnten wir den großen Anteil von verworfenen Jobs ausmachen, zeitweise wurden weniger als 10% der angelegten Jobs auch tatsächlich durchgeführt. Um diesen Ansatz weiter zu verfolgen bräuchte es also eine geeignetere Implementierung des SpatialLocker, der die lock und unlock Operationen granularer durchführen kann.


Kontakt & Impressum