Evaluation von ROOT als Option für die Datenhaltung bei USCT Projektarbeit

Größe: px
Ab Seite anzeigen:

Download "Evaluation von ROOT als Option für die Datenhaltung bei USCT Projektarbeit"

Transkript

1 Evaluation von ROOT als Option für die Datenhaltung bei USCT Projektarbeit im Studiengang Angewandte Informatik an der Berufsakademie Karlsruhe von Johannes Kissel August 2007 Bearbeitungszeitraum 2. Juli August 2007 Kurs TAI05 Ausbildungsrma Forschungszentrum Karlsruhe Betreuer der Ausbildungsrma Dipl.-Inf. Michael Zapf

2 Ehrenwörtliche Erklärung Karlsruhe, 31. August 2007 Hiermit versichere ich, dass ich die vorliegende Projektarbeit selbstständig verfasst und keine anderen als die angegebenen Quellen benutzt habe. (Johannes Kissel) (Dipl.-Inf. Michael Zapf, Betreuer) 1

3 Inhaltsverzeichnis 1 Einführung und Motivation Datenhaltung bei USCT ROOT A Data Analysis Framework MATLAB und MEX Motivation für die Nutzung von ROOT Chronologischer Bericht Evaluation verschiedener ROOT-Schnittstellen Funktionale Evaluation des MEX-Ansatzes Schnittstellenkonzept Abstraktes Speicherformat Abbildung der MATLAB-Datentypen in ROOT Implementierung der Schnittstelle Die Funktion saveroot() Die Funktion loadroot() Diverse Probleme bei der Implementierung Zusammenspiel der Schnittstellenteile Ergebnisse Tests Stabilitätstests Funktionstests Diskussion Literaturverzeichnis 20 Anlagenverzeichnis 21 Abbildungsverzeichnis 22 Abkürzungsverzeichnis 23 2

4 Kapitel 1 Einführung und Motivation 1.1 Datenhaltung bei USCT Am Forschungszentrum Karlsruhe wird ein auf Ultraschall basierendes, bildgebendes Verfahren zur Brustkrebsfrüherkennung entwickelt. Diese Ultraschall- Computertomographie (USCT) bietet dreidimensionale Volumenbilder einer Brust in wesentlich höherer Bildqualität als herkömmliche Verfahren, die Ultraschall nutzen. Eine Messung mit allen Ultraschallwandlern erzeugt eine Masse von ca. 20 GB Rohdaten, die zur Zeit im MATLAB eigenen Speicherformat mit der Dateierweiterung.mat abgelegt werden. Im Laufe der Zeit hat sich dabei folgende projektinterne Verzeichnishierarchie entwickelt: Abb. 1.1: Verzeichnishierarchie der USCT-Daten Die gesamte Datenhaltung ist was die Nutzung verschiedener Datentypen anbelangt sehr heterogen. Unter anderem werden z.b. UInt16, Double, String, Array und Struct verwendet. Im Rahmen dieser Arbeit sollen nun die Möglichkeiten von ROOT als Datenhaltungsoption geprüft werden. 3

5 1.2. ROOT A DATA ANALYSIS FRAMEWORK 1.2 ROOT A Data Analysis Framework ROOT ist ein am CERN entwickeltes C++-Framework zur Datenhaltung und -Analyse. Es ist auf den Umgang mit riesigen Datenmengen (Petabyte-Bereich) optimiert und skaliert deshalb sehr gut. Die Daten werden ezient abgespeichert: Sie erlauben selektiven Zugri und können on-the-y mit GNU ZIP komprimiert werden. ROOT ist vollständig objektorientiert und Thread-fähig. Abb. 1.2: Logos von CERN und ROOT Da ROOT in C++ programmiert ist, ist es nahezu plattformunabhängig. Es gibt fertige Binärpakete für zahlreiche Umgebungen, sowie einen komfortablen Windows-Installer. Das System wird unter einer Open Source-Lizenz vertrieben (GNU LGPL) - mit allen bekannten Vorteilen. Es gibt eine groÿe, heterogene Nutzergemeinde; die Entwicklung geht schnell voran. Graphische Elemente und Benutzeroberächen können durch externe Bibliotheken wie z.b. OpenGL oder Qt dargestellt werden. Eine groÿe Stärke ROOTs ist die einfache Erstellung von Histogrammen, wie sie in der Teilchenphysik am CERN genutzt werden. Dank des mitgelieferten C/C++-Interpreters CINT kann ROOT-Code sowohl kompiliert als auch interpretiert werden. In Verbindung mit dem Kommandozeileninterpreter kann dies das Testen mitunter sehr erleichtern: Abb. 1.3: Die ROOT-Konsole Es existiert umfangreiche Dokumentation zu ROOT, wobei diese sich hauptsächlich an der konventionellen Nutzung in einer ROOT-Umgebung orientiert. Was JavaDoc bei Java, das ist die THtml-Klasse in ROOT: eine automatische Dokumentations-Engine. 4

6 1.3. MATLAB UND MEX 1.3 MATLAB und MEX MATLAB ist ein kommerzielles Produkt der Firma Mathworks, Inc. Der Code ist auf verschiedenen Plattformen lauähig, ist aber proprietär. Die mathematisch orientierte Syntax ermöglicht ein schnelles Prototyping von Lösungsansätzen im wissenschaftlichen Bereich. In der MATLAB-Entwicklungsumgebung können Quelltext-Dateien (*.m) komfortabel erstellt, ausgeführt und einem Debugging unterzogen werden. Sie bietet zudem einen Kommandozeileninterpreter und umfangreiche Visualisierungsmöglichkeiten für 2D und 3D. Eine wichtige Komponente ist auch die Workspace- Verwaltung. Ein Workspace entspricht dem Gültigkeitsbereich einer Funktion. Mit dem MEX-Compiler können in C/C++ geschriebene Funktionen von MATLAB aus aufgerufen werden. MEX-Code muss mindestens Folgendes enthalten: #include "mex.h" void mexfunction(int nlhs, mxarray *plhs[], int nrhs, const mxarray *prhs[]) {... } Für Matrix-Operationen muss zusätzlich die Datei matrix.h eingebunden werden. plhs und prhs sind Arrays von MATLAB-Variablen für Aus- bzw. Eingabe der Einsprungsfunktion mexfunction() (left/right hand side). nlhs und nrhs geben jeweils die Anzahl der Elemente an. 1.4 Motivation für die Nutzung von ROOT ROOT wurde bereits erfolgreich im Grid getestet und eignet sich wegen seiner eingebauten Thread-Fähigkeiten für die parallele Datenverarbeitung. Grid ist eine Infrastruktur zur gemeinschaftlichen Nutzung autonomer Ressourcen und wird ebenfalls am FZK mitentwickelt. MATLAB hat eine recht kostspielige Einzelplatz-Lizensierung, während das plattformunabhängige ROOT-Framework unter einer freien Lizenz (GNU LGPL) steht. Weltweit sind zahlreiche ROOT-Installationen in Nutzung. Dies könnte den Datenaustausch mit anderen Projekten vereinfachen. Auf lange Sicht möchte das USCT-Projekt weg von der dateibasierten Datenhaltung und hin zu einer auf Datenbanken basierenden. ROOT könnte hier das entscheidende Interface sein, obwohl auch MATLAB eigene Datenbank- Schnittstellen besitzt. Die Evaluation von ROOT für USCT wurde angeregt von Prof. Dr. Gemmeke, dem Leiter des IPE, weil es am Institut bereits andere Projekte gibt, die ROOT nutzen. 5

7 Kapitel 2 Chronologischer Bericht 2.1 Evaluation verschiedener ROOT-Schnittstellen Nachdem ich mich zunächst über ROOT informiert hatte, habe ich verschiedene Möglichkeiten für Schnittstellen zwischen ROOT und MATLAB überprüft: Java Tony Johnson hat eine Java-Schnittstelle geschaen, mit der man auf ROOT-Dateien zugreifen kann - allerdings nur lesend. Obwohl MATLAB mit Java kommunizieren kann, konnte sie daher für dieses Projekt, in dem auch geschrieben werden muss, nicht eingesetzt werden. Python ROOT besitzt von Haus aus ein Python-Modul names PyRoot, das alle ROOT-Funktionalitäten unter Python zugänglich macht. Diese Schnittstelle war nicht interessant, weil man Python nicht direkt von MATLAB aus ansprechen kann. Sockets Es gibt eine Socket-Schnittstelle, über die ROOT-Programme untereinander kommunizieren können. Diese Schnittstelle ist jedoch nicht weiter dokumentiert. Um sie für eine MATLAB/ROOT-Kommunikation anzupassen, müsste sie anhand der Quelltexte nachgebaut werden - ein zu hoher Aufwand. MEX MEX ist optimal in die MATLAB-Umgebung integriert und nutzt C/C++. Die C++-Bibliotheken von ROOT können so direkt eingebunden werden. Der MEX-Ansatz war nach diesem Vergleich der einzig verbleibende, sodass ich mich entschied, meine Schnittstelle darauf aufzubauen. Mein Ziel war es dabei, maximale Transparenz aus Sicht von MATLAB zu erreichen. 6

8 2.1. EVALUATION VERSCHIEDENER ROOT-SCHNITTSTELLEN Später wurde noch vorgeschlagen, als Schnittstelle die Java-Implementierung von Python Jython zu nutzen und diese über die Java-Schnittstelle von MATLAB anzusprechen. Diesen Ansatz habe ich nicht weiter verfolgt, weil ich bereits mit der Implementierung der MEX-Schnittstelle begonnen hatte. Zudem erwartete ich von dem Umweg von MATLAB über Java und Python zu ROOT Performanz-Einbuÿen. Aufwändig in meiner Arbeit war, dass die Dokumentation von ROOT an dessen Nutzung in einer ROOT-Umgebung orientiert ist, sodass ich vieles erst mühsam selbst herausnden oder in Foren erfragen musste. Das ozielle ROOT-Forum erlaubt zudem nur registrierten Nutzern das Schreiben und hat recht langen Aufnahmezeiten Funktionale Evaluation des MEX-Ansatzes Um die Realisierbarkeit des MEX-Ansatzes zu prüfen mussten die folgenden Hauptprobleme gelöst werden: 1. Man kann sogennante ROOT-Macros (C++-Code in einem bestimmten Format) leicht kompilieren, indem man sie in einer ROOT-Shell folgendermaÿen lädt: root [].L sourcefile++ Befehle der interaktiven Shell beginnen immer mit einem Punkt, L ist der Lade-Befehl und ++ erzwingt das Kompilieren. Die Ausgabe ist eine.so- (Linux) bzw..dll-datei (Windows). Sie ist allerdings auf die interne Nutzung in ROOT ausgelegt, sodass die MATLAB-Funktion loadlibrary() nicht funktioniert. 2. Also musste der ROOT-Code auÿerhalb von ROOT kompiliert werden. Zunächst war ich der irrigen Annahme, dass MEX nur eine Schnittstelle zu C und nicht zu C++ sei. Das macht beim Rufen von Funktionen in einer Bibliothek einen Unterschied. Deshalb wollte ich eine Proxy-Bibliothek erzeugen, über die ich mit meiner eigentlichen ROOT-Bibliothek interagieren könnte. Dieses Missverständnis rührte daher, dass in den Überschriften der MEX-Dokumentation nirgendwo von C++ die Rede ist. Glücklicherweise stellte sich aber heraus, dass MEX eine C++-Schnittstelle ist. 3. Als nächstes musste ich herausnden, welche ROOT-Bibliotheken ich an meine eigene binden muss, um sie direkt aus MATLAB rufen zu können. Es gibt dazu keine Dokumentation. Dafür gibt es das Script root-config.sh, das allerdings unter Windows automatisch von einer Visual C++-Umgebung ausgeht. Visual C++ hatte ich nicht installiert und wollte auch lieber den schlanken GCC-Compiler benutzen, mit dem ich gute Erfahrungen gemacht habe. Unter Linux habe ich daher den Binder ld dazu benutzt herauszunden, welche Bibliotheken der ROOT-Befehl.L sourcefile++ standardmäÿig einbindet. Dann habe ich das Programm mit dem GCC- Compiler übersetzt und diejenigen der gefundenen Bibliotheken dazugebunden, die ich durch Ausprobieren als notwendig und hinreichend identiziert hatte. 7

9 2.2. SCHNITTSTELLENKONZEPT 4. Dann gab es noch einen Versionskonikt zwischen den Bibliothekenlibstdc++, mit denen ROOT einerseits und mein Betriebssystem andererseits kompiliert worden waren. Ich musste ROOT also neu kompilieren und die Pfade so setzen, dass diese ROOT-Version benutzt wird. Man kompiliert meine Quellen nun mit folgendem MEX-Aufruf: mex -I$ROOTSYS/include -L$ROOTSYS/lib -lcore -lcint -lrio -lmatrix source.cpp 2.2 Schnittstellenkonzept Um möglichst groÿe Transparenz aus Sicht von MATLAB zu erreichen, habe ich zwei Funktionen saveroot() und loadroot() in direkter Analogie zu den MATLAB-Funktionen save() und load() implementiert. Es gibt eine ganze Reihe von I/O-Klassen, die alle von der I/O-Basisklasse TFile erben. Sie haben damit alle das gleiche Verhalten und erweitern TFile um bestimmte Fähigkeiten, wie z.b. das FTP-Protokoll. Bei meiner Software habe ich mich auf die Klasse TFile beschränkt, um das prinzipielle Vorgehen der ROOT-Ein- und -Ausgabe zu zeigen. Es sind keine gröÿeren Anpassungen nötig, um sie etwa für den Einsatz mit Datenbanken zu erweitern. Um gespeicherte Daten direkt weiterverarbeiten zu können, habe ich für die unterstützten MATLAB-Datentypen eine Entsprechung in nativen ROOT-Klassen gesucht Abstraktes Speicherformat In einer ROOT-Datei wird ein UNIX-artiges Dateisystem mit Verzeichnissen und persistierten Objekten als Dateien abgebildet. Man kann dieses Dateisystem komfortabel mit dem ROOT Object Browser durchsuchen: Abb. 2.1: Der ROOT Object Browser 8

10 2.2. SCHNITTSTELLENKONZEPT ROOT-Dateien werden auf Objektebene von der Klasse TFile repräsentiert, Verzeichnisse von der Klasse TDirectory. TFile erbt von TDirectory, eine ROOT-Datei ist also ein spezielles Verzeichnis. Jeder Eintrag in einem Verzeichnis hat den Typ TKey und verweist auf ein Unterverzeichnis oder eine Datei. Diese Organisation erlaubt den wahlfreien Zugri auf Objekte einer ROOT-Datei, ohne dass diese komplett in den Hauptspeicher geladen werden muss. Abb. 2.2: Beschreibung des ROOT-Dateiformats Es gibt noch weitere, spezialisierte I/O-Klassen, die von TFile erben und deren Fähigkeiten ergänzen. Zwei davon möchte ich nennen: TNetFile Ermöglicht mittels des sogenannten rootd-deamons das Lesen und Schreiben von ROOT-Dateien über ein Netzwerk. Man muss kein Superuser sein, um rootd starten zu können. TSQLFile Erlaubt es, ROOT-Objekte in einer relationalen Datenbank abzulegen. Unterstützt werden MySQL, ODBC und Oracle. Da diese Klassen von TFile erben, haben sie das gleiche Verhalten. Um meine Schnittstelle für den Einsatz im Netz oder mit Datenbanken zu erweitern, müsste man daher nur das verwendete TFile-Objekt durch ein Objekt dieser Klassen ersetzen und die entsprechende Syntax (z.b. URL) für den Dateinamen- Parameter vorsehen. Während einer ROOT-Sitzung bendet man sich immer in einem Verzeichnis, auch wenn noch keine ROOT-Datei geladen ist. In diesem Fall bendet man sich im virtuellen Verzeichnis TROOT. Der Pfad des aktuellen Verzeichnisses wird von der globalen Variablen gdirectory repräsentiert Abbildung der MATLAB-Datentypen in ROOT MATLAB kennt einige spezialisierte Datentypen für verschiedene Anwendungen. Im Wesentlichen sind fünf davon bei USCT in Nutzung: 9

11 2.2. SCHNITTSTELLENKONZEPT Integer-Matrizen Double-Matrizen Structure-Arrays Cell-Arrays Strings Structure-Arrays sind vergleichbar mit assoziativen Arrays oder Hashtables, eine Ansammlung von Schlüssel/Wert-Paaren. Cell-Arrays sind Arrays mit Elementen unterschiedlichen Typs. Structure- und Cell-Arrays können ineinander verschachtelt werden und die anderen Typen kapseln. Erst erwog ich, eigene ROOT-Klassen zu implementieren, um die MATLAB- Datentypen abzubilden, denn nicht alle haben ein Pendant. Doch das würde den Mehrwert von ROOT empndlich einschränken, denn die Daten könnten nur gespeichert und geladen, nicht aber direkt in ROOT weiterverarbeitet werden. Auÿerdem müssten die neuen Datentypen in ROOT einkompiliert werden, was recht aufwändig gewesen wäre. So habe ich mich entschieden, geeignete ROOT- Klassen zu suchen. Matrizen und Cell-Arrays, die nahezu beliebig viele Dimensionen haben können, haben keine direkte Entsprechung in ROOT, da es dort nur Matrizentypen mit zwei Dimensionen gibt. Deshalb wandle ich sie zunächst in ein einfaches Array um. Damit die Information über die Dimensionalität nicht verloren geht, habe ich eine Art Kopormat deniert: Abb. 2.3: Umwandlung numerischer Matrizen in Arrays mit Kopormat Bei der Umwandlung von MATLAB-Cell-Arrays in ROOT-Objektarrays verfahre ich ähnlich, die Anzahl der Dimensionen muss hier jedoch nicht zusätzlich gespeichert werden: In der ersten Zelle lege ich ein Array mit den Dimensionslängen ab. Die Anzahl der Einträge in diesem Array entspricht der Anzahl der Dimensionen. 10

12 2.3. IMPLEMENTIERUNG DER SCHNITTSTELLE Bei allen ROOT-Klassen war darauf zu achten, dass sie von TObject erben, einer ROOT-Basisklasse. Dies hat einige Vorteile: Objekte von Klassen, die von TObject erben, können in ROOT-Collections abgelegt werden (wichtig bei Structure- und Cell-Arrays). sich selbst serialisieren und persistieren. sich selbst reektieren, um ihren Typ zu bestimmen (RTTI). Es gibt jedoch eine Vielzahl von Klassen, die nicht von TObject erben und andere Basisklassen haben. Die Gründe sind mir unbekannt. Objekte dieser Klassen müssen über die WriteObject()-Methode von TDirectory persistiert werden. ROOT-Dateien mit solchen Objekten lieÿen sich im ROOT Object Browser aber nicht mehr önen, während das im CINT problemlos möglich war. Dies deutet auf einen Fehler im Browser hin. Leider gibt es keine von TObject abgeleitete Klasse für Integer-Matrizen, weshalb ich sie noch nicht vollständig implementiert habe. Sie können zwar abgespeichert werden, sind aber beim Laden wegen der fehlenden RTTI nicht wiederzunden. Die Zuordnung von MATLAB-Datentypen zu ROOT-Klassen ist nun wie folgt: Integer-Matrizen Float-Matrizen Structure-Arrays Cell-Arrays Strings TArrayI (nicht von TObject abgeleitet) TVectorT<double> TMap TObjArray TObjString Das Abbilden von beliebig komplex verschachtelten Datentypen (Structure- und Cell-Arrays) habe ich jeweils in einer rekursiven Funktion (mx2root() bzw. root2mx()) implementiert. 2.3 Implementierung der Schnittstelle Die Funktion saveroot() Bei der Implementierung begann ich mit der Funktion saveroot(). Mit Hilfe des ROOT Object Browsers und des CINT konnte ich während der Entwicklung immer wieder ihre Funktionalität überprüfen, ohne parallel loadroot() entwickeln zu müssen. Die als Vorbild dienende MATLAB-Funktion save() hat folgende Syntax: 11

13 2.3. IMPLEMENTIERUNG DER SCHNITTSTELLE save save filename save filename content save filename options save filename content options save('filename', 'var1', 'var2',...) Um maximale Transparenz zu erreichen, sollte saveroot() bei gleichen Eingaben das gleiche Verhalten haben wie save(). Die gewählten Variablen werden in die angegebene Datei gespeichert. Wird nur ein Dateiname angegeben, werden alle Variablen des aktuellen Workspaces in die Datei gespeichert. Wird auch kein Dateiname angegeben, so werden sie in eine Datei mit Namen matlab.mat bei save() bzw. matlab.root bei saveroot() gespeichert. Da es sich bei meiner Arbeit um eine Konzeptstudie handelt, habe ich Optionen und spezielle Angaben in der Variablenliste auÿer Acht gelassen. Das Besondere ist meine Erweiterung des Dateinamens: Ein Rautezeichen (#) trennt physikalischen Dateipfad und virtuellen ROOT-Pfad, sodass man bspw. die obige Verzeichnishierarchie (Abb. 1.1) in einer einzigen ROOT-Datei unterbringen könnte. Der Pfad zum Beispiel in Abb. 2.1: /home/kissel/matlab.root#bla/blubb /physikalischer/pfad#virtueller/pfad Für das Anlegen der Ordnerstruktur innerhalb einer ROOT-Datei muss ich einen ROOT-Pfad in seine Bestandteile zerlegen, weshalb ich die von PHP bekannte Funktion explode(), die einen gegebenen String an einem Teilstring trennt, in MATLAB implementierte. Ich implementierte auch das Gegenstück implode(). Da saveroot() nur mit den Namen der zu speichernden Variablen aufgerufen wird und diese im Workspace der aufrufenden Funktion liegen, hat saveroot() keinen direkten Zugri auf diese Variablen. Vorverarbeitungen und den eigentlichen Aufruf der MEX-Funktion mexsaveroot() machte ich deshalb mit den MATLAB-Funktionen assignin() und evalin(), denen man einen bestimmten Workspace vorgeben kann. Die Parameterliste für evalin() füge ich mit implode() zu einem String zusammen Die Funktion loadroot() Die Syntax der MATLAB-Funktion load() ist recht ähnlich der save()-syntax: load load filename load filename X Y Z... load filename -regexp expr1 expr2... load -ascii filename load -mat filename S = load('arg1', 'arg2', 'arg3',...) 12

14 2.3. IMPLEMENTIERUNG DER SCHNITTSTELLE Auch hier strebte ich mit loadroot() eine ähnliche Schnittstelle an. Der Schalter, ob es sich bei der angegebenen Datei um eine ASCII- oder MATLAB-Datei handelt, entfällt, weil nur ROOT-Dateien geladen werden können. Die Suche nach Objekten mit regulären Ausdrücken wird bei USCT nicht verwendet, weshalb ich auf ihre Implementierung verzichtet habe. Die restlichen Parameter verhalten sich wie bei save() beschrieben. Auch mexloadroot() wird nicht direkt von loadroot() aufgerufen, sondern indirekt über evalin() aus dem Kontext der aufrufenden Funktion. Damit kann mexloadroot() die geladenen Variablen direkt in den Workspace dieser Funktion schieben, da dieses Schieben nur über jeweils eine Stufe der Aufrufhierarchie bzw. direkt in den Basis-Workspace möglich ist. Zuvor wurden die gewünschten ROOT-Objekte geladen und in MATLAB-Variablen konvertiert. 13

15 2.4. ZUSAMMENSPIEL DER SCHNITTSTELLENTEILE Diverse Probleme bei der Implementierung Objekte, die in einem TObjArray oder einer TMap abgelegt sind, werden standardmäÿig einzeln abgespeichert, was das Wiedernden beim Laden erschwert. Es gibt aber einen Schalter in der Write()-Methode, der diese veranlasst, sie im Ganzen abzuspeichern. Obwohl TFile von TDirectory abgeleitet ist, führt die Benutzung eines TFile- Objekts zur Verzeichnisverwaltung zu fehlerhaften Ergebnissen. Ich folgte der Empfehlung im ROOT-User Guide, stattdessen die globale Variable gdirectory zu verwenden. Die TDirectory-Klasse stellt keine Methode zur Verfügung, um zu überprüfen, ob ein Verzeichnis ein bestimmtes Objekt enthält. Mit einem kleinen Umweg ist dies dennoch möglich: Mit gdirectory->getlistofkeys() erhält man eine TList der TKeys im aktuellen Verzeichnis. Und die Klasse TList hat eine Methode Contains(). Nicht initialisierte Pointer führten in meinen MEX-Funktionen immer wieder zu Schutzverletzungen (Segmentation Violations), obwohl ich vor deren Initialisierung nicht lesend auf sie zugreifen wollte. Deshalb initialisiere ich alle Pointer, für die später dynamisch Speicher allokiert werden soll, mit 0. Es wird auÿerdem empfohlen, die MEX-Speicherverwaltung zu nutzen, anstatt der von C++. Im Falle eines Absturzes kann MATLAB dann dynamisch allokierten Speicher wieder freigeben. Ab einer gewissen Entwicklungsstufe von saveroot() entwickelte ich loadroot(). Trotz deutlicher Parallelen in der Signatur der Funktionen können die Parameter nicht alle durch eine gemeinsame Funktion überprüft werden. Das liegt daran, dass diese Überprüfung teilweise im Kontext der aufrufenden Funktion stattnden muss, um z.b. beim Speichern die Existenz der zu speichernden Variablen festzustellen. Das wäre bei einer gemeinsamen Funktion nur wieder saveroot() oder loadroot(). Das Prüfen und Zerlegen des Dateinamens ist daher die einzige Funktionalität, die ich auslagern konnte (processfilename()). Auf C++-Seite können die Gemeinsamkeiten wegen des Include-Mechanismus besser genutzt werden, sodass ich einige Funktionen und Konstanten in eine gemeinsame Header-Datei mexroot.h auslagern konnte. 2.4 Zusammenspiel der Schnittstellenteile Wie läuft nun ein Speicher-Lade-Zyklus ab? saveroot() wird mit einem Dateinamen und einer Reihe von Variablennamen aufgerufen. Der Dateiname wird in den physikalischen Pfad und ein Array der virtuellen Verzeichnisnamen zerlegt und zusammen mit einem Array der Variablennamen in der Variablen rootinfo abgelegt, die wiederum von assignin() in den Workspace der aufrufenden Funktion kopiert wird. Das ist eine Vorverarbeitung, die nicht mehr in der MEX-Funktion gemacht werden muss. Auÿerdem werden Variablen wie oben beschrieben (1.3) in einem Array übergeben, sodass die Namen separat 14

16 2.4. ZUSAMMENSPIEL DER SCHNITTSTELLENTEILE transportiert werden müssen. Über evalin() wird dann mexsaveroot() mit rootinfo und den gewünschten Variablen (nicht ihren Namen) im Kontext der aufrufenden Funktion aufgerufen. mexsaveroot() legt dann die ROOT-Datei und darin den virtuellen Pfad an. Die Variablen werden von der rekursiven Funktion mx2root() in ROOT-Objekte gewandelt und unter ihrem Namen persistiert. rootinfo wird danach nicht mehr benötigt und gelöscht. Auch loadroot() wird mit einem Dateinamen und einer Reihe von Variablennamen aufgerufen, woraus rootinfo generiert wird. Die MEX-Funktion mexloadroot() wird wiederum indirekt über evalin() mit rootinfo gerufen. mexloadroot() önet die ROOT-Datei und navigiert zum angegebenen virtuellen Verzeichnis. Dann werden die gewünschten ROOT-Objekte einzeln geladen, mit root2mx() in MATLAB-Variablen konvertiert und über mexputvariable() in den Workspace der aufrufenden Funktion geschoben. Zum Schluss wirdrootinfo gelöscht. Abb. 2.4: Aufrufhierarchie und zeitliche Aufruf-Reihenfolge 15

17 Kapitel 3 Ergebnisse 3.1 Tests Stabilitätstests Während der Entwicklung traten Stabilitätsprobleme auf, die ich auf Fehler in meiner Pointer-Arithmetik zurückführen konnte. Bestimmte Fehler, bei denen sich MATLAB unter Linux ohne Meldung beendete, konnte ich aber nicht identi- zieren (Windows-Exception: 0xc ). Um ein Speicherleck auszuschlieÿen, machte ich vor den eigentlichen Funktionstests einen Belastungstest mit vielen Schreibvorgängen: Ich habe vier kleine, mit Einsen gefüllte Double-Matrizen verschiedener Dimensionalität 1000mal in dieselbe Datei geschrieben. Die eigentlich Funktionalität blieb unauällig, die Abstürze folgten immer eine gewisse Zeit später bei einer anderen MATLAB-Tätigkeit. Sie sind nicht direkt reproduzierbar, treten aber mit einer groÿen Wahrscheinlichkeit auf. Unter Windows treten sie deutlich seltener auf als unter Linux. Vermutlich führt die Speicherbereinigung am Ende meines Programms zu diesen Problemen und unter Windows wird diese anders gehandhabt. Evtl. wird zu viel bereinigt, sodass eine andere MATLAB-Funktionalität beeinträchtigt wird Funktionstests Die zu speichernden Variablen können nicht beliebig groÿ sein, da sie während der Transformation in ROOT-Variablen zeitweise zweimal im Hauptspeicher liegen. Da groÿe, numerische Matrizen am stärksten am Datenumsatz bei USCT beteiligt sind, habe ich eine 80 MB-Double-Matrix wiederum 1000mal in dieselbe Datei geschrieben. Diese war zunächst mit Einsen, in einem weiteren Durchlauf mit Zufallszahlen gefüllt. 16

18 3.1. TESTS Um die Fähigkeit meiner Software zu zeigen, mit den verschiedenen, mitunter tief verschachtelten MATLAB-Datentypen umzugehen, habe ich als nächstes 1000mal ein 100-Elemente-Struct zufällig mit vier Variablen (Double-Matrix, Structure- und Cell-Array und String) gefüllt und abgespeichert. Das Gleiche habe ich mit einem Elemente-Cell-Array wiederholt. Zum Vergleich habe ich diese Tests auch mit der MATLAB-Funktion save() gemacht. Um den Umgang mit Anwendungsdaten zu demonstrieren habe ich ein USCT-Experiment mit ca. 840 MB MATLAB-Dateien in einer ROOT-Datei gespeichert. Die in Abb. 3.1 aufgeführten Ergebnisse lassen folgende Interpretationen zu: Aus den Unterschieden beim Speicherbedarf von save() und saveroot() kann man schlieÿen, dass die verwendeten Kompressionsmethoden hier erheblichen Einuss haben und sich deutlich unterscheiden. Auch der groÿe Unterschied beim Zeitbedarf von saveroot() bei den Matrix-Tests lässt sich so erklären. Insgesamt benötigt save() deutlich weniger Zeit (Faktor 2-5) als saveroot(). Das war jedoch zu erwarten, denn save() ist keine Schnittstelle über mehrere Stufen. Abb. 3.1: Ergebnisse der Tests Die zeitlichen Diskrepanzen zwischen Linux- und Windows-Ergebnissen kommen hauptsächlich durch die unterschiedliche Ausstattung der Rechner zustande. Auÿerdem waren nicht genau die gleichen MATLAB-Versionen installiert. Beim Laden habe ich nur getestet, ob es fehlerfrei funktioniert. Gespeicherte und wieder geladene numerische Matrizen habe ich mit Erfolg vom Original abgezogen. Wieder geladene Cell-Arrays hatten die gleiche Gröÿe wie die Originale und auch Stichproben einzelner Zellen waren erfolgreich. Bei Tests mit verschachtelten Structure-Arrays musste ich feststellen, dass ich diese beim Laden falsch wieder zusammensetze. Ich war davon ausgegangen, dass es sich dabei um simple Assoziativ-Arrays handelt (Abb. 3.2), es sind aber kompliziertere Gebilde: 17

19 3.2. DISKUSSION 3.2 Diskussion Abb. 3.2: Überraschung bei den Structure-Arrays In dieser Praxisphase habe ich mich mit ROOT als Option für die Datenhaltung bei USCT beschäftigt. Ich habe eine eigene I/O-Schnittstelle auf Basis von MEX entwickelt, die ROOT-Bibliotheken und Header-Dateien nativ einbindet. Man kann damit Double-Matrizen, Structure- und Cell-Arrays und Strings aus MATLAB heraus in ROOT-Dateien ablegen. Meine Software unterstützt zur Zeit die für USCT wichtigsten MATLAB-Datentypen. Für völlige Transparenz zu den MATLAB-Pendants müssten weitere Datentypen wie z.b. Logik-Matrizen und die bei save() und load() vorgesehenen Optionen in ROOT implementiert werden. Insbesondere Integer-Matrizen bedürfen noch einer adäquaten Umsetzung. Man könnte sie z.b. binär in einem TBits-Container ablegen. Die anderen Datentypen sind in nativen ROOT- Klassen abgebildet. Die Array-Typen habe ich allerdings umformatiert. Mit weiterem Aufwand könnte man diese Umformung vermeiden bzw. reduzieren. Problematisch bei der Suche nach geeigneten Klassen ist, dass es in ROOT so viele Basisklassen gibt. Die Kompatibilität zu anderen Datenauswertesystemen wäre besser, wenn es für alle Datenklassen eine gemeinsame Basisklasse gäbe, z.b. TObject. Der direkte Datenaustausch mit externen Projekten, die ROOT nutzen (wollen), ist nun möglich. Um die Ziele Datenverarbeitung im Grid und Datenhaltung in Datenbanken weiter zu verfolgen, wäre ein nächster Schritt, meine Software um die I/O- Klassen TNetFile und TSQLFile zu erweitern. Das ist ohne Weiteres möglich, da diese von der von mir verwendeten I/O-Basisklasse TFile erben. Die Probleme mit Programmabstürzen lassen sich eingrenzen, wenn man weniger oft Speicher dynamisch allokiert. Weitere Speicher-Fehlersuche ist möglich, z.b. mit GDB oder durch Dazubinden der Bibliothek mcheck. ROOT komprimiert standardmäÿig Dateien mit der ersten von neun gzip-kompressionsstufen. Je nach Bedarf könnte man Geschwindigkeit (Stufe 0, store) oder Speicherbedarf (Stufen 2-9) optimieren. Dies ist über die TFile-Methode SetCompressionLevel() möglich. 18

20 3.2. DISKUSSION Um die gesamte USCT-Datenhaltung nach ROOT portiert werden kann, wäre es sinnvoll, auch die Datenerfassung in ROOT-Dateien zu machen. Mit der nächsten Hardware-Generation kommt eine neue Akquisitions-Software, für die ROOT in Erwägung gezogen wird. Die aktuelle Akquisitions-Software ist in Java geschrieben ist, sodass die saveroot()-funktion hier nicht verwendet werden kann. Theoretisch wäre es möglich, auch die Bildrekonstruktions-Software in ROOT zu implementieren. Das wäre interessant wegen der eingebauten Thread-Fähigkeiten. Trotz oener Fragen werde ich meine Software in den Nutzergemeinden von ROOT und MATLAB publizieren und bin gespannt auf das Echo. Bei meinen Recherchen habe ich nämlich den Eindruck gewonnen, dass diese Problematik der Datenaustausch von ROOT und MATLAB schon länger einer Lösung bedarf. 19

21 Literaturverzeichnis [1] Nicole Ruiter. Ultraschall Computertomographie, April USCT. [2] The ROOT System Homepage, Juli [3] The MathWorks - MATLAB and Simulink for Technical Computing, Juli [4] Ingo Strauch. H1 ROOT Howtos, April strauch/root. [5] René Brun. ROOT Enhancement Request, Oktober kmself/sas/root_mail.txt. [6] Tony Johnson. Java interface to Root, Juni

22 Anlagenverzeichnis MATLAB-Quelltexte Anlage 1: saveroot.m Anlage 2: loadroot.m Anlage 3: processfilename.m Anlage 4: reformat.m Anlage 5: explode.m Anlage 6: implode.m C++-Quelltexte Anlage 7: mexsaveroot.cpp Anlage 8: mexloadroot.cpp Anlage 9: mexroot.h 21

23 Abbildungsverzeichnis 1.1 Verzeichnishierarchie der USCT-Daten Logos von CERN und ROOT Die ROOT-Konsole Der ROOT Object Browser Beschreibung des ROOT-Dateiformats Umwandlung numerischer Matrizen in Arrays mit Kopormat Aufrufhierarchie und zeitliche Aufruf-Reihenfolge Ergebnisse der Tests Überraschung bei den Structure-Arrays

24 Abkürzungsverzeichnis CERN CINT FTP GCC GDB GNU gzip IPE I/O LGPL MATLAB MEX OpenGL PHP RTTI USCT Conseil Européen pour la Recherche Nucléaire C++ Interpreter File Transfer Protocol GNU Compiler Collection GNU Debugger GNU's not Unix (rekursives Akronym) GNU zip Institut für Prozessdatenverarbeitung und Elektronik Input/Output Lesser General Public License Matrix Laboratory MATLAB External Interfaces Open Graphics Library PHP: Hypertext Processor (rekursives Backronym) Runtime Type Information Ultraschall-Computertomographie 23