Masterarbeit. Implementation eines CUDA basierten Kalman-Filters zur Spurrekonstruktion des ATLAS-Detektors am LHC

Größe: px
Ab Seite anzeigen:

Download "Masterarbeit. Implementation eines CUDA basierten Kalman-Filters zur Spurrekonstruktion des ATLAS-Detektors am LHC"

Transkript

1 Fachhochschule Münster Masterarbeit Implementation eines CUDA basierten Kalman-Filters zur Spurrekonstruktion des ATLAS-Detektors am LHC Rene Böing, B.Sc. Matrikelnummer: Oktober 2013 Betreuer: Prof. Dr. rer. nat. Nikolaus Wulff Zweitprüfer: Dr. Sebastian Fleischmann

2 Urheberrechtlicher Hinweis Dieses Werk einschließlich seiner Teile ist urheberrechtlich geschützt. Jede Verwertung ausserhalb der engen Grenzen des Urheberrechtgesetzes ist ohne Zustimmung des Autors unzulässig und strafbar. Das gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen sowie die Einspeicherung und Verarbeitung in elektronischen Systemen. I

3 Zusammenfassung Die vorliegende Masterarbeit thematisiert die Implementation eines Kalman-Filters für kleine Matrizen auf Basis der von NVIDIA entwickelten Programmiersprache CUDA. Die Implementation ist dabei speziell auf die Spurrekonstruktion von Ereignisdaten des ATLAS-Experiments am CERN zugeschnitten. Es werden ausgehend von einer selbst entwickelten Grundimplementation verschiedene Verfahren zur Optimierung der Berechnungsgeschwindigkeit beschrieben. Neben der erfolgreichen Implementation des Kalman-Filters wird ein Vergleich der Laufzeit mit einer CPU basierten Lösung durchgeführt, um abschließend zu ermitteln, ob die Verwendung von Grafikkarten die Berechnungsdauer des Kalman-Filters reduzieren kann. Die Arbeit zeigt, dass die Verwendung von CUDA die Verarbeitungsdauer im Vergleich zu einer CPU basierten Lösung auf ein Viertel reduzieren kann. Abstract This master s thesis describes the implementation of a Kalman filter using GPU technology based on NVIDIA CUDA. Besides the objective of implementing the Kalman filter this thesis answers the question of whether or not such an implementation is faster than a CPU based approach. The Kalman filter implementation is customized to fit the needs of track reconstruction for the ATLAS expirement located at CERN. Based on a self-developed basic implementation, various strategies to optimize and enhance the speed, at which the most recent released graphic solutions of NVIDIA produce results, are applied. As a result the final implementation finishes the calculations in a quarter of the time needed by the CPU implementation. II

4 Danksagung Ich möchte mich an dieser Stelle bei allen beteiligten Personen bedanken, die das Anfertigen und Fertigstellen dieser Masterarbeit ermöglicht haben. Ich möchte mich an dieser stelle ganz besonders bei Herrn Prof. rer. net. Nikolaus Wulff bedanken, der durch die Kontaktaufnahme mit der Wuppertaler ATLAS- Gruppe diese Arbeit möglich gemacht hat. Herrn Dr. Sebastian Fleischmann bin ich für die vielen Hilfestellungen im Bereich Hochenergiephysik, sowie seiner Betreuung und Beratung bei Implementationsdetails, zu großem Dank verpflichtet. Auch möchte ich der restlichen ATLAS Forschungsgruppe und insbesondere Herrn Prof. Dr. Peter Mättig für die gute Zusammenarbeit danken. Ich möchte mich zudem bei meinem Projektpartner Maik Dankel für die überaus gute Zusammenarbeit bedanken. Auch die Masterprojektgruppe bestehend aus Philipp Schoppe und Matthias Töppe verdient meinen Dank. Weiterhin bedanke ich mich bei Nancy Linek, Marina Böing und nochmals Maik Dankel für das Korrekturlesen dieser Arbeit. III

5 Inhaltsverzeichnis Inhaltsverzeichnis Zusammenfassung Abstract Abbildungsverzeichnis Tabellenverzeichnis Listings II II VI VII VIII 1 Einleitung Motivation Ziele der Arbeit Kalman-Filter Grundlagen GPU-Architektur Hardwaremodell Warps Hardwareeigenschaften und Programmierung NVIDIA CUDA Definition Host und Device Compute Capability Kernel Grundlegendes Threadingmodell Streams CUDA Programmierung CUDA Host und Device Threadingmodell Shared Memory in CUDA CUDA Streams API-Fehler abfangen Deviceeigenschaften Verwaltung mehrerer Devices IV

6 Inhaltsverzeichnis 3.8 Grafikkartenspeicher allozieren und verwalten Synchronisation von Threads Implementierung Detektordaten Kalman-Filter Initialisierung Projekteigenschaften Funktionsimplementierung Devicefunktionen Hostfunktion Optimierungsschritte Datenstrukturen Blobdaten und Pinned Memory CUDA Streams OpenMP Deviceauslastung steigern Numerische Genauigkeit und Symmetrie Performance Performancevergleich der Optimierungsstufen Performancevergleich OpenCL vs. CUDA vs. CPU Fazit 73 7 Ausblick 74 8 Anhang 76 Literatur 78 Eidesstattliche Erklärung 82 V

7 Abbildungsverzeichnis Abbildungsverzeichnis 1 ATLAS-Detektor Vergleich der Messpunkte und echter Spur Kalman-Filter korrigierte Spur Durch Smoothing korrigierte Spur GK110 Blockdiagramm SMX Blockdiagramm Warp Scheduler Transferraten in Abhängigkeit der Datenmenge Verschiedene Speicherzugriffsmuster Skalierbarkeit über mehrere Devices Zusammenfassung des Threadingmodells Abarbeitung von Streams Vergleich der Kopiervorgänge Projekt Erstellungsablauf Aufbau des Verarbeitungsgrids Ausgabe des Visual Profilers Grafischer Laufzeitvergleich Technische Spezifikation der Compute Capabilities VI

8 Tabellenverzeichnis Tabellenverzeichnis 1 Parameterübersicht CUDA-Kernelaufruf CUDA Deviceeigenschaften Inlinefunktionen Matrix/Vektor-Multiplikation Zu übertragende Datenmengen pro Spur Zu übertragende Datenmengen pro Event Verwendetes Computersystem Verwendeter Testdatensatz Performancevergleich Angepasster Performancevergleich Performancevergleich CPU/CUDA/OpenCL VII

9 Listings Listings 1 Beispielcode für Warpdivergenz Funktionskopf für GPU-Funktion Aufruf GPU-Funktion CUDA Threadindizes CUDA Blockgrößen CUDA Gridposition, sowie Gridgröße Beispielanwendung der Threadposition Beispielaufruf im Host-Code Mehrdimensionaler Beispielaufruf im Host-Code Shared Memory mit statischer Größe Shared Memory mit dynamischer Größe Beispielaufruf im Host-Code mit dynamischem Shared Memory Deklaration eines Streams Initialisierung eines Streams Löschen eines Streams Status eines Streams Lesbarer Fehlercode Error Handler Anzahl der Devices ermitteln Deviceeigenschaften ermitteln Ein Device auswählen Streambindung an ein Device Speicher auf einem Device reservieren Speicher auf einem Device freigeben Daten zum Device kopieren Asynchrones Kopieren Allokation von Pinned Memory Freigabe von Pinned Memory Struktur eines Events Struktur eines Tracks Struktur eines Hits Spurrekonstruktionsdatenstruktur VIII

10 Listings 33 Matrixindizes und Funktionskopf Start der Kalman-Filterung Prädiktionsphase des Kalman-Filters Matrix-Vektor-Multiplikation Kalman-Gain Implementation 1D Kalman-Gain Implementation 2D-Invertierung Kalman-Gain Implementation 2D p k k Implementation 1D p k k Implementation 2D C k k Implementation 1D C k k Implementation 2D Anpassung Speichern der Updateergebnisse aus dem Hinweg Implementation Smoothing Pseudocode Hostfunktion Spurdaten auslesen und verarbeiten Allokation und Kopieren von Daten Starten des Kalman-Filter Kernels Pseudocode Anpassung der Datenstrukturen Datenblob als Pinned Memory Pseudocode streambasiertes Filtern Indexberechnung für höhere Auslastung Shared Memory Benutzung bei gesteigerter Auslastung Matrixmultiplikation transponiert IX

11 Listings Abkürzungsverzeichnis AVX Advanced Vector Extensions CPU Central Processing Unit CUDA Compute Unified Device Architecture FCFS First Come First Serve FLOPS Floating Point Operations Per Second GPU Graphics Processing Unit GPGPU General Purpose Graphics Processing Unit SIMD Single Instruction Multiple Data SM Streaming Multiprocessors SMX Next Generation Streaming Multiprocessors SSE Streaming SIMD Extensions SVD Singular Value Decomposition X

12 1 Einleitung 1 Einleitung Die 1952 gegründete Organisation für Nuklearforschung CERN, welche ihren Namen aus dem französischen Akronym Conseil Européen pour la Recherche Nucléaire ableitet, betreibt unter anderem Grundlagenforschung im Bereich der Teilchenphysik. Zu diesem Zweck werden im Large Hadron Collider, kurz LHC, Protonen und Atomkerne bei sehr hohen Schwerpunktsenergien von bis zu acht Terraelektronenvolt (TeV) zur Kollision gebracht. Die Ergebnisse dieser Kollision geben Aufschluss über die Wechselwirkungen der Teilchen und ermöglichen eine Überprüfung der vom derzeitigen Standardmodell der Physik hervorgesagten Eigenschaften.[1] Am LHC werden sieben Experimente durchgeführt, wobei die Experimente AT- LAS und CMS die beiden größten sind. Die Ergebnisse der beiden Detektoren werden für eine gegenseitige Ergebnisverifizierung genutzt, da beide Detektoren unabhängig voneinander entwickelt und umgesetzt sind. Am ATLAS Experiment beteiligen sich über 3000 Wissenschaftler aus insgesamt 38 Ländern.[2] 1.1 Motivation Der Detektor des ATLAS-Experiments am CERN nimmt pro Sekunde ca. 65 TB Rohdaten auf, welche dann auf ca. 300 MB/s ausgedünnt werden.[4] Es werden dabei sogenannte Events bzw. Ereignisse aufgezeichnet. Als Ereignis wird eine Teilchenkollision im Detektor bezeichnet. Zu diesem Zweck werden Protonen oder ganze Atomkerne auf nahezu Lichtgeschwindigkeit beschleunigt und dann im Detektor des ATLAS Experiments zur Kollision gebracht. Die beiden aufeinander treffenden Teilchenstrahlen besitzen dabei eine Energie von bis zu vier TeV. Durch die Kollision entstehen Bruchstücke in Form von neuen Teilchen. Diese Teilchen wechselwirken mit den im Detektor befindlichen Messinstrumenten, sodass diese die Teilchen registrieren können. In Abbildung 1 ist der Detektor des ATLAS-Experiments dargestellt. Es sind die verschiedenen Detektorlagen abgebildet, welche jeweils mit unterschiedlichen Messinstrumenten ausgestattet sind. Die aufgezeichneten Daten müssen weiter verarbeitet werden. Teil dieses Verarbeitungsprozesses ist die Rekonstruktion der Flugbahn der Teilchen, sowie die Suche nach dem Entstehungsort. Zu diesem Zweck wird ein Kalman-Filter eingesetzt, welcher in der Lage ist, die systembedingten Ungenauigkeiten der aufgezeichneten Messungen zu verbessern. Der Aufwand diese Datenmengen zu analysieren ist enorm, sodass nach neuen Mitteln und Wegen ge- 1

13 1 Einleitung Abbildung 1: ATLAS-Detektor[3] sucht wird, die Verarbeitungsgeschwindigkeit zu erho hen. Dabei steht nicht nur die verarbeitende Hardware im Fokus der Entwicklung, ebenso werden die verwendeten Algorithmen verbessert oder ersetzt. 1.2 Ziele der Arbeit Das Ziel dieser Arbeit ist es, eine Kalman-Filter Implementation zu entwickeln, welche auf Basis der Programmiersprache CUDA die Berechnung des Kalman-Filters auf einer Grafikkarte ausfu hrt. Die grafikkartenspezifische Implementation wird im Anschluss einem Laufzeitvergleich mit einer auf der CPU rechnenden Implementation, sowie einer OpenCL basierten Lo sung unterzogen. Diese Zeitmessungen zeigen auf, in wie weit der Kalman-Filter unter Verwendung einer CUDA basierten Lo sung beschleunigt werden kann und es wird detailliert beschrieben, welche Techniken zur Beschleunigung und Optimierung des Kalman-Filters beitragen. 1.3 Kalman-Filter Grundlagen Um die Genauigkeit der Messung zu verbessern und damit den wahren Punkt der Messung na her zu kommen, wird der sogenannte Kalman-Filter eingesetzt. Dieser Filter ist 1960 von Herrn Rudolph E. Kalman in seinem Paper A New Approach 2

14 1 Einleitung to Linear Filtering and Prediction Problems veröffentlicht worden und wird heute in vielen Bereichen, wie beispielsweise in der Luft- und Raumfahrt, eingesetzt. In diesem Kapitel wird die Arbeitsweise des Kalman-Filters erläutert.[5] Abbildung 2: Vergleich der Messpunkte und echter Spur In Abbildung 2 ist ein Vergleich von Messpunkten und der echten Spur zu sehen. Es wird deutlich, dass die Spur des Teilchens nicht exakt mit den Messpunkten übereinstimmt. Dies liegt an mehreren Faktoren, wie beispielsweise die Auflösung der Detektorlage, Anregung von mehreren benachbarten Messpunkten, Störungen, etc. Der Kalman-Filter verbessert unter Minimierung der Fehlerkovarianz die Genauigkeit der Messung. Ein großer Vorteil im Vergleich zu anderen Verfahren ist die endrekursive Arbeitsweise des Filters, welche zur Korrektur der nächsten Messung nur die Ergebnisse der vorherigen benötigt, nicht aber den kompletten Verlauf der Korrekturberechnung. Damit wird sowohl die zu speichernde Datenmenge pro Messpunkt minimiert, als auch die Berechnung des Ergebnisses für einen neuen Messpunkt im Vergleich zu Verfahren, welche die komplette Messreihe neu verarbeiten müssen, vereinfacht. Informationen zur Herleitung einzelner Formeln, sowie Beispiele sind im Paper [6] zu finden. Der Kalman-Filter arbeitet in zwei grundlegenden Schritten. Im ersten Schritt wird eine Prädiktion für die k-te Messung durchgeführt. Hierbei werden die Ergebnisse aus der vorherigen Messung p k 1 k 1 mit der sogenannten Jakobimatrix F k multipliziert. Dies ist die a priori Prognose der Messung und wird mit p k k 1 3

15 1 Einleitung bezeichnet. Daraus resultiert Gleichung 1. p k k 1 = F k p k 1 k 1 (1) Außerdem wird noch die a priori Fehlerkovarianzmatrix C k k 1 über Gleichung 2 geschätzt. Die angegebene Matrix Q k beschreibt das prozessbedingte Rauschen und wird im Falle der Spurrekonstruktion für das ATLAS-Experiment nicht berücksichtigt, sodass die Multiplikation der Jakobimatrix mit der vorherigen Fehlerkovarianzmatrix zur Vorhersage führt. C k k 1 = F k C k 1 k 1 F T k + P k Q k P T k (2) Im Falle der ersten Messung liegen keine vorherigen Werte vor und es müssen Startwerte angenommen werden. Die Bestimmung dieser Startwerte ist mit den hier verwendeten Formeln aus verschiedenen Gründen problematisch. Die vom Teilchen durchquerten Materialien genau zu bestimmen ist eine schwierige Aufgabe, da die bisher geschätzte Flugbahn nicht nahe der echten Flugbahn verlaufen muss. Zudem ist die lineare Approximation des Spurmodels eventuell falsch, falls der verwendete Startwert zu stark von der eigentlichen Spur entfernt liegt. Außerdem kann die Vorhersage komplett fehlschlagen, wenn der vorhergesagte Pfad die nächste Detektorlage nicht schneidet. Eine Lösung für dieses Problem ist die Verwendung einer Referenzspur, welche durch vorangegangene Mustererkennungsverfahren generiert wird, um die Messpunkte zu einer Spur zusammen zu fassen. Der Fit der Spur wird nicht mehr auf den Messpunkten alleine, sondern auf der Differenz der korrespondierenden Messpunkte und Referenzpunkte ausgeführt. Dadurch wird anstatt m k jetzt m k = m k H k p k ref für den Fit verwendet, sodass sich die Startwertproblematik entspannt.[7] Die Wahl der Startwerte ist in Kapitel auf Seite 38 beschrieben. Damit ist die Prädiktionsphase abgeschlossen und es folgt die Aktualisierungsphase des Kalman- Filters. Diese Phase korrigiert die vorhergesagten Ergebnisse der ersten Phase unter Berücksichtigung des eingehenden Messpunktes. Zunächst wird, wie in Gleichung 3 dargestellt, K k berechnet, welches als Kalman-Gain bezeichnet wird. Der Kalman- Gain minimiert die a posteriori Fehlerkovarianz.[8] Die angegebene Matrix H k dient als Transformationsmatrix von einer Dimension in eine Andere. Dies ist im Falle der Spurrekonstruktion wichtig und wird im Kapitel auf Seite 39 näher beleuchtet. K k = C k k 1 H T k (V k + H k C k k 1 H T k ) 1 (3) 4

16 1 Einleitung Die Aktualisierung des vorausgesagten Wertes p k k 1 wird, wie in Gleichung 4 angegeben, berechnet. Der neue Messwert aus der Messreihe ist im Vektor m k gespeichert. p k k = p k k 1 + K k (m k H k p k k 1 ) (4) Zudem kann die Fehlerkovarianzmatrix C k k mit Hilfe von Gleichung 5 berechnet werden. C k k = (I K k H k )C k k 1 (5) In Abbildung 3 ist beispielhaft die Korrektur der Spur angegeben, welche die Mess- Abbildung 3: Kalman-Filter korrigierte Spur punkte und die echte Teilchenspur zumindest für die Messungen m l, l > k die Spur näher an das wahre Ergebnis bringt. Um die vorherigen Messungen zu verbessern, fehlen dem Kalman-Filter einige Informationen. Dieser Informationsgehalt kann gesteigert werden, da der Kalman-Filter für die Spurrekonstruktion benutzt wird und damit alle Messpunkte bereits vorliegen. Um die Informationen der letzten Messungen bei der Berechnung der ersten Messungen zu beachten, werden weitere Schritte durchgeführt, um ein möglichst optimales Ergebnis für jeden Messpunkt zu bekommen, in dem alle vorhandenen Informationen eingeflossen sind. Hierfür wird ein Smoothing-Verfahren eingesetzt. Für das in diesem Projekt verwendete Smoothing wird der Kalman-Filter zweimal auf alle Messpunkte, mit jeweils der entgegen gesetzten Richtung, angewendet. 5

17 1 Einleitung Anschließend werden die jeweiligen Ergebnisse über die Fehlermatrizen gewichtet zusammengefasst. K k = C f k k (Cf k k + Cb k k) 1 (6) Die Gewichtung wird mittels Gleichung 6 berechnet, wobei zu beachten ist, dass das f in C f k k die Matrix der Vorwärtsrichtung bezeichnet und dementsprechend b in C b k k die Matrix der Rückwärtsrichtung. p k = p f k k + K k(p b k k 1 + p f k k ) (7) Um das, durch das Smoothing korrigierte, p k zu bestimmen, muss, wie in Gleichung 7 angegeben, aus der Vorwärtsrichtung das aktualisierte p k k und für den Rückweg das prognostizierte p k k 1 genutzt werden. Dies verhindert eine doppelte Gewichtung des aktualisierten Wertes. C k = (I K k )C f k k (8) Gleichung 8 beschreibt die Berechnung der neuen Fehlerkovarianzmatrix C k. Abbildung 4: Durch Smoothing korrigierte Spur Abbildung 4 veranschaulicht die durch das Smoothing verbesserte Spur. Durch die Berücksichtigung der letzten Messungen sind die beiden ersten Messpunkte korrigiert worden und es zeichnet sich eine weitere Annäherung an die reale Spur ab. 6

18 1 Einleitung 1.4 GPU-Architektur Um zu verstehen, warum GPUs so viel mehr Leistung bieten als moderne CPUs und dennoch nur in speziellen Bereichen schneller sind als eben jene, muss die Architektur moderner GPUs bekannt sein. Die aktuell am weitesten fortgeschrittene GPU wird von NVIDIA unter dem Chipnamen GK110 gebaut. Die hier vorgestellte Architektur la sst sich grundlegend auf fru here Chips und deren Architekturen anwenden, wobei sich Einheitenanzahl und Ausfu hrungsfa higkeiten unterscheiden ko nnen Hardwaremodell Abbildung 5: GK110 Blockdiagramm[14] Das Blockdiagramm in Abbildung 5 stellt den grundlegenden Aufbau dar. PCI Express 3.0 Host Interface U ber dieses Interface ist die GPU mit dem HostSystem verbunden. Die Kommunikation hat eine Bandbreite von knapp 16 GB/s und ist vollduplexfa hig. 7

19 1 Einleitung GigaThread Engine Die GigaThread Engine ist ein in Hardware realisierter Scheduler für zu bearbeitende Daten. Der Scheduler arbeitet auf Block-Ebene (siehe Kapitel auf Seite 11) und weist den SMX-Einheiten entsprechende Arbeitsblöcke zu. Memory Controller GK110 verfügt über insgesamt 6 Memory Controller, welche mit jeweils 64 Bit (insgesamt 384 Bit) an den dahinterliegenden Speicher angebunden sind. Der Speicher kann, je nach Modell, ECC-fähig sein und die maximale Speicherbestückung erlaubt 6 GB Speicher mit einer theoretischen Gesamtbandbreite von ca. 250 GB/s. SMX Die sogenannten Next Generation Streaming Multiprocessors sind am ehesten mit einem CPU-Kern vergleichbar, welcher in der Lage ist, mehrere Threads gleichzeitig auszuführen. Da diese Einheit besonders wichtig im Hinblick auf die Programmierung von GPUs ist, wird die Funktionsweise im folgenden Absatz genauer erläutert. L2 Cache Ähnlich zu einer CPU hat eine GPU mehrere Cache-Stufen. Der L2- Cache dient dabei einerseits regulär als Cache für Speicherzugriffe, andererseits tauschen SMX-Einheiten bei Bedarf Informationen über diese Cachestufe aus. 8

20 1 Einleitung Abbildung 6: SMX Blockdiagramm[15] Abbildung 6 zeigt eine detaillierte Ansicht über eine SMX. Ein Verständnis über die Abarbeitung von Instruktionen, die Anzahl der Register und deren Größe, sowie die Konfigurationsmöglichkeiten der Caches ist essentiell um eine hohe Auslastung der GPU und damit in der Regel einhergehende hohe Anzahl von FLOPS zu erreichen. Instruction Cache Dies ist der Instruktionsspeicher, in dem die Instruktionen für die auszuführenden Warps zwischengespeichert werden. Warp Scheduler Stellt einen weiteren Hardwarescheduler dar (vgl. GigaThread Engine), welcher auf Warp-Ebene agiert. Dieser Scheduler verfügt über zwei Instruction Dispatch Units, welche parallel die Befehle n und n+1 an ein Warp schicken (siehe Abbildung 7). Es sind pro SMX je vier Warp Scheduler vorhanden, welche innerhalb von zwei Takten jeweils 8 Warps mit neuen Instruk- 9

21 1 Einleitung Abbildung 7: Warp Scheduler[16] tionen für die nächsten zwei Takte versorgen können (siehe Kapitel auf Seite 11). Register File Pro SMX stehen Bit Register zur Verfügung, welche in Blöcken von 32 Einheiten zu den jeweiligen Single und Double Precision Cores zugewiesen werden können. Das Limit pro Kern liegt allerdings bei 256 Registern. Kerne Eine SMX des GK110 besteht aus insgesamt 192 Single Precision Kernen, 64 Double Precision Kernen, 32 Special Function Einheiten und 32 Load/Store Einheiten.[9] Shared Memory / L1 Cache Pro SMX sind 64 KB lokaler Speicher verbaut. Dieser Speicher fungiert sowohl als L1 Cache, als auch als sogenannter Shared Memory. Shared Memory ist ein extrem schneller, lokaler Speicher, welcher es erlaubt, innerhalb eines CUDA-Blocks Daten auszutauschen. Die Aufteilung des Speichers in L1-Cache und Sahred Memory kann konfiguriert werden. Mit dem Compute Level 3.5 des GK110 lässt sich der Speicher in 16 KB L1 Cache / 48 KB Shared Memory, 32 KB L1 Cache / 32 KB Shared Memory oder 48 KB L1 Cache / 16 KB Shared Memory aufteilen. Wird eine Konfiguration gewählt, die mit dem aktuell ausgeführten Kernel inkompatibel ist, erfolgt eine automatische Änderung der Eisntellungen. Bei einer Konfiguration von 48 KB L1 Cache und 16 KB Shared Memory und einem Kernel, welcher 40 KB Shared 10

22 1 Einleitung Memory anfordert, wird die Konfiguration an den Kernel angepasst. 48 KB Read-Only Data Cache Repräsentiert einen lokalen Cache für Read-Only Werte. Tex Lokaler Speicher für Texturen. Texturspeicher kann allerdings beliebige Daten enthalten, welche sich in einem Texturformat darstellen lassen Warps Ein Warp ist die kleinste Menge an Threads, welche die GPU einzeln ansprechen kann. Diese Gruppe von Threads muss damit immer die gleichen Instruktionen ausführen. Dies wird durch Abbildung 7 deutlich, da Instruktionen nur an ganze Warps geschickt werden können. Derzeitig haben alle Architekturen von NVI- DIA eine Warpsize von 32 Threads bzw. Cores, welche im Verbund Instruktionen ausführen. Dies kann als eine Art SIMD-Architektur (Single Instruction Multiple Data) interpretiert werden, welche allerdings bei der Implementierung nur indirekt beachtet werden kann, da CUDA keine Unterscheidung von Threads innerhalb oder außerhalb von Warps vorsieht. Sei als Beispiel folgender Code gegeben (CUDAspezifische Befehle werden im Kapitel 2 auf Seite 15 erläutert): // Laenge der Arrays sei 32 == Warpsize // threadindex sei entsprechend im Intervall [0, 31] global void foocopy32 ( double * src, double * res ) { int threadindex = threadidx. x; if( threadindex < 16) res [ threadindex ] = src [ threadindex ]; else res [ threadindex ] = src [ threadindex ] + 1; } Listing 1: Beispielcode für Warpdivergenz Es scheint, als wenn 16 Threads den if-zweig ausführen und die anderen 16 Threads den else-zweig, dem ist allerdings auf Grund der Warpsize nicht so. Bei der Ausführung durchlaufen alle 32 Threads den ersten Zweig, die Ergebnisse der ersten 16 werden gespeichert. Anschließend führen alle 32 Threads den else-zweig aus. Dort werden die Ergebnisse der ersten 16 Threads verworfen. Es sollte bei der Programmierung darauf geachtet werden, Code-Divergenzen innerhalb eines Warps zu vermeiden. Weitere Beispiele und Vermeidungsstrategien sind im Kapitel 2 zu finden. 11

23 1 Einleitung Hardwareeigenschaften und Programmierung Im Anschluss an die Erläuterung der grundlegenden Architektur werden einige Beispiele gegeben, die in der Implementierung des Kalman-Filters eine große Rolle spielen, allerdings nicht auf Grund der verwendeten Sprache, sondern auf Grund der verwendeten Hardware und deren Eigenschaften durchgeführt werden. So werden anders als bei klassischen CPUs einige lokale Speicher nicht automatisch angesprochen und benutzt, sondern müssen im Programmcode selbst explizit angesteuert werden. Da dieser lokale On-Chip-Speicher um Größenordnungen schneller sein kann, müssen diese Hardwareeigenschaften genutzt werden. Kommunikation zwischen System und GPU Die im Kapitel beschriebenen Übertragungsgeschwindigkeiten zeigen auf, dass die Anbindung der Grafikkarte an das Host-System vergleichsweise langsam ist. Dieser Umstand kann sich je nach Problemstellung als relevant erweisen und muss bei der Implementierung des Kalman-Filters beachtet werden. Die erreichte Bandbreite wird dabei maßgeblich von der Größe der zu übertragenen Daten beeinflusst und es spielt neben der Bandbreite auch die Verzögerung für den Start eines Transfers eine Rolle. Dieser Umstand ist für ein PCIe 2.0 und PCIe 3.0 Interface unter Verwendung von Pinned Memory in Abbildung 8 zu sehen. Deshalb sollten ne- Abbildung 8: Transferraten in Abhängigkeit der Datenmenge ben der zu übertragenden Datenmenge die Anordnung und die Größe der einzelnen 12

24 1 Einleitung Datenpakete beachtet werden. Viele kleine Datenpakete sollten, wenn möglich, in eine zusammenhängende Struktur oder in einen Datenblob hintereinander im Host- Speicher liegend mit einem einzigen Transfer zum GPU-Speicher transferiert und auf der GPU entsprechend verarbeitet werden. Coalesced Memory Access Abbildung 9: Verschiedene Speicherzugriffsmuster Wird ein Datum aus dem Hauptspeicher in den lokalen Speicher eines Threads gelesen, so werden automatisch 128 Byte in den L1 Cache der SMX übertragen. Zu beachten ist, dass ab der Kepler-Architetkur (GK110) Ladevorgänge aus dem Hauptspeicher immer im L2 Cache zwischengepeichert werden. [13] Dies führt zu verschiedensten Szenarien von suboptimalen Speicherzugriffen, welche die Bandbreite verschwenden und die Latenz erhöhen können. In Abbildung 9 sind vier verschiedene Szenarien dargestellt, die verdeutlichen, dass unterschiedliche Datenstrukturen die Bandbreite und Anzahl der Schreib-/Lesevorgänge erheblich beeinflussen können. Hierbei wird nicht nur die Speicherbandbreite unnötig verschwendet. Das Blockschaltbild einer SMX (Abbildung 6) zeigt, dass die Anzahl der Load/Store-Einheiten um Faktor sechs geringer ist, als die der single precision Einheiten. Gegeben sei folgendes, konstruiertes Beispiel, in dem 32 Threads eine Aufgabe erledigen, deren Datenstruktur pro Thread genau 32 Floats enthält und damit eigentlich optimale 128 Byte lang ist. Außerdem wird angenommen, dass jeder Thread die Summe der 13

25 1 Einleitung 32 Floats iterativ bilden muss, wobei pro Schritt genau ein Wert hinzu addiert wird. Es gibt verschiedene Möglichkeiten, eine Datenstruktur aufzubauen, welche für diese Applikation funktionieren würde. Beispielstrukturen: Struktur A Die Summendaten für die 32 Threads liegen pro Iteration hintereinander. Im Speicher liegen an der Startadresse 32 Floats für Iteration 1, anschließend 32 Floats für Iteration 2, usw. Die 32 Threads würden alle einen Ladebefehl absetzen, wobei alle Adressen in einen 128 Byte großen Block fallen. Mit dieser Struktur wird pro Iteration genau ein Block gelesen und nur ein Ladebefehl an die entsprechden Load-/Store-Einheiten übergeben. Das heißt dieser Zugriff verwendet so wenig Bandbreite mit so wenigen Ladebefehlen wie möglich. Struktur B Die Summendaten werden pro Element beziehungsweise pro Thread abgelegt. Das heißt, es stehen an der Startadresse 32 Floats, welche für Thread 1 von Iteration 1 bis 32 alle Daten enthält. Anschließend kommen die Informationen für Thread 2 usw. In diesem Fall würden in Iteration 1 32 Ladebefehle ausgeführt werden, von denen jeweils 124 Byte übertragen werden, welche erst in der nächsten Iteration benötigt werden. Der Overhead liegt bei knapp 97 %. Dies kann eventuell durch die Caches abgefangen werden, sodass es zu keinen nachfolgenden Ladebefehlen kommen muss, allerdings setzt dies ausreichend große Caches voraus, welche je nach Algorithmus nicht mehr ausreichend Platz bieten könnten. Es wird deutlich, dass die richtigen Datenstrukturen einen großen Einfluss auf die Durchsatzraten und Latenzen des Arbeitsspeichers auf der GPU haben können. Shared Memory Wie auf Seite 10 beschrieben, ist der sogennante Shared Memory ein lokaler Speicher mit geringer Latenz und hoher Bandbreite. Durch die relativ kleine Größe des Speichers lassen sich jedoch nicht beliebig große Daten innerhalb des Shared Memory verarbeiten und es muss je nach Algorithmus spezieller Gebrauch von diesem Speicher gemacht werden. Oftmals kann es durch den Gebrauch von Shared Memory vermieden werden, aus dem Hauptspeicher gelesene Daten für die aktuelle Berechnung zu verwerfen und später nochmal nachladen zu müssen. [17] 14

26 2 NVIDIA CUDA 2 NVIDIA CUDA CUDA ist eine von NVIDIA entwickelte Sprache für die Grafikkartenprogrammierung. Sie erlaubt es, die Ressourcen der GPU für Berechnungen zu nutzen, ist dabei stark an ANSI-C angelehnt und kann dementsprechend in C/C++ Umgebungen durch einfaches Einbinden der CUDA-Bibliothek verwendet werden. Zudem kann CUDA nativ in der Programmiersprache Fortran verwendet werden und wird von Standards wie beispielsweise OpenACC durch einfaches Einführen von Pragmas unterstützt. Neben diesen Verwendungsmöglichkeiten von CUDA innerhalb bereits existierenden Codes besteht die Option, geschriebenen CUDA-Code in nativen x86-code zu übersetzen. Dies ermöglicht automatischen Gebrauch von Autovektorisierung, SSE-/AVX-Befehlen und Multicore-CPUs zu machen. Damit kann im HPC-Berech zwischen CPUs und GPUs gewechselt werden, ohne einen Algorithmus in mehreren Sprachen oder Implementierungen zu entwickeln. Weiterhin gibt es CUDA-Wrapper für weitere Programmiersprachen, die es erlauben die GPU in Java oder Python zu benutzen. 2.1 Definition Host und Device Den Programmiersprachen CUDA und OpenCL ist es gemein, dass die Grafikkarte nicht automatisch verwendet wird, um Berechnungen auszuführen. Vielmehr muss im Code unterschieden werden, welcher Teil auf der GPU und welcher Teil auf der CPU berechnet werden muss. Die Unterteilung in diese Ebenen erfolgt über entsprechende Befehle im Programmcode, wobei der Teil, der wie von anderen Sprachen gewohnt auf der CPU-Seite ausgeführt wird, dem sogenannten Host entspricht und die NVIDIA Grafikkarten des Systems als Device bezeichnet werden. Das Device bekommt vom Host sogenannte Kernel übergeben, welche ausgeführt werden sollen. Die Abbildung 10 verdeutlicht die Möglichkeit pro Host mehrere Devices zu verwenden. Außerdem besteht die Option, Arbeit beliebig auf die verschiedenen Devices zu verteilen und es ist nicht erforderlich homogene Devices einzusetzen. Es kann jederzeit ein neues Device in ein vorhandenes Gesamtsystem eingebaut und (sofern multiple Devices im Programmcode berücksichtigt werden) automatisch verwendet werden. Voraussetzung für die automatische Verwendung ist allerdings, dass der Code mit dem Featureset (siehe Kapitel 2.2 Compute Capability) des Devices übereinstimmt oder unter bestimmten Voraussetzungen aktueller ist. 15

27 2 NVIDIA CUDA Abbildung 10: Skalierbarkeit über mehrere Devices[20] 2.2 Compute Capability Die Compute Capability beschreibt das verfügbare Featureset eines Devices. Die im Kapitel vorgestellte Kepler GPU auf Basis des GK110 unterstützt die aktuell fortschrittlichste Compute Capability 3.5. Es muss bei der Grafikkartenwahl dennoch auf mehr als nur die Compute Capability geachtet werden. NVIDIA hat mit der CUDA 5 Spezifikation zwar GPUDirect eingeführt, welches direkten RDMA Zugriff auf Peripheriegeräte erlaubt [10], allerdings wird dies beispielsweise nur von den Tesla K20 Karten, nicht aber von der Consumerkarte Geforce GTX TITAN trotz identischen Chips und Computelevel unterstützt[12]. Dies macht das Überprüfen des unterstützten Featuresets per Hand erforderlich. Im Anhang auf Seite 77 sind die verschiedenen Hardwarespezifikationen nach Computelevel aufgeschlüsselt. Sowohl das Computelevel, als auch die technischen Daten eines Devices können zur Laufzeit abgefragt werden, sodass das Programm entsprechend reagieren kann. Ein Beispielcode befindet sich im Kapitel Deviceeigenschaften auf Seite 28. Damit vorhandener Kernelcode auf einem Device höheren Computelevels ausführbar ist, darf der Kernel nicht nur als kompilierter Code im Binaryformat vorliegen, son- 16

28 2 NVIDIA CUDA dern muss in einem virtuellen Codeformat abgespeichert werden. Dieses virtuelle Codeformat erlaubt, Kernel zur Laufzeit für die entsprechende Architektur zu kompilieren, solange das Computelevel des Zieldevices gleich oder höher ist, als das vom Kernel verlangte Computelevel. Hierzu muss der zu kompilierende Code mit speziellen Compilerflags kompiliert werden, bei denen die Zielarchitektur und der Zielcode einer virtuellen Architektur entsprechen. Es wird anschließend PTX-Code generiert, welcher nicht von der GPU ausgeführt werden kann, aber vor der Ausführung in ausführbaren Binärcode übersetzt wird.[23] 2.3 Kernel Ein Kernel ist eine in CUDA geschriebene Funktion (siehe Listing 2 auf Seite 21). Diese Funktionen müssen neben dem Rückgabewert noch mit device versehen werden. Dies teilt dem CUDA-Compiler mit, dass diese Funktion auf einem Device ausgeführt werden muss. Mehr Informationen dazu befinden sich im Abschnitt 3.1 auf Seite Grundlegendes Threadingmodell Die Verwaltung von Threads wird in vier Bereiche unterschiedlicher Dimension aufgeteilt, um die Handhabung von tausenden Threads zu vereinfachen. Thread Ein Thread ist vergleichbar mit einem normalen CPU-Thread. Warp Eine Gruppe von Threads, welche die gleichen Instruktionen ausführen müssen wird zu einem Warp zusammengefasst (siehe Kapitel auf Seite 11). Block Kernel werden in sogenannten Blocks bzw. Blöcken ausgeführt. Ein Block besteht dabei aus n Threads und kann bis zu drei Dimensionen beinhalten. Dies kann hilfreich sein, zwei- oder dreidimensionale Probleme im Programm selbst durch zwei- oder dreidimensionale Darstellung der Threads abzuarbeiten. Dies wird durch ein Beispiel verdeutlicht: Ein Algorithmus addiert zwei l k Matrizen. Jedes Feld in der Ergebnismatrix a ij setzt sich aus der Summe der beiden entsprechenden Felder aus den beiden l k Matrizen zusammen. Dies lässt sich in CUDA leicht durch entsprechende Dimensionierung eines Blocks darstellen, sodass jeder Thread die Indizes i, j 17

29 2 NVIDIA CUDA besitzt, während die Blockgröße genau der Matrixgröße von l k entspricht. Blöcke sind für CUDA die größte zusammenhängende Anzahl von Threads, welche sich an Synchronisierungspunkten synchronisieren müssen. Grid Das sogenannte Grid ist vom Aufbau her vergleichbar mit den Blöcken. Ein Gridelement besteht dabei aus einem Block und dementsprechend einer Menge von Threads und kann ebenso wie die Blöcke bis zu drei Dimensionen haben. Anders als bei den Blöcken synchronisieren sich Gridelemente nicht an Synchronisierungspunkten im Code, sondern die unterschiedlichen Blöcke laufen unabhängig voneinander. Abbildung 11: Zusammenfassung des Threadingmodells[19] Ein zweidimensionaler Aufbau des Threadingmodells ist in Abbildung 11 dargestellt und verdeutlicht die Abhängigkeiten. 18

30 2 NVIDIA CUDA 2.5 Streams Durch den enormen Flopdurchsatz moderner Grafikkarten ist es, je nach Aufgabenstellung, nicht einfach die Einheiten mit genügend Daten zu versorgen. Um dieses Problem zu entschärfen werden sogenannte Streams eingeführt, welche parallel abgearbeitet werden können. Ein Stream ist dabei eine Art Verarbeitungskette, welche die an den Stream gesendeten Befehle abarbeitet. Die Verarbeitung erfolgt dabei nach dem First Come First Serve (FCFS) Prinzip. Hierbei werden die eingehenden Befehle in genau der Reihenfolge abgearbeitet, in der sie an den Stream gesendet werden. Hierzu stellt CUDA eine Reihe von asynchronen Funktionen zur Verfügung, welche es erlauben, mehrere Befehle an einen Stream zu senden, ohne den Host zu blockieren, während die klassischen blockierende Aufrufe automatisch Stream 0 benutzen. Dass die Verwendung von Streams einen Performancevorteil bringen kann, zeigen die verschiedenen Abarbeitungsketten in Abbildung 12. Das obere Beispiel Abbildung 12: Abarbeitung von Streams ohne Streambenutzung zeigt die klassische Arbeitsweise ohne die Verwendung von Streams. Das Hostprogramm kopiert zunächst alle Daten auf das Device, startet dann die Kernelausführung und kopiert die Ergebnisse anschließend zurück. Im Anschluss können die Daten für den nächsten Kernel kopiert werden, etc. Dieses Vorgehen sorgt in diesem einfachen Beispiel dafür, dass die Grafikkarte nur ein Drittel der Laufzeit Berechnungen durchführt. Durch das Benutzen von Streams ist es möglich den Vorgang zu parallelisieren. Hierbei können während der Ausführung des ersten Kernels die benötigten Eingabedaten für den zweiten Kernel kopiert werden. Wenn das Device zudem noch mehrere Kopiereinheiten (Copyengines) bietet, kann mit 19

31 2 NVIDIA CUDA einem dritten auszuführenden Kernel der Kopiervorgang zum Device, der Kopiervorgang vom Device und die Ausführung des mittleren Kernels parallel ablaufen. Die Verwendung von Streams führt hierbei nicht automatisch zu einer besseren Laufzeit, wie das dritte Beispiel zeigt. Hier werden die Kommandos zum Device in falscher Reihenfolge an die Streams geschickt. Durch die nötige Serialisierung der Abarbeitung der Streams wird hierbei die Laufzeit nicht verbessert. Sollte ein Kernel beispielsweise nicht die zur Verfügung stehenden Ressourcen des Devices nutzen, so kann das Device, sofern es concurrent Kernels (siehe Kapitel 3.6 auf Seite 28) unterstützt, multiple Kernel gleichzeitig ausführen, sodass die sogenannte Utilization der Devicekerne entsprechend ansteigt. 20

32 3 CUDA Programmierung 3 CUDA Programmierung Um den im Kapitel 4 ab Seite 36 vorgestellten Code mit der Implementierung des Kalman-Filters besser verstehen zu können, ist eine Einführung in die Syntax der CUDA-Programmiersprache erforderlich. 3.1 CUDA Host und Device Der NVIDIA CUDA-Compiler hat mehr Aufgaben, als nur den Grafikkarten-Code zu kompilieren. Er erlaubt es außerdem, Code für die Grafikkarte (das Device) und für die CPU (der Host) in einer Datei automatisch zu trennen und den Devicecodeabschnitt selbst zu kompilieren, während der Hostcode an den normalen C/C++- Compiler weitergeleitet wird. Diese Unterscheidung kann auf ganze Dateien zutreffen, sodass *.c oder *.cpp Dateien immer direkt an den Hostcodecompiler weitergereicht werden. Sollte eine Datei die CUDA-C Dateiendung *.cu aufweisen, so wird dieser Code auf Device- und Hostcode hin untersucht und von dem entsprechendem Compiler kompiliert. Da CUDA starke Ähnlichkeiten mit C hat, werden durch CU- DA einige neue Kommandos eingefügt, welche diese Unterscheidung ermöglichen. Um eine Funktion foo(float *a, float*b) auf der Grafikkarte berechnen zu lassen muss diese Funktion neben dem typischen Funktionsaufbau aus Rückgabewert Name(Parameter 0,..., Parameter n) {...} allem voran noch der CUDA-Befehl device stehen. device void foo ( float *a, float * b) {... } Listing 2: Funktionskopf für GPU-Funktion Damit wird diese Funktion in Grafikkartencode übersetzt. Der Aufruf dieser Funktion innerhalb des Hostcodes orientiert sich sehr stark an einem normalen Funktionsaufruf, benötigt allerdings mehr als nur die Funktionsparameter um korrekt ausgeführt zu werden. Zu beachten ist ebenfalls, dass die aus C bekannten Funktionsparameter nicht immer vollautomatisch auf die Grafikkarte kopiert werden. In einigen Fällen ist es nötig, die Daten zunächst auf die Grafikkarte zu kopieren. Informationen zum Kopieren von Daten zur Grafikkarte, sowie das reservieren von Grafikkartenspeicher sind im Kapitel 3.8 auf Seite 31 zu finden. Der Aufruf dieser Funktion ist im Code durch das Beispiel 2 gegeben. 21

33 3 CUDA Programmierung... foo <<< Parameter 0,..., Parameter n >>>(a, b);... Listing 3: Aufruf GPU-Funktion Es ist ersichtlich, dass es neben den üblichen Parametern noch eine weitere Parameterart gibt, welche in den Spitzklammern <<<...>>> angegeben wird. Welche Parameter das sind und welchen Einfluss diese haben, wird in den folgenden Abschnitten erläutert. Eine kurze Übersicht der Parameter ist in Tabelle 1 gegeben. Tabelle 1: Parameterübersicht CUDA-Kernelaufruf Parameter Beschreibung Kapitel Auf Seite 1 Dimensionen des Grids Dimensionen eines Threadblocks Dynamische Größe des Shared Memory Verwendeter Stream Threadingmodell Da der Zugriff auf diese Informationen innerhalb des Device-Codes oftmals benötigt wird, um beispielsweise die korrekte Position der vom aktuellen Thread zu bearbeitenden Daten zu ermitteln, existieren im Device-Code eingebaute Variablen, welche von der CUDA-API automatisch gesetzt werden. Um beispielsweise die Position eines Threads innerhalb eines Blocks zu bestimmen, kann folgender Codeabschnitt genutzt werden. int xposblock = threadidx. x; int yposblock = threadidx. y; int zposblock = threadidx. z; Listing 4: CUDA Threadindizes Je nach Aufgabenstellung kann es zudem sinnvoll sein, die absolute Größe eines Blocks im Code zu kennen. Dies geschieht über die folgenden Kommandos: int xblocksize = blockdim. x; int yblocksize = blockdim. y; int zblocksize = blockdim. z; 22

34 3 CUDA Programmierung Listing 5: CUDA Blockgrößen Äquivalent hierzu die Befehle für die Position und Dimension des gesamten Grids. int xposgrid = blockidx. x; int yposgrid = blockidx. y; int zposgrid = blockidx. z; int xgridsize = griddim. x; int ygridsize = griddim. y; int zgridsize = griddim. z; Listing 6: CUDA Gridposition, sowie Gridgröße Die Werte dieser Variablen sind immer benutzerdefiniert. Beim Aufruf eines Kernels muss im ersten CUDA-Parameter die Größe der einzelnen Dimensionen angegeben werden. Der zweite Parameter bezieht sich immer auf die Größe der Blockdimensionen. Gegeben sei der Kernel aus dem Codeabschnitt 7. device void foo ( float *a, float * b) { int mypos = threadidx. x + blockdim. x * blockidx. x; b[ mypos ] = a[ mypos ]; } Listing 7: Beispielanwendung der Threadposition Der Aufruf dieser Funktion muss offensichtlich eine spezielle Größe der x-dimension der Blöcke sowie des Grids angeben. Hierbei ist zu beachten, das bei Verwendung einer eindimensionalen Struktur für die Blockgröße oder die Gridgröße automatisch die verbleibenden Dimensionsgrößen auf Eins gesetzt werden und somit keine überflüssigen Threads erzeugt werden. Seien die Zeiger a und b zwei Arrays mit der Größe n, so könnte die Funktion foo folgendermaßen aufgerufen werden. // a und b seien bereits auf der Grafikkarte alloziert und a wurde kopiert foo <<<n,1 > > >(a, b); Listing 8: Beispielaufruf im Host-Code Dies führt zu einem eindimensionalen Grid der Größe n, wobei jedes Gridelement aus einem eindimensionalen Threadblock der Größe Eins besteht. Da die Größen der einzelnen Dimensionen je nach Compute Capability der verwendeten Hardware unterschiedlich sein können und damit die Größe des zu kopierenden Arrays begrenzen, muss sowohl die Implementierung, als auch der Aufruf der Funktion gegebenenfalls mehrere der verfügbaren Dimensionen nutzen. Dies kann je nach Aufgabenstellung irrelevant sein. Die genauen Größen befinden sich im Anhang und lassen sich von 23

35 3 CUDA Programmierung der Abbildung 18 auf Seite 77 ablesen. Um mehrdimensionale Grids und Blocks zu erzeugen gibt es den dim3 Datentyp von NVIDIA. Dessen Benutzung ist in Listing 9 angegeben. dim3 griddim (n,m, l); dim3 blockdim (32,32,16) ; foo <<< griddim, blockdim >>>(a, b); Listing 9: Mehrdimensionaler Beispielaufruf im Host-Code 3.3 Shared Memory in CUDA In CUDA hat der Programmierer direkten Zugriff auf den schnellen lokalen Shared Memory. Hierfür stellt CUDA im Devicecode den Befehl shared zur Verfügung, welches eine Variable als im Shared Memory liegend markiert. Der Speicherbereich kann sowohl dynamisch zur Laufzeit reserviert werden, als auch statisch im Kernel. Die statische Allokation ist im Listing 10 zu sehen und ähnelt stark der aus C bekannten Allokation von Arrays fester Größe. device void foo100 ( float *a, float * b) { int mypos = threadidx. x + blockdim. x * blockidx. x; shared float c [100]; c[ mypos ] = a[ mypos ] + b[ mypos ];... } Listing 10: Shared Memory mit statischer Größe Die statische Größe macht die Benutzung des Shared Memory Speichers sehr einfach. Die Nachteile sind allerdings identisch zu denen statischer Arrays in normalen C-Code, sodass oft auf dynamische Größen zurückgegriffen werden muss. Die dynamische Allokation erfordert die Kenntnis über die Größe des benötigten Speichers auf der Hostseite. Außerdem ist es notwendig den Speicher kernelseitig in Teilbereiche zu splitten, da nur ein einziger Zeiger auf den Anfang des Speicherbereichs zeigt. Dies ist solange kein Problem, wie es nur ein einziges Array gibt, welches beachtet werden muss. Sollten mehrere Arrays benötigt werden, muss mittels Zeigerarithmetik jeweils der Anfang der Teilbereiche bestimmt werden. Der dynamische Bereich muss außerdem mit dem Keyword extern gekennzeichnet werden. device void foodyn ( float *a, float *b, int items ) { int itempos = threadidx. x + blockdim. x * blockidx. x; extern shared float * c; shared float d [32]; 24

36 3 CUDA Programmierung } float * p1, p2; p1 = c; p2 = c + items ; p1[ itempos ] = a[ itempos ] + b[ itempos ]; p2[ itempos ] = a[ itempos ] * b[ itempos ];... Listing 11: Shared Memory mit dynamischer Größe Listing 11 zeigt ein einfaches Beispiel für zwei Arrays auf den Shared Memory mit dynamischer Größe. Es ist ersichtlich, dass trotz des Einsatzes eines dynamischen Bereiches weiterhin die Möglichkeit besteht, statische Größen zu verwenden. Wichtig ist, dass im Kernel selbst keine Möglichkeit besteht, zu prüfen, ob der dynamische Bereich groß genug ist. Hierbei muss sich auf die Berechnung der Hostseite verlassen werden. Im Fehlerfall können die von der Hostseite bekannten Speicherfehler auftreten, aber genau wie beim Host, müssen diese Fehler nicht zum Absturz oder zu Fehlermeldungen führen. int items = n; foo <<<1, n, sizeof ( float )* items *2 > > >(a, b, items ); Listing 12: Beispielaufruf im Host-Code mit dynamischem Shared Memory Im Listing 12 ist der Kernelaufruf auf Hostseite angegeben. 3.4 CUDA Streams Um die in Kapitel 2.5 beschriebenen Streams zu verwenden, müssen diese zunächst in beliebiger Anzahl erstellt werden. Ein Stream selbst wird dabei durch eine Struktur beschrieben, dessen Inhalt während der Initialisierung von der API gefüllt wird. Streams sind rein hostseitig existent und relevant und spielen somit keine Rolle in einem Kernel. Die Verwaltung der Streams kann, je nach Struktur, komplexe Züge annehmen, sodass im Vorfeld über eine geeignete Anwendung der Streams nachgedacht werden muss. Der Einfachheit halber werden in diesem Kapitel nur die grundlegenden Funktionen anhand eines Beispiels mit einem einzelnen Stream erläutert, die weit komplexere Anwendung von Streams in der Umsetzung des Kalman-Filters wird im Kapitel ausführlich beschrieben. // Host Code... cudastream_t stream1 ; 25

37 3 CUDA Programmierung... Listing 13: Deklaration eines Streams In Listing 13 ist die Deklaration des Datentyps cudastream t eines Streams abgebildet. Um die Variable stream1 benutzen zu können, ist allerdings noch eine Initialisierung nötig, sodass der Code aus Listing 14 eingefügt werden muss. // Host Code... error = cudastreamcreate (& stream1 );... Listing 14: Initialisierung eines Streams Damit ist stream1, sofern kein Fehler zurückgegeben wird, korrekt initialisiert und kann in den verschiedenen API-Aufrufen, wie beispielsweise asynchronen Kopiervorgängen oder in Kernelaufrufen genutzt werden. An dieser Stelle ist anzumerken, dass fast alle API-Funktionen einen Fehlercode zurückgeben, welcher entsprechend überprüft werden sollte. Die Fehlerüberprüfung ist in Kapitel 3.5 auf Seite 27 erläutert. Um einen Stream nach Benutzung zu schließen muss die Destroyfunktion aus Listing 15 aufgerufen werden. Im Gegensatz zur Initialisierung ist der Streamparameter kein Zeiger. // Host Code... error = cudastreamdestroy ( stream1 );... Listing 15: Löschen eines Streams Da die Synchronisation zwischen verschiedenen Streams und das gezielte Warten auf Ergebnisse innerhalb eines Streams essentiell sind, stellt die CUDA API entsprechende Funktionen zur Steuerung und Überwachung eines Streams zur Verfügung. // Host Code... error = cudastreamquery ( stream1 );... error = cudastreamsynchronize ( stream1 );... Listing 16: Status eines Streams Die cudastreamquery-funktion aus Listing 16 ist eine asynchrone Funktion, welche den akutellen Ausführungsstatus des übergebenen Streams zurückgibt. 26

38 3 CUDA Programmierung cudasuccess Der Stream hat alle Aufgaben erfolgreich abgeschlossen. cudaerrornotready Der Stream hat noch weitere Aufgaben auszuführen. cudaerrorinvalidresourcehandle Der angegebene Stream exisitert nicht bzw. nicht mehr. Außerdem kann der Stream alle Fehlercodes von vorherigen asynchronen Aufrufen zurückgeben. Die zweite Funktion aus Listing 16 ist eine synchrone Funktion, welche den Hostprozess bis zur vollständigen Abarbeitung aller noch anstehender Aufgaben oder bis zum Auftreten eines Fehlers blockiert. Die Rückgabewerte, sowie deren Interpretation, ist, bis auf den in diesem Fall unnötigen Rückgabewert cudaerror- NotReady, zur ersten Funktion identisch. 3.5 API-Fehler abfangen Da die meisten Funktionen der CUDA-Bibliothek verschiedenste Fehlercodes zurückgeben können, ist es sinnvoll für die Fehlercodeabfragen eine Funktion oder eine Makrofunktion zu erstellen. Um die Handhabung im Fehlerfall zu vereinfachen, empfiehlt sich eine Makrofunktion, da diese sehr einfach die Zeile und Quellcodedatei des Fehlers ausgeben kann und es keine Kontextswitches auf der CPU zum Aufruf einer Funktion geben muss. Da die Fehlercodes durch ein Enum repräsentiert werden[24], ist es nötig dieses Enum in eine vom Programmierer lesbare Fehlermeldung zu übersetzen. // Host Code... char * errormessage = cudageterrorstring ( error );... Listing 17: Lesbarer Fehlercode Die in Listing 17 dargestellte Funktion gibt einen null-terminiertes char-array zurück, in dem sich eine lesbare Repräsentation des Fehlers befindet. // Host Code # define CUDA_ERROR_HANDLER ( value ) { \ cudaerror_t _m_cudastat = value ; \ if ( _m_cudastat!= cudasuccess ) { \ fprintf ( stderr, " Error %s at line %d in file %s\n", \ cudageterrorstring ( _m_cudastat ), LINE, FILE ); \ exit (1) ; \ 27

39 3 CUDA Programmierung } } Listing 18: Error Handler Im Listing 18 ist die in der Implementierung verwendete Makro-Funktion zum Abfangen von Fehlern dargestellt. Wie dort zu sehen ist, gibt dieses Makro eine Fehlermeldung auf die Konsole aus und beendet anschließend das Programm. 3.6 Deviceeigenschaften Da nicht alle Grafikkarten die gleichen technischen Daten haben, sei es durch eine neue Grafikkartengeneration oder durch Verbreiterung der bestehenden Karten, kann es sinnvoll sein, den Hostcode durch Überprüfen der Funktionalitäten und technischen Daten einer Grafikkarte zur Laufzeit anzupassen. Diese Informationen können dazu dienen, die Auslastung auf zukünftigen Grafikkarten zu erhöhen, indem der Workload dynamisch angepasst wird. Außerdem können diese Informationen dazu genutzt werden, ein Programm kontrolliert zu beenden und den Benutzer darauf hinzuweisen, dass der aktuelle Code für die darunterliegende Hardware angepasst werden muss. Dies kann beispielsweise leicht der Fall sein, wenn sich die Warpsize von bisher 32 auf zum Beispiel 64 erhöhen würde, da oftmals viele Codeabschnitte auf dieser festen Größe aufbauen. Da die Devices nicht identisch sein müssen, müssen diese Informationen für jedes Device abgefragt werden und es muss entsprechend reagiert werden. // Host Code int count ; CUDA_ERROR_HANDLER ( cudagetdevicecount (& count )); Listing 19: Anzahl der Devices ermitteln In Listing 19 ist dargestellt, wie zunächst die Anzahl der im System vorhandenen CUDA-Devices ermittelt werden kann. Jedes Device muss einzeln geprüft werden. Dies ist in Listing 20 dargestellt. // Host Code cudadeviceprop prop [ count ]; for ( int deviceid = 0; deviceid < count ; deviceid ++) { CUDA_ERROR_HANDLER ( cudagetdeviceproperties (& prop [ deviceid ], deviceid )); } Listing 20: Deviceeigenschaften ermitteln 28

40 3 CUDA Programmierung Im Anschluss an die for-schleife befinden sich die Deviceeigenschaften in dem angegebenen prop-array. Der Datentyp cudadeviceprop ist dabei eine Struktur mit allen Daten des Devices. Die für die Umsetzung des Kalman-Filters wichtigsten in CUDA 5.0 enthaltenen Eigenschaften sind in der Tabelle 2 ersichtlich. 3.7 Verwaltung mehrerer Devices Beim Umgang mit mehreren Devices ist zu beachten, dass die Zuweisung eines Devices im Code (siehe Listing 21) nachfolgende Befehle entscheidend beeinflussen kann. // Host Code int deviceid = 0; CUDA_ERROR_HANDLER ( cudasetdevice ( deviceid )); Listing 21: Ein Device auswählen So ist es leicht durschaubar, dass eine Speicherallokation nach dem Setzen von Device 0 nur auf Device 0 durchgeführt wird und dementsprechend der zurückgegebene Zeiger nur auf dem Device gültig ist. Es gibt allerdings weitere Befehle, bei denen dieser Zusammenhang nicht so einfach ersichtlich ist. // Host Code cudastream_t stream1, stream2 ; CUDA_ERROR_HANDLER ( cudasetdevice (0) ); CUDA_ERROR_HANDLER ( cudastreamcreate (& stream1 ));... kernel < < <1,1,0, stream1 >>>( parama );... CUDA_ERROR_HANDLER ( cudasetdevice (1) ); CUDA_ERROR_HANDLER ( cudastreamcreate (& stream2 ));... kernel < < <1,1,0, stream2 >>>( paramb );... CUDA_ERROR_HANDLER ( cudasetdevice (0) ); // NOT WORKING kernel < < <1,1,0, stream2 >>>( parama ); Listing 22: Streambindung an ein Device Im Beispiel aus Listing 22 sind offensichtlich 2 Devices im System verbaut und ansprechbar. Es werden zwei Streams angelegt und je Device der gleiche Kernel mit anderem Parameter ausgeführt. In der letzten Zeile ist ein fehlerhafter Aufruf dargestellt, welcher fehlschlagen wird. Wir sehen, dass parama zwar auf dem Device 0 liegt und der Kernel offensichtlich auf Device 0 ausführbar ist, allerdings verwendet der letzte Aufruf den stream2 zur Ausführung, welcher erst nach der Auswahl des 29

41 3 CUDA Programmierung Variable Beschreibung int ECCEnabled Wird ECC unterstützt und ist aktiv? int asyncenginecount Anzahl der asynchronen Ausführungseinheiten int clockrate Taktrate in khz int computemode Hostthreadzugriffsmuster auf das Device int concurrentkernels Gleichzeitige Ausführung mehrerer Kernels? int kernelexectimeoutenabled Ist die Laufzeit eines Kernels begrenzt? int major Compute Capability des Devices (Vor dem Komma) int maxgridsize[3] Maximale Dimensionsgröße eines Grids int maxthreadsdim[3] Maximale Dimensionsgröße eines Blocks int maxthreadsperblock Maximale Anzahl an Threads in einem Block int maxthreadspermultiprocessor Maximale gleichzeitig ausführbare Anzahl an Threads pro SMX int memorybuswidth Breite der Speicheranbindung in Bit int memoryclockrate Maximale Taktrate des Speichers in khz int minor Compute Capability des Devices (Nach dem Komma) int multiprocessorcount Anzahl der SMX-Einheiten char name[256] ASCII String zur Identifizierung des Devices size t sharedmemperblock Verfügbare Größe des Shared Memory pro Block size t totalconstmem Größe des gesamten konstanten Speichers size t totalglobalmem Größe des gesamten RAMs des Devices int warpsize Größe eines Warps Tabelle 2: CUDA Deviceeigenschaften 30

42 3 CUDA Programmierung zweiten Devices angelegt wird. Dieser Stream hat damit seine Gültigkeit nur auf dem zweiten Device und kann dementsprechend nur dort verwendet werden. Dieses Verhalten kann unerwartet sein und muss dementsprechend besondere Beachtung bekommen. Zudem können Grafikkarten mit verschiedenen Zugriffsberechtigungen konfiguriert werden, welche den Zugriff anderer Prozesse oder mehrerer Threads einschränken können. Welchen Wert diese Eigenschaft für ein spezifisches Device hat, ist in den Deviceeigenschaften gespeichert und kann über Abfrage des Wertes des Computemodes ermittelt werden (siehe Kapitel 3.6). Die möglichen Werte dieser Eigenschaft sind folgende[25]: cudacomputemodedefault Ein beliebiger Thread in einem beliebigen Prozess kann das Device benutzen. cudacomputemodeexclusive In diesem Modus kann nur ein einziger Thread in einem einzigen Prozess das Device benutzen. cudacomputemodeprohibited Hierbei wird dieses Device für alle Threads aller Prozesse geblockt und kann somit nicht genutzt werden. cudacomputemodeexclusiveprocess Hier können beliebig viele Threads eines einzigen Prozesses das Device benutzen. Sollte kein Device als aktives Device im Code ausgewählt werden, wird immer das Device mit der ID 0 angesprochen. 3.8 Grafikkartenspeicher allozieren und verwalten Damit in CUDA Speicher im Arbeitsspeicher der Grafikkarte reserviert wird, müssen ähnlich wie bei C/C++ mallocs durchgeführt werden. Anders als auf dem Hostsystem ist noch ein weiterer Parameter als nur die Größe des Speicherbereiches nötig, um Speicher zu reservieren. // Host Code int * vga_p ; size_t size = 1000* sizeof ( int ); CUDA_ERROR_HANDLER ( cudamalloc (& vga_p, size )); Listing 23: Speicher auf einem Device reservieren 31

43 3 CUDA Programmierung Im Codeabschnitt 23 ist die von der CUDA-Library zur Verfügung gestellte Funktion zur Speicherreservierung dargestellt. Diese Funktion gibt einen Fehlercode zurück und erwartet als Parameter die Adresse eines Zeigers, in dem im Anschluss an den Aufruf die Adresse des Speicherbereiches mit der angegebenen Größe auf der Grafikkarte gespeichert ist. Diese Art des Speichermanagements, mit echten Zeigern auf Speicherbereiche des Devices, hat im Vergleich zu einem einfacheren System bei OpenCL, welches mit einer Art Identifikationsnummer arbeitet, sowohl Vorteile als auch Nachteile. Der wohl größte Nachteil ist die Durchmischung von Zeigern auf der Hostseite. Während die Verwendung von Zeigern in C/C++ komplex werden kann, so wird dieses Problem durch hinzufügen von Devicezeigern weiter verschärft, da dem Programmierer zu jeder Zeit bewusst sein muss, ob ein Zeiger zu dem Host oder zu dem Device gehört. Weiter verschlimmert wird dieser Zustand bei der Verwendung mehrerer Devices, sodass zu der Unterscheidung Host oder Device noch jedes Device unterschieden werden muss. Auf der anderen Seite sind die bekannten Vorteile von Zeigern für die Devicezeiger gültig. Und dies sowohl auf Hostseite, als auch auf der Deviceseite. Einige dieser Vorteile werden im Kapitel 4 deutlich. // Host Code CUDA_ERROR_HANDLER ( cudafree ( vga_p )); Listing 24: Speicher auf einem Device freigeben Nach der Allokation und Verwendung eines Speicherbereichs ist es analog zu Hostspeicher notwendig, diesen wieder freizugeben, um Speicherlecks im Programm zu verindern. Hierfür stellt die CUDA-API die in Listing 24 dergestellte Funktion bereit, welche analog zum free() auf Hostseite funktioniert. Erwähnenswert ist, dass der Aufruf cudafree(null); valide ist und somit keinen Fehler zurückgibt, während ein bereits freigegebener Zeiger bei erneuter Freigabe einen Fehler zurückgibt.[26] Es ist auf Hostseite nicht möglich einen Devicezeiger über einfache Zuweisungen mit Inhalt zu füllen. Ein Aufruf der Art vga P[0] = 1; würde auf Hostseite so interpretiert werden, als wenn der Zeiger auf einen Speicherbereich im Host zeigt, sodass hierbei diverse Speicherfehler auftreten können und das weitere Verhalten des Programms nicht voraussagbar ist. // Host Code int * vga_p, host_p [1000]; size_t size = 1000* sizeof ( int ); CUDA_ERROR_HANDLER ( cudamalloc (& vga_p, size )); 32

44 3 CUDA Programmierung // host_p f l l e n... CUDA_ERROR_HANDLER ( cudamemcpy (( void *) vga_p, ( const void *) host_p, size, cudamemcpyhosttodevice )); Listing 25: Daten zum Device kopieren Der in Listing 25 dargestellte Kopiervorgang macht deutlich, dass der Datentransfer nicht über den gewohnten Zugriff auf Indizes des Devicezeigers geschieht, sondern ähnlich zur aus C/C++ bekannten memcpy-funktion ein auf dem Host liegender Speicher in einen auf einem Device liegenden Speicher kopiert wird. Dies wird über die cudamemcpy-funktion realisiert. Wie zu sehen ist, erwartet diese Funktion zunächst einen void-pointer auf den Zielbereich. Anschließend muss ein const void Zeiger für den Quellbereich angegeben werden, gefolgt von der Größe in Bytes, welche übertragen werden soll. Der letzte Parameter bestimmt die Kopierrichtung. In diesem Fall gibt es folgende fünf Möglichkeiten. cudamemcpyhosttohost Die Kopie wird von einem im Host liegenden Speicherbereich zu einem anderen, auf dem Host liegenden, Speicherbereich kopiert. Dies ist vor allem bei asynchroner Verarbeitung von Daten von Bedeutung, um den korrekten Ausführungszeitpunkt der Hostkopie zu gewährleisten. cudamemcpyhosttodevice In diesem Fall werden die Daten vom Host auf das Device kopiert. cudamemcpydevicetohost Hier werden die Daten vom Device zurück auf den Host transferiert. cudamemcpydevicetodevice Hiermit kann auf einem Device eine Kopie eines Speicherbereiches erzeugt werden oder alternativ eine Kopie von Device A zu Device B gesendet werden. cudamemcpydefault Diese Funktion spielt nur bei der Verwendung eines unified adress space eine Rolle. unified adress space beschreibt einen gemeinsamen Adressraum für die CPU und GPU. Neben der synchronen Kopierfunktion existiert noch eine asynchrone Variante. Diese ist in Listing 26 dargestellt. 33

45 3 CUDA Programmierung // Host Code CUDA_ERROR_HANDLER ( cudamemcpyasync (( void *) vga_p, ( const void *) host_p, size, cudamemcpyhosttodevice, stream1 )); Listing 26: Asynchrones Kopieren Erkennbar ist der nahezu identische Aufruf. Der Streamparameter ist dabei optional und kann, sofern dieser Aufruf keinem Stream zugeordnet werden soll, durch eine 0 ersetzt werden, sodass der Defaultstream des Devices genutzt wird. Neben diesen beiden Kopierfunktionen gibt es eine Reihe weiterer, welche allerdings in diesem Projekt keine Verwendung finden. Weitere Informationen sind in der CUDA Library in [11] zu finden. Zu denen aus C/C++ vergleichsweise bekannten Funktionen gibt es noch eine spezielle Funktion zur Allokation von Hostspeicher. Welche Vorteile diese Funktion gegenüber der normalen Allokation mittels malloc hat, wird erst deutlich, wenn ein Kopiervorgang von oder zum Device durchgeführt werden soll. Da die Devices in der Regel über den PCIe-Bus mit dem Hostsystem verbunden sind, müssen alle zu kopierenden Daten über diesen Bus laufen. Hierfür muss sichergestellt werden, dass die zu kopierenden Daten nicht auf die Festplatte ausgelagert werden können, um dem System direkten Zugriff auf den Speicher zu gewähren. Damit ist es notwendig die Daten zunächst in einen sogenannten non pageable Memory-Bereich zu kopieren. Dieser Bereich wird auch als Pinned Memory bezeichnet. Erst anschließend können die Daten über den PCIe-Bus zum Device kopiert werden. Dieser Kopiervorgang kostet auf Hostseite CPU-Zeit sowie Bandbreite des Arbeitsspeichers, sodass die CUDA API eine Alternative bietet. Das Problem besteht in Rückrichtung genauso, mit dem Unterschied, dass das Device keine Kopie anlegen muss, sondern das Host System zunächst in einen Pinned Memory Bereich schreiben muss und erst anschließend die Daten in den angegebenen Puffer kopiert werden. In Abbildung 13 sind zwei Kopiervorgänge dargestellt, welche den unterschiedlichen Ablauf der Kopievorgänge abbilden. // Host Code int * host_p ; size_t size = 1000* sizeof ( int ); CUDA_ERROR_HANDLER ( cudamallochost (( void **) & host_p, size )); Listing 27: Allokation von Pinned Memory Die in dem Listing 27 dargestellte Allokation von Hostspeicher über die CUDA-API ermöglicht es ohne diesen Umweg zu arbeiten, indem der allozierte Speicherbereich 34

46 3 CUDA Programmierung Abbildung 13: Vergleich der Kopiervorgänge selbst nicht mehr pagable ist. Dies erhöht die maximale Transferleistung des Hostsystems, da unnötige Kopien und implizite Allokationen von Pinned Memory durch die CUDA-API wegfallen. Die Verwendung von Pinned Memory hat allerdings unter Umständen gravierende Nachteile. Dadurch, dass dieser Speicherbereich nicht ausgelagert werden kann, wird der verfügbare Speicher für reguläre Allokationen verkleinert, sodass diese früher ausgelagert werden müssen und somit die Systemperformance verlangsamen können. Aus diesem Grund sollte Pinned Memory nur als Puffer zum Einsatz kommen und nicht zu exzessiv genutzt werden. Um diese Art Speicher wieder freizugeben bedarf es der Funktion aus Listing 28. // Host Code CUDA_ERROR_HANDLER ( cudafreehost ( host_p )); Listing 28: Freigabe von Pinned Memory 3.9 Synchronisation von Threads Die Synchronisation von Threads ist erforderlich, um die von der CPU Seite bekannten Multithreadingprobleme zu verhindern. Dabei stellt die CUDA Bibliothek verschiedene Synchronisationsbefehle zur Verfügung, welche es erlauben, auf spezielle Befehle zu warten. Dies kann förderlich sein, falls die Art der Synchronisation sich nur auf einen lesenden oder schreibenden Zugriff bezieht. Innerhalb des Projektes wird die Funktion syncthreads() zur Synchronisation von Blöcken genutzt. Dieser Synchronisationstyp blockiert einen GPU-Kern, bis alle weiteren GPU-Kerne des Blocks an diesem Punkt angelangt sind und alle lesenden und schreibenden Zugriffe auf den Arbeitsspeicher abgeschlossen sind. 35

47 4 Implementierung 4 Implementierung 4.1 Detektordaten Die zur Verfügung gestellten Testdaten liegen im Format des Rootframeworks vor. Dieses, unter der LGPL-Lizens stehende, Framework wird zur Datenanalyse und -verarbeitung genutzt, da es auf die Verarbeitung großer Datenmengen spezialisiert ist. So ist es vergleichsweise einfach möglich, vorliegende Daten zu visualisieren oder miteinander zu kombinieren, ohne die Originaldaten zu verlieren. Außerdem steht ein C++-Interpreter zur Verfügung, welcher die Erstellung eigener Klassen und Verarbeitungsstrukturen zur Laufzeit ermöglicht. Die Datenstrukturen werden in einem sogenannten EventReader verarbeitet und in Ereignisse (Events) und dazugehörige Spuren (Tracks) zusammengeführt. Der EventReader wird innerhalb eines externen Projektes erstellt, sodass die vorgegebene Schnittstelle zum Auslesen von Testdaten genutzt wird. typedef std :: vector < Track_t > KF_Event_t ; Listing 29: Struktur eines Events Im Codeabschnitt 29 ist ein vom EventReader zurückgegebenes Event beschrieben. Da ein Event aus Tracks zusammengesetzt wird, wird das Event durch einen Vektor von Tracks beschrieben. typedef std :: vector < TrackHit_t > TrackData_t ; struct TrackStruct { TrackData_t track ; TrackInfo_t info ; TrackInfo_t truthtrackinfo ; }; typedef TrackStruct Track_t ; Listing 30: Struktur eines Tracks Der Codeabschnitt 30 zeigt den Aufbau eines Tracks. Ein Track wird durch eine Struktur beschrieben, welche eine Liste der zugehörigen Hits (track), Informationen über den Startpunkt der Flugbahn (info) und den echten Startpunkt aus der Simulation, falls die Daten aus einem Simulator stammen (truthtrackinfo) beinhaltet. struct TrackHitStruct { scalar_t normal [ ORDER ]; scalar_t ref [ ORDER ]; 36

48 4 Implementierung scalar_t err_locx ; scalar_t err_locy ; scalar_t cov_locxy ; scalar_t jacobi [ ORDER * ORDER ]; scalar_t jacobiinverse [ ORDER * ORDER ]; char is2dim ; int dettype ; int bec ; }; typedef struct TrackHitStruct TrackHit_t ; Listing 31: Struktur eines Hits Die TrackHitStruct-Struktur beinhaltet alle benötigten Parameter für einen Messpunkt einer Detektorlage. ORDER ist ein globales Define und wird durch die Zahl Fünf ersetzt. Dieses Define leitet sich aus den vorliegenden Daten ab und beschreibt die Ordnung der meisten quadratischen Matrizen. Der Typ scalar t kann über ein weiteres Define gesteuert werden und wird über eine Typdefinition zu einem Float oder Double. Dies erlaubt eine einfache Umschaltung der Genauigkeit, wobei auf Deviceseite der Typ gpu scalar t separat umgestellt werden kann, sodass die Genauigkeit der Berechnung und die Genauigkeit der weiteren Verarbeitung auf Hostseite getrennt voneinander konfigurierbar sind. In den Variablen normal und ref ist die Position des Treffers bzw. die schon korrigierte Position der Referenzspur gespeichert. Der erwartete Fehler wird in den drei darauf folgenden Variablen beschrieben, wobei nicht alle Berechnungen die Kovarianzmatrix oder den Fehler des Y-Wertes benötigen, da nicht immer ein zweidimensionaler Treffer vorliegt. Ob ein Treffer zweidimensional oder eindimensional behandelt werden muss, wird in der is2dim- Variable gespeichert. Diese Information ist in der Grundversion noch nicht mit in dieser Struktur zusammengefasst und wird vom Host an gegebener Stelle selbst berechnet. Im weiteren Verlauf des Projektes wird diese Information vom EventReader selbst bestimmt und in dieser Struktur entsprechend gespeichert. Die Jakobimatrix übersetzt einen Treffer von einer Lage zur nächsten, sodass die Koordinaten aufeinander abgebildet werden. Die Inverse wird für den Rückweg des Kalman-Filters benötigt. Weiterhin wird die Art der Detektorlage in dettype beschrieben und bec beschreibt, ob der Treffer zu den sogenannten barrel end caps gehört. 37

49 4 Implementierung Kalman-Filter Initialisierung Die Startparameter des Kalman-Filters für die erste Messung lauten wie folgt: p k 1 k 1 = 0, C k 1 k 1 = E 6 (9) Die Wahl des C k 1 k 1 -Parameters beschreibt einen großen anzunehmenden Fehler, da der vorherige Startwert p k 1 k 1 annimmt, es Bestünde keine Differenz zwischen Messpunkt und Referenzspur. 4.2 Projekteigenschaften Das im Rahmen der Masterarbeit umgesetzte Programm ist Teil eines CMake- Projektes. CMake wird genutzt, um den Bauprozess der Anwendung zu automatisieren und Abhängigkeiten des Projektes von anderen Projekten zu prüfen. Da das CUDA-Programm in Rahmen einer Kollaboration aus einem Masterprojektteam und einer weiteren Masterarbeit besteht, sind in dem CMake-Projekt Abhängigkeiten zwischen den einzelnen Subprojekten abgebildet und werden während der Kompilationsphase entsprechend behandelt. Das Masterprojektteam bestehend aus den Personen Philipp Schoppe und Matthias Töppe hat im Rahmen des Projektes eine Schnittstelle zu den vorliegenden Detektordaten definiert und implementiert. Parallel zur Arbeit mit CUDA wird außerdem im Rahmen einer weiteren Masterarbeit von Herrn Maik Dankel die hier vorliegende Aufgabenstellung mit der Programmiersprache OpenCL umgesetzt. In Abbildung 14 ist der grundlegende Vorgang zur Erstellung eines Kompilats abgebildet. Zunächst muss CMake mit dem Pfad zur ersten Konfigurationsdatei aufgerufen werden, in der der Projektname, Compileroptionen, sowie Ein- / Ausgabeverzeichnisse angegeben werden. Außerdem werden in der Konfiguration die benötigten externen Bibliotheken über entsprechende Befehle lokalisiert. Die verschiedenen Subprojekte können die lokalisierten Bibliotheken für einen erfolgreiches Kompilat voraussetzen, sodass eine fehlende Abhängigkeit durch eine Fehlermeldung angezeigt und der Bauprozess abgebrochen wird. Außerdem gibt es die Möglichkeit, 38

50 4 Implementierung Abbildung 14: Projekt Erstellungsablauf Subprojekte zu einer eigenen Bibliothek zu bauen und diese Bibliothek in den weiteren Subprogrammen zu verwenden. Die Baureihenfolge wird somit, wie in der Abbildung 14 dargestellt, automatisch angepasst, sodass die interne Bibliothek vor den einbindenden Programmen kompiliert wird. Neben der Verwendung von CMake und Make zur Kompilierung der Programme wird SVN als Versionierungssystem genutzt. 4.3 Funktionsimplementierung Devicefunktionen Bei der Implementierung des Kalman-Filters werden zunächst die benötigten Informationen analysiert um einerseits die benötigten Daten zu bestimmen und andererseits die konkrete Implementierung zu beeinflussen. Im Abschnitt 4.1 sind die eingehenden Daten aus der EventReader-Schnittstelle definiert. Daraus lässt sich unter Anderem die Dimension der einzelnen Arrays aus dem Kalman-Filter ableiten, welche konkreten Einfluss auf die Implementierung haben. Zunächst wird eine geeignete Schnittstelle zum Device definiert, sodass eine sinnvolle Verarbeitung der Daten möglich ist. Im einfachsten Fall wird eine einzelne Spurrekonstruktion durchgeführt, sodass nur die Daten dieser Spur inklusive der Startparameter, etc. benötigt werden. Aus diesem Grund wird eine Struktur angelegt, welche diese Daten bereitstellt. struct kalmanfilterparams { TrackHit_t * hits ; 39

51 4 Implementierung scalar_t * C_k_1k_1_global ; bool * is2dim ; int * hitcount ; trackhitdata * fitsforward ; trackhitdata * fitsbackward ; trackhitdata * fitsresult ; scalar_t * C_Values ; scalar_t * C_Inverse ; }; typedef struct kalmanfilterparams filterparam ; Listing 32: Spurrekonstruktionsdatenstruktur In der Struktur filterparam aus dem Codeabschnitt 32 werden alle nötigen Parameter übergeben. An erster Stelle wird ein Zeiger auf die zur Spur gehörenden Treffer übergeben. Die zweite Variable zeigt auf ein globales Initialisierungsarray für die Prädiktion der Fehlerkovarianzmatrix C k k 1 im ersten Schritt des Kalman-Filters. Da dieser Wert für alle Spuren identisch ist, wird dieses Array nur einmalig im Arbeitsspeicher des Devices abgelegt und mit den vorgegebenen Werten gefüllt. Alle C k 1k 1 global-zeiger zeigen auf diesen einen Speicherbereich. Der nächste Zeiger verweist auf ein Array, in dem für jeden Treffer vermerkt ist, ob dieser Treffer als zweidimensionaler oder eindimensionaler Fall behandelt werden muss. Der hitcount verweist auf die Anzahl der Treffer dieser Spur und gibt damit die Länge aller anderen Arrays vor. Die Zeiger fitsforward, fitsbackward und fitsresult sind Zeiger auf anfangs nicht gefüllte Speicherbereiche in denen während der Berechnung das jeweilige Ergebnis des Kalman-Filters gespeichert wird. Analog dazu stehen in C Values und C Inverse die berechneten Fehlerkovarianzmatrizen. Da der Kalman-Filter oftmals mit Matrixmultiplikationen arbeitet, werden auf Grund der Datenlage 25 Threads pro Spurrekonstruktion verwendet. Dabei sind diese Threads in zwei Dimensionen aufgeteilt, sodass der Matrixindex eines Threads über die Breite einer Zeile multipliziert mit der Zeilenposition und der Addition der Spaltenposition bestimmt werden kann. Die hier beschriebene Version ist als Grundlage zu sehen, welche in den weiteren Kapiteln weiter optimiert und korrigiert wird. Außerdem ist die numerische Genauigkeit für einige Teilspuren nicht hoch genug und führt zu falschen Ergebnissen. Die zunächst stichprobenartig durchgeführte Plausibilitätsprüfung hat dieses Problem nicht aufgezeigt, da zufällig Spuren extern durch Wolfram Mathematica nachgerechnet und bestätigt wurden, welche nicht von diesem Problem betroffen waren. global void dofiltering ( filterparam * param ) { 40

52 4 Implementierung } int column = threadidx. y; int row = threadidx. x; int block = blockidx. x;... Listing 33: Matrixindizes und Funktionskopf Die Verwendung der Variablen row und column im Listing 33 hat zwei Gründe. Zum einen ist die Lesbarkeit im Code deutlich höher, wenn bei der Indizierung die entsprechenden Variablennamen genutzt werden können, was automatisch den Code wartbarer macht und Fehler von vornherein vermeidet. Der entscheidendere Grund ist allerdings die Datenlokalität dieser Variablen, denn diese liegen nun, sofern freie Register zur Verfügung stehen, in den Registern der einzelnen Threads. Da nach derzeitigem Stand ausreichend Register zur Verfügung stehen, benötigt die häufige Indexberechnung entsprechend wenig Zeit im Vergleich zu im Arbeitsspeicher liegenden Indizes. Das Argument der dofiltering-funktion ist ein Zeiger auf ein ganzes Array von zu bearbeitenden Spurrekonstruktionsdaten. In Abbildung 15 ist Abbildung 15: Aufbau des Verarbeitungsgrids der Aufbau des Grids und der Blöcke abgebildet. Die durch die Datenlage gewählten 41

53 4 Implementierung Datenstrukturen führen zu dem abgebildeten Verarbeitungskonzept. Da die verschiedenen Spuren unabhängig voneinander berechnet werden können und eine Spur genau einem Ereignis zugehörig ist, werden alle Spuren eines Ereignisses zu einem Grid zusammengefasst. Damit kann ein komplettes Ereignis auf ein Device kopiert werden, wobei die Spuren unabhängig voneinander berechnet werden müssen, da keinerlei Synchronisation zwischen der einzelnen Blöcken möglich ist, was genau dem vorliegenden Problem entspricht. Jeder Block besteht aus 5x5 Threads, wobei jeder Thread genau ein Teilergebnis der verschiedenen mathematischen Operationen bestimmt. Es werden alle benötigten Variablen deklariert. Dabei wird die Anzahl der Treffer für die zu berechnende Spur in ein Register geladen. Anschließend wird sichergestellt, dass nur Ergebnisse von Threads berücksichtigt werden, wenn diese innerhalb der Ordnung der Daten von maximal 5x5 liegen. Dies stellt einen Branch dar, welcher innerhalb eines Warps zwar divergiert, da nur 25 Threads arbeiten dürfen, aber 32 Threads innerhalb eines Warps immer den gleichen Code ausführen. Allerdings lässt sich dies auf Grund der Problemstellung nicht verhindern, sodass pro Spur die Ergebnisse von sieben Threads verworfen werden müssen. Wegen dem fehlendem else-zweig gibt es keinen weiteren Overhead in diesem Branch. Im Anschluss daran werden die vom Kalman-Filter zur Berechnung genutzten Variablen deklariert. Die Variablennamen entsprechen abgesehen von der Jakobimatrix der Terminologie aus den Grundlagen des Kalman-Filters in Kapitel 1.3. Die Jakobimatrix F k wird in der Variablen jacobi zwischengespeichert. Dabei werden alle Variablen im Shared Memory angelegt, da diese ausreichend klein sind um gleichzeitig vorgehalten zu werden, ohne das gleichzeitig ausgeführte Blöcke blockieren müssen. Dies liegt an den Eigenschaften des verwendeten Devices, da in diesem Fall die maximale Anzahl von gleichzeitig ausführbaren Blöcken seitens der SMX blockiert. Wie dieses Limit verändert werden kann, ist im Kapitel auf Seite 63 beschrieben. Im Anschluss werden die Startwerte aus Kapitel in den lokalen Shared Memory Bereich geschrieben und die Berechnung in Vorwärtsrichtung wird gestartet. Da die Größe des Grids mit der Anzahl der Spuren in dem aktuellen Ereignis übereinstimmt, wird der Blockindex als Index auf den, dem Kernel übergebenen, Parameter verwendet. int currenthitindex ; for ( currenthitindex = 0; currenthitindex < privhitcount ; currenthitindex ++) { // currenthit = hits [ currenthitindex ]; 42

54 4 Implementierung normaldata [ row ] = param [ block ]. hits [ currenthitindex ]. normal [ row ]; refdata [ row ] = param [ block ]. hits [ currenthitindex ]. ref [ row ]; err_locx = param [ block ]. hits [ currenthitindex ]. err_locx ; err_locy = param [ block ]. hits [ currenthitindex ]. err_locy ; cov_locxy = param [ block ]. hits [ currenthitindex ]. cov_locxy ; jacobi [ row * ORDER + column ] = param [ block ]. hits [ currenthitindex ]. jacobi [ row * ORDER + column ]; syncthreads ();... Listing 34: Start der Kalman-Filterung Zu Beginn der iterativen Berechnung werden die Informationen des aktuellen Treffers in die im Codeabschnitt 34 angegebenen Variablen geschrieben. Die hier verwendete Grafikarchitektur liest blockweise Daten aus dem Speicher und speichert diese zunächst im Level 2 Cache, sodass die multiplen Lesevorgänge auf den Arbeitsspeicher gegebenenfalls reduziert werden könnnen und direkt aus dem Level 2 Cache gelesen werden kann. Um sicher zu stellen, dass jeder Thread alle Daten in den gemeinsamen Shared Memory geschrieben hat, wird zum Schluss eine Synchronisation der Threads durchgeführt. Da in der Regel mehr Blöcke abgearbeitet werden müssen, als gleichzeitig ausgeführt werden können, kann das wartende Warp theoretisch ausgelagert werden. Dies ist allerdings vom Compiler und der verwendeten Hardware abhängig. Anschließend wird die Prädiktionsphase des Kalman-Filters durchgeführt. device forceinline void doprediction ( scalar_t * jacobi, scalar_t * p_k_1k_1, scalar_t * p_kk_1, scalar_t * C_k_1k_1, scalar_t * C_kk_1, int column, int row ) { // Predict state multiply5x1c_av ( jacobi, p_k_1k_1, p_kk_1, column, row ); // Predict Error Covariance predict_c_kk_1 ( jacobi, C_k_1k_1, C_kk_1, column, row ); } Listing 35: Prädiktionsphase des Kalman-Filters Die Prädiktionsphase wird in einer Devicefunktion implementiert. Um unnötige Kontextwechsel zu vermeiden, wird die Funktion über forceinline immer als Inlinefunktion in den Kernel eingebaut. Durch das Kommando device wird dem CUDA-Compiler außerdem mitgeteilt, dass die Funktion doprediction nur auf einem Device laufen darf. In der Prädiktionsphase muss mittels der Gleichung 1 der Wert von p k k 1 bestimmt werden. Diese Matrix-Vektor-Multiplikation wird in ei- 43

55 4 Implementierung ner weiteren Inlinefunktion durchgeführt, welche die 5x5 Jakobimatrix mit dem 5x1 Vektor p k 1 k 1 multipliziert und im Zielvektor p k k 1 speichert. device forceinline void multiply5x1c_av ( scalar_t *A, scalar_t *v, scalar_t *c, int column, int row ) { scalar_t result = 0; for ( int i = 0; i < ORDER ; i ++) { result += A[ row * ORDER + i] * v[ i]; } c[ row ] = result ; syncthreads (); } Listing 36: Matrix-Vektor-Multiplikation Die Implementation der multiply5x1c Av-Funktion aus dem Codeabschnitt 36 zeigt eine Besonderheit der GPU-Programmierung. Durch die hohe Anzahl an gleichzeitigen Threads können alle Teilergebnisse gleichzeitig bestimmt werden. Bei genauerem Hinsehen wird eine hohe Berechnungsredundanz deutlich. Durch den Verwendeten Blockaufbau von zwei Dimensionen mit jeweils der Größe Fünf, stehen immer 25 Threads zur Verfügung. Da das hier abgebildete Problem allerdings nur 5 Teilergebnisse hat und die benötigten Berechnungen dieser Teilergebnisse so gering sind, dass sich eine divide and conquer-strategie zur Unterteilung der Berechnung in Teilergebnisse nicht lohnt, arbeiten jeweils 5 Prozessoren am gleichen Ergebnis. Dies liegt an der Tatsache, dass alle Threads bei dem gegebenen Code zunächst ein Ergebnisregister result auf Null setzen und anschließend die Summe aus den Produkten einer Spalte in einer bestimmten Zeile der Matrix mit der zugehörigen Zeile des Vektors bildet. Anschließend wird das Ergebnis in den Ergebnisvektor in die entsprechende Zeile geschrieben und alle Threads synchronisieren sich. Dieser Overhead an identischen Berechnungen lässt sich zu diesem Zeitpunkt nicht verhindern, da das Starten eines neuen Kernels mit den gegebenen Eingangsdaten und nur eindimensionalen Block der Größe Fünf diesen Overhead nicht verringern würde, da immer mindestens ein kompletter Warp den Code ausführt und damit auf allen bekannten NVIDIA-Grafikkarten mindestens 32 Threads diesen Code ausführen müssen. In Tabelle 3 sind weitere Funktionen angegeben, welche für die Implementation des Kalman-Filters benötigt werden. Dabei benutzen alle diese Funktionen ein Register als Ergebnisspeicher und lesen die Daten aus dem Shared Memory. Die Funktionen, die eine der Eingabevariablen überschreiben, arbeiten mit zwei Synchronisierungspunkten zur Eliminierung von Race-Conditions. 44

56 4 Implementierung Tabelle 3: Inlinefunktionen Matrix/Vektor-Multiplikation Funktionsname Funktion Ergebnis multiply5x1c Av c = Av 5x1 multiply5x1v Av v = Av 5x1 multiply5x5 C = AB 5x5 multiply5x5a AB A = AB 5x5 multiply5x5b AB B = AB 5x5 Im Anschluss an die Prädiktionsphase wird die Aktualisierungsphase durchgeführt. Für die Aktualisierung des prognostizierten p k k 1 und der Fehlerkovarianzmatrix C k k 1 wird jeweils der Kalman-Gain benötigt. Die Berechnung des Kalman-Gains ist vom aktuellen Treffer abhängig, da je nach Dimension unterschiedliche Berechnungen durchgeführt werden müssen. Gleiches gilt für die Berechnung von p k k und C k k. Da dieser Branch für alle Threads des Kernels identisch ist, wird in diesem Fall nur eine der beiden Berechnungen von dem ausführendem Warp durchgeführt. K k = C k k 1 H T k (V k + H k C k k 1 H T k ) 1 (10) In Gleichung 10 ist die allgemeine Berechnugnsformel für den Kalman-Gain angegeben. Die Berechnung hängt maßgeblich von der Dimension ab, da im Falle eines eindimensionalen Treffers V k ein Skalar ist. Dieses Skalar ist das Quadrat der Variablen err locx und muss auf die transformierte Fehlerkovarianzmatrix addiert werden. ( ) H k = (11) Die Transformationsmatrix H k aus Gleichung 11 gilt nur für den eindimensionalen Fall und transformiert die Fehlerkovarianzmatrix im Gleichungsabschnitt H k C k k 1 H T k zu einem Skalar mit dem Wert c 0,0. Aus der Multiplikation der Fehlerkovarianzmatrix und der transponierten Transformationsmatrix folgt ein Vektor mit den Werten der ersten Spalte der Fehlerkovarianzmatrix. Diese Vereinfachungen führen zu Glei- 45

57 4 Implementierung chung 12. K k = c 0,0 c 1,0 c 2,0 c 3,0 1 (12) err locx err locx + c 0,0 c 4,0 Diese optimierte Gleichung benötigt drei bzw. vier Operationen, abhängig davon, ob das Compilerflag zur Unterstützung der MADD-Funktion aktiv ist oder nicht und ob die Zielarchitetkur die MADD-Operation unterstützt. Die Implementation der Gleichung ist als Inlinefunktion realisiert und führt die in Listing 12 angegebenen Instruktionen aus. scalar_t inv = C_kk_1 [0] + err_locx [0] * err_locx [0]; // MUL + ADD or MADD inv = (( scalar_t ) 1.0) / inv ; // DIV gain_k [ row ] = inv * C_kk_1 [ row ]; // MUL Listing 37: Kalman-Gain Implementation 1D Im zweidimensionalem Fall ist die Berechnung des Kalman-Gains deutlich komplexer. ( ) H k = Gleichung 13 gibt die Transformationsmatrix für den zeidimensionalen Fall an. Es wird deutlich, dass die Transformation in eine 2x5 oder 2x2 Matrix überführt. ( ) err locx 2 cov locxy V k = (14) cov locxy err locy 2 In Gleichung 14 ist das benötigte V k dargestellt. Damit steht im zu invertierendem Teil des Kalman-Gains eine 2x2 Matrix. Die Invertierung dieser Matrix wird über die, sich aus der cramer schen Regel ableitende, Invertierungsvorschrift durchgeführt, sodass die Inverse der Determinanten mit der Adjunkten der Matrix multipliziert werden muss. ( ) 1 (V k + H k C k k 1 H T k ) 1 c 0,0 + err locx 2 c 0,1 + cov locxy = (15) c 1,0 + cov locxy c 1,1 + err locy 2 (13) 46

58 4 Implementierung Die Anwendung der Invertierungsvorschrift auf Gleichung 15 führt zu Gleichung 16. ( ) c 1,1 + err locy 2 c 0,1 cov locxy c 1,0 cov locxy c 0,0 + err locx 2 (c 0,0 + err locx 2 ) (c 1,1 + err locy 2 ) (c 0,1 + cov locxy ) (c 1,0 + cov locxy ) (16) scalar_t inv00 = C_kk_1 [0] + ( err_locx [0] * err_locx [0]) ; scalar_t inv01 = C_kk_1 [1] + cov_locxy [0]; scalar_t inv10 = C_kk_1 [ ORDER ] + cov_locxy [0]; scalar_t inv11 = C_kk_1 [ ORDER + 1] + ( err_locy [0] * err_locy [0]) ; scalar_t det = 1.0 / ( inv00 * inv11 - inv01 * inv10 ); inv11 *= det ; inv01 *= - det ; inv10 *= - det ; inv00 *= det ; Listing 38: Kalman-Gain Implementation 2D-Invertierung Bei der Implementation der Invertierung werden zunächst die Werte der vier Matrixelemente berechnet. Die vier Einzelwerte werden in je einer Variablen gespeichert. Anschließend wird die Determinante berechnet und mit den Einzelwerten multipliziert. Damit ist die Invertierung abgeschlossen. Diese Berechnung hat den größten Overhead, da jeder Thread die gleichen Ergebnisse berechnet und speichert. In diesem Fall macht eine Unterscheidung in kleinere Abschnitte wieder keinen Sinn, da alle Threads des Warps jeden Branch berechnen würden und damit der Sinn der Verzweigungen abhanden kommt. c 0,0 c 0,1 c 1,0 c 1,1 ( K k = c 2,0 c 2,1 inv11 inv10 c 3,0 c 3,1 c 4,0 c 4,1 ) inv01 inv00 In Gleichung 17 ist die vereinfachte Berechnung des Kalman-Gains angegeben. gain_k [ row * 2] = C_kk_1 [ row * ORDER ] * inv11 + C_kk_1 [ row * ORDER + 1] * inv10 ; gain_k [ row * 2 + 1] = C_kk_1 [ row * ORDER ] * inv01 + C_kk_1 [ row * ORDER + 1] * inv00 ; Listing 39: Kalman-Gain Implementation 2D (17) 47

59 4 Implementierung Der im Listing 39 dargestellte Code berechnet und speichert die Ergebnisse von Gleichung 17. Hierbei werden die Indizes der verschiedenen Threads genutzt, um möglichst wenig Speicherzugriffe pro Thread durchzuführen. Die erste Spalte der Kalman-Gain-Ergebnismatrix setzt sich aus der Summe der ersten Spalte der Fehlerkovariantmatrix multipliziert mit dem Wert von inv11 und dem Wert der zweiten Spalte der Fehlerkovarianzmatrix multipliziert mit inv10 zusammen. Analog dazu setzt sich die zweite Spalte aus der Summe der Werte der ersten Spalte multipliziert mit inv01 und dem Wert der zweiten Spalte multipliziert mit inv00 zusammen. Da jeder Thread einer Zeile zugeordnet ist, muss jeder Thread die entsprechenden Werte der Zeile in die Ergebnismatrix speichern. Eine Unterscheidung zwischen den Threads nach Spalte kann den Overhead auf Grund der gemeinsamen Codeausführung eines Warps nicht verringern. Im Anschluss an die Kalkulation des Kalman-Gains müssen p k k und C k k berechnet werden. In beiden Fällen sind dies die Eingabewerte für den nächsten Treffer, sodass diese Werte im Code in p k 1 k 1 beziehungsweise C k 1 k 1 gespeichert werden. p k k = p k k 1 + K k (m k H k p k k 1 ) (18) Die Berechnung von p k k wird durch Einsetzen der bekannten Dimension des Problems weiter aufgelöst. Hierbei wird der Kalman-Gain mit der Differenz aus den Skalaren m k0 und p k k 10 multipliziert und auf p k k 1 addiert. p_kk [ row ] = gain_k [ row ] * ( m_k [0] - p_kk_1 [0]) + p_kk_1 [ row ]; Listing 40: p k k Implementation 1D Bei der Implementation aus Listing 40 kann die verwendete Blockarchitektur genutzt werden, um alle Ergebniszeilen gleichzeitig zu berechnen. Um bei der Implementation von Gleichung 18 im zweidimensionalen Fall auf die verschiedenen Matrix-Vektor-Multiplikationen zu verzichten, wird wieder eine Berechnungsvorschrift durch Einsetzen der bekannten Dimensionsgrößen hergeleitet. MHP = m k H k p k k 1 = ( m 0 m 1 ) ( ) p 0 p 1 p 2 p 3 p 4 ( ) m 0 p 0 = m 1 p 1 (19) 48

60 4 Implementierung Die Differenz von m k und dem Produkt aus H k und p k k 1 ergibt den Vektor MHP aus Gleichung 19. p k k = p k k 1 + K k MHP = p 0 p 1 p 2 p 3 k 0,0 k 0,1 k 1,0 k 1,1 ( ) + k 2,0 k 2,1 m 0 p 0 m 1 p 1 k 3,0 k 3,1 (20) p 4 k 4,0 k 4,1 Durch Einsetzen in die Ursprungsgleichung ergibt sich Gleichung 20, aus der die Berechnungsvorschrift p k ki = p k k 1i + (m 0 p k k 10 ) k i,0 + (m 1 p k k 11 ) k i,1 für jede Zeile i abgelesen werden kann. Diese Berechnungsvorschrift führt zum Code aus Listing 41. p_kk [ row ] = gain_k [ row * 2] * ( m_k [0] - p_kk_1 [0]) Die Initialisierung + gain_k [ row * 2 + 1] * ( m_k [1] - p_kk_1 [1]) + p_kk_1 [ row ]; Listing 41: p k k Implementation 2D Anschließend wird die Fehlerkovarianzmatrix aktualisiert. Zunächst wird Gleichung 21 durch Einsetzen der bekannten Matrix- und Vektorgrößen vereinfacht. C k k = (I K k H k )C k k 1 (21) Es wird zunächst eine Formel für den eindimensionalen Fall abgeleitet. Durch einsetzen der speziellen Matrizen und Vektoren wird Gleichung 22 gebildet k k 1 ( ) (I K k H k ) = k k 3 k 4 (22) Durch Auflösen folgt Gleichung k k (I K k H k ) = k k k (23) Die sich ergebene Formel wird in CUDA als weitere Funktion implementiert. 49

61 4 Implementierung C_kk [ row * ORDER + column ] = 0; syncthreads (); C_kk [ row * ORDER ] = gain_k [ row ]; syncthreads (); if ( row == column ) C_kk [ row * ORDER + column ] = 1 - C_kk [ row * ORDER + column ]; else C_kk [ row * ORDER + column ] = - C_kk [ row * ORDER + column ]; syncthreads (); multiply5x5a_ab ( C_kk, C_kk_1, column, row ); Listing 42: C k k Implementation 1D Die Implementation aus dem Codelisting 42 setzt zunächst den Klammerterm um, indem die Zielmatrix genullt und anschließend die erste Zeile mit den Werten des Kalman-Gains initialisiert wird. Die Subtraktion von der Einheitsmatrix erfolgt im Anschluss daran durch Invertieren des Vorzeichens der Matrix, falls der aktuelle Thread nicht auf der Hauptdiagonalen arbeitet. Falls der Thread das Ergebnis der Hauptdiagonalen bestimmt, wird anstatt der Invertierung des Vorzeichens der bisherige Inhalt von Eins subtrahiert. Damit ist Gleichung 23 implementiert und muss mit der prognostizierten Fehlerkovarianzmatrix multipliziert werden. Die Umsetzung der Berechnung eines zweidimensionalen Treffers erfolgt analog, sodass zunächst die speziellen Matrizen in die Gleichung eingesetzt werden k 0,0 k 0, k 1,0 k 1,1 ( ) (I K k H k ) = k 2,0 k 2, (24) k 3,0 k 3, k 4,0 k 4,1 Durch Auflösen resultiert daraus Gleichung k 0,0 k 0, k 1,0 1 k 1, (I K k H k ) = k 2,0 k 2, (25) k 3,0 k 3, k 4,0 k 4, Die Implementation dieser Gleichung in der Kernelfunktion funktioniert analog zur vorherigen Implementation des eindimensionalen Updates der Fehlerkovarianzmatrix. Der Unterschied zum eindimensionalen Fall ist lediglich eine weitere, mit Werten des Kalman-Gains gefüllte, Spalte. Aus diesem Grund muss nur die Codezeile 50

62 4 Implementierung C kk[row * ORDER] = gain k[row]; ersetzt werden. Anstatt nur die erste Zeile zu kopieren, muss zudem die zweite Zeile kopiert werden. Es kann in diesem Fall auf eine zweite Codezeile zum Kopieren verzichtet werden, da die Spaltenanzahl einer Zweierpotenz entspricht. Dies ermöglicht eine erweiterte Indexberechnung über den logischen UND-Operator aus der boolschen Algebra. Es muss weiterhin aus jeder Zeile kopiert werden, sodass jeder Thread um seinen Zeilenindex verschoben auf die Matrizen zugreifen muss. Weiterhin müssen die Threads Spalte Null und Spalte Eins kopieren. Diese Verschiebung des Indizes um Eins wird durch den UND-Operator auf den Spaltenindex jedes Threads angewendet, sodass das Ergebnis dieser Operation entweder Null oder Eins ergibt. Daraus folgt der Codeabschnitt 43. Die restlichen Operationen werden aus dem eindimensionalem Fall übernommen. C_kk [ row * ORDER + ( column & 1)] = gain_k [ row * 2 + ( column & 1) ]; Listing 43: C k k Implementation 2D Anpassung Damit sind die benötigten Berechnungen für den aktuellen Treffer abgeschlossen. Die Ergebnisse dieses Treffers werden im Hauptspeicher des Devices gespeichert, da diese für den Smoothing-Vorgang benötigt werden. param [ block ]. fitsforward [ currenthitindex ]. data [ row ] = p_k_1k_1 [ row ]; currentcvaluepointer [ ORDER * row + column ] = C_k_1k_1 [ ORDER * row + column ]; currentcvaluepointer = currentcvaluepointer + ORDER * ORDER ; Listing 44: Speichern der Updateergebnisse aus dem Hinweg In Listing 44 ist der Speichervorgang dargestellt. Gespeichert werden p k k und C k k und es wird, falls ein weiterer Treffer in der aktuell bearbeiteten Spur vorhanden ist, mit der Berechnung des Hinwegs fortgefahren. Andernfalls wird mit der Berechnung des Rückwegs begonnen. Bevor die Kalkulation des Rückwegs gestartet wird, wird die globale Fehlerkovarianzmatrix aus dem übergebenen Spurparameter in die lokale Fehlerkovarianzmatrixvariable geschrieben. Anders als beim Hinweg wird p k 1 k 1 nicht mit einem Nullvektor vorinitialisiert, sondern es wird das Ergebnis des Hinwegs als Startwert verwendet. Die Berechnung des Rückwegs ist, bis auf wenige Ausnahmen, identisch zur Berechnung des Hinwegs. Unterschiedlich ist der Startindex, welcher auf dem letzten vorhandenen Treffer gesetzt wird, sowie die Laufrichtung, welche den Startwert dekrementiert und entstrechend der Vorgabe rückwärts durch die Spur läuft. Außerdem wird beim Laden der Trefferwerte nicht die Jakobimatrix des Treffers in die lokale 51

63 4 Implementierung Jakobivariable geschrieben, sondern die Inverse dieser Jakobimatrix wird verwendet. Eine Ausnahme bildet hier die Bearbeitung des ersten Treffers, da in diesem Fall die reguläre Jakobimatrix genutzt werden muss. Die zu speichernden Ergebnisse für den Smoothing-Teil des Filters werden beim Rückweg analog zum Hinweg in den Hauptspeicher geschrieben. Hierbei müssen nicht die Updatewerte gespeichert werden, sondern die prognostizierten Werte, da sonst die Messung selbst doppelt gewichtet wird. Die für das Smoothing benötigte Summe der beiden Fehlerkovarianzmatrizen wird in der Filterstruktur in der Variable C Inverse gespeichert und vom Host invertiert. Weitere Informationen zur Invertierung sind im Kapitel zu finden. Das erfolgreiche Berechnen des Rückwegs ermöglicht das Fortfahren mit der Ausführung des Smoothings. Das Smoothing selbst ist als weitere Kernelfunktion implementiert, welche vom Host aufgerufen wird und erwartet, identisch zum Kernel der Hin-/Rückweg-Berechnung des Kalman-Filters, ein Array aus den einzelnen Spurinformationen eines Events als Parameter. Das Smoothing verwnedet außerdem einen identischen Aufbau des Grids und der Blöcke, sodass 5x5 Threads pro Block aktiv sind und jede Spur genau von einem Block abgearbeitet wird. for ( int hit = 0; hit < privhitcount ; hit ++) { int ptranslation = hit * ORDER * ORDER ; //K = C_f ( C_f + C_b )^ -1 multiply5x5 ( param [ block ]. C_Values + ptranslation, param [ block ]. C_Inverse + ptranslation, K, column, row ); syncthreads (); //p = Kp^b multiply5x1c_av (K, param [ block ]. fitsbackward [ hit ]. data, PRes, column, row ); syncthreads (); // p = kp^b + p^f PRes [ row ] += param [ block ]. fitsforward [ hit ]. data [ row ]; //C = I - K C_S [ row * ORDER + column ] = I[ row * ORDER + column ] - K[ row * ORDER + column ]; syncthreads (); //C = (I - K)C^f multiply5x5a_ab ( C_S, param [ block ]. C_Values + ptranslation, column, row ); // Write back everything param [ block ]. C_Inverse [ ptranslation + row * ORDER + column ] = C_S [ row * ORDER + column ]; param [ block ]. fitsresult [ hit ]. data [ row ] = PRes [ row ]; } 52

64 4 Implementierung Listing 45: Implementation Smoothing Der Codeabschnitt 45 zeigt den relevanten Teil des Smoothings. Es werden alle Berechnungen für jeden Treffer durchgeführt, sodass die Messung jedes Treffers aktualisiert werden kann. Zunächst wird hierzu K k durch die Multiplikation der Fehlerkovarianzmatrix aus dem Hinweg, sowie der Inversen der Summe der Fehlerkovarianzmatrizen aus Hin- und Rückweg bestimmt. Im Anschluss daran kann p k berechnet werden. Dazu wird zuerst K k p b berechnet, wobei b analog zur Schreibweise aus den Kapitel 1.3 keinen Exponenten darstellt, sondern die Richtung aus welcher das p stammt. Es gilt ebenso, dass b der Rückrichtung und f der Vorwärtsrichtung entsprechen. Anschließend wird das Ergebnis PRes gebildet, indem p f addiert wird. Danach wird die Fehlerkovarianzmatrix C k bestimmt und gemeinsam mit p k im Hauptspeicher des Devices gespeichert. Damit ist das Smoothing abgeschlossen und die Ergebnisse können vom Host gelesen und verarbeitet werden Hostfunktion Der Host einer CUDA-Applikation versorgt die einzelnen Devices mit Arbeit und übernimmt dabei größtenteils die Verwaltung. Im Verlauf der Masterarbeit wird deutlich, dass einige der benötigten Funktionen auf dem Host deutlich performanter umsetzbar sind, sodass einige der Berechnungen auf die Hostseite ausgelagert werden. Sowohl die Auslagerung der Berechnungen, als auch die Verwaltung wird erläutert. Der Algorithmus ist in einer C++-Klasse gekapselt und muss zur Verwendung entsprechend instaziiert werden. In der hier erläuterten Grundversion benötigt die Klasse keine weiteren Parameter zum Ausführen, da alle Informationen fest im Code vermerkt sind. Im Verlauf der Optimierungen wird die starre Implementation durch eine parametrierbare Implementation ersetzt. Dieser Abschnitt erläutert die Grundversion, die anschließend durch einzelne Optimierungsschritte angepasst wird. Initialize EventReader ; Read events EventReader ; FOR event IN EventReader DO preprocessing event ; Copy to Device ; Run Kalman - Filter on Device ; Read Results ; Invert matrices ; Copy inverted matrices onto Device ; Run Kalman - Filter - Smoothing on Device ; Read final results ; 53

65 4 Implementierung Process final results ; END FOR ; Listing 46: Pseudocode Hostfunktion Der Pseudocode aus Listing 46 gibt den Ablauf des Hostprogrammes wieder. Zuerst wird der vom Masterprojekt-Team entwickelte EventReader instanziiert und initialisiert. Die Initialisierung liest dabei Daten aus einem angegebenen Root-File ein und liest darin gespeicherte Ereignisse und Spuren aus. Diese werden in den im Kapitel 4.1 beschriebenen Strukturen gespeichert und können über eine dafür vorgesehene Funktion abgerufen werden. Neben dem Erzeugen des EventReaders und der anschließenden Initialisierung wird eine Fehlerkovarianzmatrix angelegt, welche die Standardparameter für den Kalman-Filter Hin-/Rückweg enthält. Diese Fehlerkovarianzmatrix wird anschließend auf das Standarddevice kopiert und der Devicezeiger wird für den späteren Gebrauch gespeichert. Es wird mit der Abarbeitung der vorhandenen Datensätze begonnen. Dies wird mittels einer FOR-Schleife über die vorhandenen Ereignisse realisiert. Innerhalb der FOR-Schleife wird die eigentliche Verarbeitung der Daten vorgenommen. Dabei werden als Erstes die Daten des Ereignisses verarbeitet, um diese in ein für die Grafikkarte passendes Format zu bringen, da die Daten als C++ Standardvektoren vorliegen. Da das Kopieren von Daten auf das Device für kleine Transfers vergleichsweise langsam ist, werden die Daten zu Blöcken zusammengefasst, sodass die Datenmenge pro Transfer vergrößert wird. hitcount = reader. gettrack (i, j). track. size (); TrackHit_t hits [ hitcount ]; bool is2dim [ hitcount ]; for ( int k = 0; k < hitcount ; k ++) { hits [k] = reader. gettrack (i, j). track.at(k); // Set if 2 Dim or not if ( abs ( hits [k]. normal [1] ) > 1.E -4) { is2dim [k] = true ; } else { is2dim [ k] = false ; } } Listing 47: Spurdaten auslesen und verarbeiten Im Codelisting 47 ist das Kopieren der Vektordaten in lokale Arrays dargestellt. Das Kopieren wird pro Spur bzw. Track vorgenommen und bestimmt die Anzahl der Treffer in dieser Spur, reserviert Speicher für entsprechend viele Treffer und 54

66 4 Implementierung Dimensionsdaten auf dem Hoststack und füllt diese Arrays anschließend mit den Daten der Spur. Ob der vorliegende Treffer zweidimensional ist, wird über einen Zahlenwert gekennzeichnet. Alle Treffer, deren locy-wert entspricht, sind eindimensionale Treffer. Damit sind die benötigten Spurdaten aus dem EventReader extrahiert und können auf das Device kopiert werden. TrackHit_t * vga_hits ; CUDA_ERROR_HANDLER ( cudamalloc (( void **) & vga_hits, sizeof ( hits ))); CUDA_ERROR_HANDLER ( cudamemcpy ( vga_hits, hits, sizeof ( hits ), cudamemcpyhosttodevice )); Listing 48: Allokation und Kopieren von Daten In Listing 48 ist beispielhaft eine Allokation und das Starten des Kopiervorgangs angegeben. Alle anderen von dem Device benötigten Werte werden auf ähnliche Art und Weise alloziert und gegebenenfalls kopiert. Zu sehen ist außerdem, dass auf dem Host selbst der Zeiger auf den Hauptspeicher des Devices gespeichert wird. Um Zeiger des Hostsystems und der Devices nicht zu vertauschen, beginnen alle auf ein Device zeigende Zeiger mit vga. Es besteht zudem die Möglichkeit Pointerarithmetik hostseitig auf die Zeiger der Devices anzuwenden. Dies wird in der Hostfunktion beispielsweise verwendet, um die Allokationsanzahl der Ergebnisspeicher zu reduzieren, indem ein einzelner, genügend großer Speicherbereich auf dem Device reserviert wird und über Zeigerarithmetik die Zeiger der Kalman-Filter Struktur kalmanfilterparams berechnet werden. Die verschiedenen Zeiger werden in der Parameterstruktur entsprechend gesetzt. Die Filterstruktur selbst ist dabei ein Array mit der Länge der im Eriegnis vorhandenen Spuren. Unter der Voraussetzung, dass alle Teilstrukturen mit den Zeigern auf die Spurinformationen gefüllt sind, werden diese auf das Device kopiert, sodass der Kernel für die Berechnung des Hin-/Rückwegs des Kalman-Filters gestartet werden kann. dim3 threads ( ORDER, ORDER ); dofiltering <<< trackcount, threads >>>( vga_filterp ); Listing 49: Starten des Kalman-Filter Kernels Codeabschnitt 49 zeigt den Aufruf des Filterkernels. Durch diesen Aufruf wird das Ausführungsgrid mit trackcount Blöcken gestartet, wobei jeder Block die benötigten 5x5 Threads zur Verarbeitung hat. Damit wird das Device mit der Berechnung starten und der Host muss auf die komplette Ausführung des Kernels warten. Wenn der Kernel die Berechnungen erfolgreich beendet hat, muss die für das Smoothing 55

67 4 Implementierung benötigte invertierte Matrix von C f +C b berechnet werden. Aus diesem Grund werden die von dem Device berechneten Matrixsummen aus den C Inverse Zeigern auf den Host zurückkopiert und anschließend invertiert. Das Invertieren der Matrizen ist durch Verwendung der GSL-Bibliothek in dem EventReader als Funktion implementiert und arbeitet auf Basis der Singular Value Decomposition (SVD) um die Pseudoinverse der Matrix zu bilden. Diese Pseudoinversen werden anschließend auf das Device zurückkopiert. Die Kopie überschreibt dabei die nicht weiter benötigten Summen der Fehlerkovarianzmatrizen. Der Smoothingkernel wird mit den gleichen Eigenschaften und Paramtern des Kalman-Filter Hin-Rückwegkernels gestartet und blockiert die weitere Ausführung des Hostcodes bis der Kernel die Berechnungen abgeschlossen hat. Im Anschluss an alle benötigten Berechnungen werden die Resultate des Smoothing und die Zwischenergebnisse des Hin-/Rückwegs ausgelesen und können zur weiteren Verarbeitung benutzt werden. Da zum aktuellen Zeitpunkt keine weitere Verwendung der Daten ansteht, werden diese auf dem Device und dem Host wieder freigegeben und die Berechnung des Filters wird für das nächste Ereignis durchgeführt. Sobald alle Ereignisse abgearbeitet sind, wird die global genutzte Initialisierungsfehlerkovarianzmatrix freigegeben und die Hostfunktion wird verlassen. 4.4 Optimierungsschritte Die hier vorgestellten Schritte zur Optimierung der Performance des Kalman-Filters basieren auf Analysen, die mit Hilfe des Visual Profilers von NVIDIA durchgeführt werden. Da der Profiler eine nur geringe Auslastung der GPU und hohe Auslastung des Hostsystems offenlegte, werden zunächst Optimierungen auf der Hostseite durchgeführt, um maximale Performancesteigerungen zu erzielen. Die Performancesteigerungen der unterschiedlichen Optimierungsstufen sind im Kapitel 5.1 näher erläutert Datenstrukturen In Abbildung 16 ist das Ausführungsprofil der bisher erläuterten Basisapplikation abgebildet. Es ist ersichtlich, dass der EventReader die ersten 5 Sekunden der Ausführung in Anspruch nimmt und erst ab diesem Zeitpunkt die Verarbeitung der Daten beginnt. Die gelben Balken repräsentieren einen Kopiervorgang, während die 56

68 4 Implementierung Abbildung 16: Ausgabe des Visual Profilers blauen Balken eine Kernelausführung anzeigen. Welcher Kernel ausgeführt wird ist darunter zu sehen. Wichtig an dieser Grafik ist die im vorherigen Absatz angesprochene Auslastung des Hostsystems bzw. Dauer der Kopiervorgänge. Da der aktuelle Zustand ab Starten der Berechnungen zu ca. 95 % aus Kopiervorgängen und Warten auf das Hostsystem besteht, wird versucht die bisher erzielte Kopierleistung zu erhöhen. Der Profiler gibt an, dass bei ca. 67 % der Kopiervorgänge nur eine durchschnittliche Transferrate von 1,1 GB/s erreicht wird, welche weit von den erreichbaren Datenraten von PCIe2 (8 GB/s) oder PCIe3 (16 GB/s) entfernt liegt. Die in Abbildung 8 dargestellten Transferraten zeigen deutlich, dass eine Vergrößerung der simultan zu übertragenen Daten automatisch zu einer deutlich gesteigerten Kopiereffizienz führen. Zu diesem Zweck wird der Hostcode zunächst auf größeren Datendurchsatz hin optimiert. Momentan werden die benötigten Informationen pro Spur auf das Device übertragen. Dies führt dazu, dass die einzelnen Kopiervorgänge aus relativ kleinen Datensätzen bestehen. Falls das Programm mit doppelter Genauigkeit für den Datentypen scalar t arbeitet, so hat die TrackHit t Datenstruktur mit den Trefferinformationen, ohne die bisher noch nicht in dieser Struktur gespeicherten is2dim-variable, auf einem 64-Bit System eine Größe von 520 Byte. Neben den Treffern wird noch die Tracklänge übertragen, welche auf einem 64-Bit System 8 Byte groß ist. Außerdem wird ein Array is2dim mit der Trefferdimension übertragen, welches pro Treffer eine Größe von einem Byte hat. In dem verwendeten Testdatensatz gibt es Spuren in 96 Ereignissen, welche durchschnittlich 32,69 Treffer beinhalten. Die minimale Spurlänge liegt bei fünf Treffern, die maximale bei 101 Treffern. Tabelle 4 macht deutlich, warum die bisher übertragenen Datenmengen nicht ausreichen um die möglichen Übertragungskapazitäten auszunutzen. Dadurch, dass die Verarbeitung pro Ereignis gestartet wird und die schon verwendeten Strukturen mit 57

4D Server v12 64-bit Version BETA VERSION

4D Server v12 64-bit Version BETA VERSION 4D Server v12 64-bit Version BETA VERSION 4D Server v12 unterstützt jetzt das Windows 64-bit Betriebssystem. Hauptvorteil der 64-bit Technologie ist die rundum verbesserte Performance der Anwendungen und

Mehr

CUDA. Moritz Wild, Jan-Hugo Lupp. Seminar Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg

CUDA. Moritz Wild, Jan-Hugo Lupp. Seminar Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg CUDA Seminar Multi-Core Architectures and Programming 1 Übersicht Einleitung Architektur Programmierung 2 Einleitung Computations on GPU 2003 Probleme Hohe Kenntnisse der Grafikprogrammierung nötig Unterschiedliche

Mehr

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen Binäre Bäume 1. Allgemeines Binäre Bäume werden grundsätzlich verwendet, um Zahlen der Größe nach, oder Wörter dem Alphabet nach zu sortieren. Dem einfacheren Verständnis zu Liebe werde ich mich hier besonders

Mehr

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren Lineargleichungssysteme: Additions-/ Subtraktionsverfahren W. Kippels 22. Februar 2014 Inhaltsverzeichnis 1 Einleitung 2 2 Lineargleichungssysteme zweiten Grades 2 3 Lineargleichungssysteme höheren als

Mehr

Avira Management Console 2.6.1 Optimierung für großes Netzwerk. Kurzanleitung

Avira Management Console 2.6.1 Optimierung für großes Netzwerk. Kurzanleitung Avira Management Console 2.6.1 Optimierung für großes Netzwerk Kurzanleitung Inhaltsverzeichnis 1. Einleitung... 3 2. Aktivieren des Pull-Modus für den AMC Agent... 3 3. Ereignisse des AMC Agent festlegen...

Mehr

Das große ElterngeldPlus 1x1. Alles über das ElterngeldPlus. Wer kann ElterngeldPlus beantragen? ElterngeldPlus verstehen ein paar einleitende Fakten

Das große ElterngeldPlus 1x1. Alles über das ElterngeldPlus. Wer kann ElterngeldPlus beantragen? ElterngeldPlus verstehen ein paar einleitende Fakten Das große x -4 Alles über das Wer kann beantragen? Generell kann jeder beantragen! Eltern (Mütter UND Väter), die schon während ihrer Elternzeit wieder in Teilzeit arbeiten möchten. Eltern, die während

Mehr

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Objektorientierte Programmierung für Anfänger am Beispiel PHP Objektorientierte Programmierung für Anfänger am Beispiel PHP Johannes Mittendorfer http://jmittendorfer.hostingsociety.com 19. August 2012 Abstract Dieses Dokument soll die Vorteile der objektorientierten

Mehr

Datensicherung. Beschreibung der Datensicherung

Datensicherung. Beschreibung der Datensicherung Datensicherung Mit dem Datensicherungsprogramm können Sie Ihre persönlichen Daten problemlos Sichern. Es ist möglich eine komplette Datensicherung durchzuführen, aber auch nur die neuen und geänderten

Mehr

Zeichen bei Zahlen entschlüsseln

Zeichen bei Zahlen entschlüsseln Zeichen bei Zahlen entschlüsseln In diesem Kapitel... Verwendung des Zahlenstrahls Absolut richtige Bestimmung von absoluten Werten Operationen bei Zahlen mit Vorzeichen: Addieren, Subtrahieren, Multiplizieren

Mehr

Abamsoft Finos im Zusammenspiel mit shop to date von DATA BECKER

Abamsoft Finos im Zusammenspiel mit shop to date von DATA BECKER Abamsoft Finos im Zusammenspiel mit shop to date von DATA BECKER Abamsoft Finos in Verbindung mit der Webshopanbindung wurde speziell auf die Shop-Software shop to date von DATA BECKER abgestimmt. Mit

Mehr

ARAkoll 2013 Dokumentation. Datum: 21.11.2012

ARAkoll 2013 Dokumentation. Datum: 21.11.2012 ARAkoll 2013 Dokumentation Datum: 21.11.2012 INHALT Allgemeines... 3 Funktionsübersicht... 3 Allgemeine Funktionen... 3 ARAmatic Symbolleiste... 3 Monatsprotokoll erzeugen... 4 Jahresprotokoll erzeugen

Mehr

Bedienungsanleitung. Stand: 26.05.2011. Copyright 2011 by GEVITAS GmbH www.gevitas.de

Bedienungsanleitung. Stand: 26.05.2011. Copyright 2011 by GEVITAS GmbH www.gevitas.de GEVITAS-Sync Bedienungsanleitung Stand: 26.05.2011 Copyright 2011 by GEVITAS GmbH www.gevitas.de Inhalt 1. Einleitung... 3 1.1. Installation... 3 1.2. Zugriffsrechte... 3 1.3. Starten... 4 1.4. Die Menü-Leiste...

Mehr

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten In dem Virtuellen Seminarordner werden für die Teilnehmerinnen und Teilnehmer des Seminars alle für das Seminar wichtigen Informationen,

Mehr

Agile Vorgehensmodelle in der Softwareentwicklung: Scrum

Agile Vorgehensmodelle in der Softwareentwicklung: Scrum C A R L V O N O S S I E T Z K Y Agile Vorgehensmodelle in der Softwareentwicklung: Scrum Johannes Diemke Vortrag im Rahmen der Projektgruppe Oldenburger Robot Soccer Team im Wintersemester 2009/2010 Was

Mehr

(1) Problemstellung. (2) Kalman Filter

(1) Problemstellung. (2) Kalman Filter Inhaltsverzeichnis (1) Problemstellung...2 (2) Kalman Filter...2 Funktionsweise... 2 Gleichungen im mehrdimensionalen Fall...3 Schätzung des Systemzustands...3 Vermuteter Schätzfehler... 3 Aktualisierung

Mehr

PowerWeiss Synchronisation

PowerWeiss Synchronisation PowerWeiss Synchronisation 1 Einrichtung der Synchronisation I. Starten des Synchronisations Wizard Seite 3 II. Schritt 1 - Benutzer auswählen Seite 3 III. Schritt 2 - Grundlegende Einstellungen Seite

Mehr

1 topologisches Sortieren

1 topologisches Sortieren Wolfgang Hönig / Andreas Ecke WS 09/0 topologisches Sortieren. Überblick. Solange noch Knoten vorhanden: a) Suche Knoten v, zu dem keine Kante führt (Falls nicht vorhanden keine topologische Sortierung

Mehr

5 DATEN. 5.1. Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

5 DATEN. 5.1. Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu Daten Makro + VBA effektiv 5 DATEN 5.1. Variablen Variablen können beliebige Werte zugewiesen und im Gegensatz zu Konstanten jederzeit im Programm verändert werden. Als Variablen können beliebige Zeichenketten

Mehr

! " # $ " % & Nicki Wruck worldwidewruck 08.02.2006

!  # $  % & Nicki Wruck worldwidewruck 08.02.2006 !"# $ " %& Nicki Wruck worldwidewruck 08.02.2006 Wer kennt die Problematik nicht? Die.pst Datei von Outlook wird unübersichtlich groß, das Starten und Beenden dauert immer länger. Hat man dann noch die.pst

Mehr

Grundlagen verteilter Systeme

Grundlagen verteilter Systeme Universität Augsburg Insitut für Informatik Prof. Dr. Bernhard Bauer Wolf Fischer Christian Saad Wintersemester 08/09 Übungsblatt 3 12.11.08 Grundlagen verteilter Systeme Lösungsvorschlag Aufgabe 1: a)

Mehr

Um ein solches Dokument zu erzeugen, muss eine Serienbriefvorlage in Word erstellt werden, das auf die von BüroWARE erstellte Datei zugreift.

Um ein solches Dokument zu erzeugen, muss eine Serienbriefvorlage in Word erstellt werden, das auf die von BüroWARE erstellte Datei zugreift. Briefe Schreiben - Arbeiten mit Word-Steuerformaten Ab der Version 5.1 stellt die BüroWARE über die Word-Steuerformate eine einfache Methode dar, Briefe sowie Serienbriefe mit Hilfe der Korrespondenzverwaltung

Mehr

Task: Nmap Skripte ausführen

Task: Nmap Skripte ausführen Task: Nmap Skripte ausführen Inhalt Einfache Netzwerkscans mit NSE Ausführen des Scans Anpassung der Parameter Einleitung Copyright 2009-2015 Greenbone Networks GmbH Herkunft und aktuellste Version dieses

Mehr

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster Es gibt in Excel unter anderem die so genannten Suchfunktionen / Matrixfunktionen Damit können Sie Werte innerhalb eines bestimmten Bereichs suchen. Als Beispiel möchte ich die Funktion Sverweis zeigen.

Mehr

Anleitung über den Umgang mit Schildern

Anleitung über den Umgang mit Schildern Anleitung über den Umgang mit Schildern -Vorwort -Wo bekommt man Schilder? -Wo und wie speichert man die Schilder? -Wie füge ich die Schilder in meinen Track ein? -Welche Bauteile kann man noch für Schilder

Mehr

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom 21.10.2013b

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom 21.10.2013b AGROPLUS Buchhaltung Daten-Server und Sicherheitskopie Version vom 21.10.2013b 3a) Der Daten-Server Modus und der Tresor Der Daten-Server ist eine Betriebsart welche dem Nutzer eine grosse Flexibilität

Mehr

Hinweise zum Ausfüllen der Zeiterfassung

Hinweise zum Ausfüllen der Zeiterfassung Hinweise zum Ausfüllen der Zeiterfassung Generelle Hinweise zu Excel Ab Version VSA 4.50 wird die dezimale Schreibweise für Zeiteingaben verwendet. Die Zeiterfassung, die Sie erhalten haben wurde für Excel

Mehr

GeoPilot (Android) die App

GeoPilot (Android) die App GeoPilot (Android) die App Mit der neuen Rademacher GeoPilot App machen Sie Ihr Android Smartphone zum Sensor und steuern beliebige Szenen über den HomePilot. Die App beinhaltet zwei Funktionen, zum einen

Mehr

How-to: Webserver NAT. Securepoint Security System Version 2007nx

How-to: Webserver NAT. Securepoint Security System Version 2007nx Securepoint Security System Inhaltsverzeichnis Webserver NAT... 3 1 Konfiguration einer Webserver NAT... 4 1.1 Einrichten von Netzwerkobjekten... 4 1.2 Erstellen von Firewall-Regeln... 6 Seite 2 Webserver

Mehr

Whitepaper. Produkt: combit Relationship Manager 7. combit Relationship Manager email-rückläufer Script. combit GmbH Untere Laube 30 78462 Konstanz

Whitepaper. Produkt: combit Relationship Manager 7. combit Relationship Manager email-rückläufer Script. combit GmbH Untere Laube 30 78462 Konstanz combit GmbH Untere Laube 30 78462 Konstanz Whitepaper Produkt: combit Relationship Manager 7 combit Relationship Manager email-rückläufer Script Inhalt Einleitung 3 Notwendige Anpassungen 3 crm Solution

Mehr

Kurzeinführung LABTALK

Kurzeinführung LABTALK Kurzeinführung LABTALK Mit der Interpreter-Sprache LabTalk, die von ORIGIN zur Verfügung gestellt wird, können bequem Datenmanipulationen sowie Zugriffe direkt auf das Programm (Veränderungen der Oberfläche,

Mehr

Whitepaper. Produkt: combit Relationship Manager / address manager. Dateiabgleich im Netzwerk über Offlinedateien

Whitepaper. Produkt: combit Relationship Manager / address manager. Dateiabgleich im Netzwerk über Offlinedateien combit GmbH Untere Laube 30 78462 Konstanz Whitepaper Produkt: combit Relationship Manager / address manager Dateiabgleich im Netzwerk über Offlinedateien Dateiabgleich im Netzwerk über Offlinedateien

Mehr

Robot Karol für Delphi

Robot Karol für Delphi Robot Karol für Delphi Reinhard Nitzsche, OSZ Handel I Version 0.1 vom 24. Januar 2003 Zusammenfassung Nach der Einführung in die (variablenfreie) Programmierung mit Robot Karol von Freiberger und Krško

Mehr

Datenübernahme easyjob 3.0 zu easyjob 4.0

Datenübernahme easyjob 3.0 zu easyjob 4.0 Datenübernahme easyjob 3.0 zu easyjob 4.0 Einführung...3 Systemanforderung easyjob 4.0...3 Vorgehensweise zur Umstellung zu easyjob 4.0...4 Installation easyjob 4.0 auf dem Server und Arbeitsstationen...4

Mehr

OPERATIONEN AUF EINER DATENBANK

OPERATIONEN AUF EINER DATENBANK Einführung 1 OPERATIONEN AUF EINER DATENBANK Ein Benutzer stellt eine Anfrage: Die Benutzer einer Datenbank können meist sowohl interaktiv als auch über Anwendungen Anfragen an eine Datenbank stellen:

Mehr

Grundlagen von Python

Grundlagen von Python Einführung in Python Grundlagen von Python Felix Döring, Felix Wittwer November 17, 2015 Scriptcharakter Programmierparadigmen Imperatives Programmieren Das Scoping Problem Objektorientiertes Programmieren

Mehr

Anleitung zum Login. über die Mediteam- Homepage und zur Pflege von Praxisnachrichten

Anleitung zum Login. über die Mediteam- Homepage und zur Pflege von Praxisnachrichten Anleitung zum Login über die Mediteam- Homepage und zur Pflege von Praxisnachrichten Stand: 18.Dezember 2013 1. Was ist der Mediteam-Login? Alle Mediteam-Mitglieder können kostenfrei einen Login beantragen.

Mehr

MailUtilities: Remote Deployment - Einführung

MailUtilities: Remote Deployment - Einführung MailUtilities: Remote Deployment - Einführung Zielsetzung Die Aufgabe von Remote Deployment adressiert zwei Szenarien: 1. Konfiguration der MailUtilities von einer Workstation aus, damit man das Control

Mehr

Einführung in die Informatik Tools

Einführung in die Informatik Tools Einführung in die Informatik Tools Werkzeuge zur Erstellung von Softwareprojekten Wolfram Burgard 8.1 Motivation Große Softwareprojekte werden schnell unübersichtlich. Änderungen im Code können leicht

Mehr

Stapelverarbeitung Teil 1

Stapelverarbeitung Teil 1 Stapelverarbeitung Teil 1 In jedem Unternehmen gibt es von Zeit zu Zeit Änderungen in Normen und Firmenstandards, an die aktuelle und bereits bestehende Zeichnungen angepasst werden müssen. Auch Fehler

Mehr

Konfiguration VLAN's. Konfiguration VLAN's IACBOX.COM. Version 2.0.1 Deutsch 01.07.2014

Konfiguration VLAN's. Konfiguration VLAN's IACBOX.COM. Version 2.0.1 Deutsch 01.07.2014 Konfiguration VLAN's Version 2.0.1 Deutsch 01.07.2014 In diesem HOWTO wird die Konfiguration der VLAN's für das Surf-LAN der IAC-BOX beschrieben. Konfiguration VLAN's TITEL Inhaltsverzeichnis Inhaltsverzeichnis...

Mehr

Support-Tipp Mai 2010 - Release Management in Altium Designer

Support-Tipp Mai 2010 - Release Management in Altium Designer Support-Tipp Mai 2010 - Release Management in Altium Designer Mai 2010 Frage: Welche Aufgaben hat das Release Management und wie unterstützt Altium Designer diesen Prozess? Zusammenfassung: Das Glück eines

Mehr

Sie werden sehen, dass Sie für uns nur noch den direkten PDF-Export benötigen. Warum?

Sie werden sehen, dass Sie für uns nur noch den direkten PDF-Export benötigen. Warum? Leitfaden zur Druckdatenerstellung Inhalt: 1. Download und Installation der ECI-Profile 2. Farbeinstellungen der Adobe Creative Suite Bitte beachten! In diesem kleinen Leitfaden möchten wir auf die Druckdatenerstellung

Mehr

Übung: Verwendung von Java-Threads

Übung: Verwendung von Java-Threads Übung: Verwendung von Java-Threads Ziel der Übung: Diese Übung dient dazu, den Umgang mit Threads in der Programmiersprache Java kennenzulernen. Ein einfaches Java-Programm, das Threads nutzt, soll zum

Mehr

EasyWk DAS Schwimmwettkampfprogramm

EasyWk DAS Schwimmwettkampfprogramm EasyWk DAS Schwimmwettkampfprogramm Arbeiten mit OMEGA ARES 21 EasyWk - DAS Schwimmwettkampfprogramm 1 Einleitung Diese Präsentation dient zur Darstellung der Zusammenarbeit zwischen EasyWk und der Zeitmessanlage

Mehr

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss Systeme 1 Kapitel 6 Nebenläufigkeit und wechselseitiger Ausschluss Threads Die Adressräume verschiedener Prozesse sind getrennt und geschützt gegen den Zugriff anderer Prozesse. Threads sind leichtgewichtige

Mehr

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3 Handbuch Fischertechnik-Einzelteiltabelle V3.7.3 von Markus Mack Stand: Samstag, 17. April 2004 Inhaltsverzeichnis 1. Systemvorraussetzungen...3 2. Installation und Start...3 3. Anpassen der Tabelle...3

Mehr

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen: VBA Programmierung mit Excel Schleifen 1/6 Erweiterung der Aufgabe Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen: Es müssen also 11 (B L) x 35 = 385 Zellen berücksichtigt

Mehr

Kompilieren und Linken

Kompilieren und Linken Kapitel 2 Kompilieren und Linken Bevor wir uns auf C++ selbst stürzen, brauchen wir einiges Vorgeplänkel, wie man komfortabel ein größeres C++- kompilieren kann. Mit Java stellt sich der Kompiliervorgang

Mehr

Lizenzierung von System Center 2012

Lizenzierung von System Center 2012 Lizenzierung von System Center 2012 Mit den Microsoft System Center-Produkten lassen sich Endgeräte wie Server, Clients und mobile Geräte mit unterschiedlichen Betriebssystemen verwalten. Verwalten im

Mehr

schnell und portofrei erhältlich bei beck-shop.de DIE FACHBUCHHANDLUNG mitp/bhv

schnell und portofrei erhältlich bei beck-shop.de DIE FACHBUCHHANDLUNG mitp/bhv Roboter programmieren mit NXC für Lego Mindstorms NXT 1. Auflage Roboter programmieren mit NXC für Lego Mindstorms NXT schnell und portofrei erhältlich bei beck-shop.de DIE FACHBUCHHANDLUNG mitp/bhv Verlag

Mehr

Einführung in die Java- Programmierung

Einführung in die Java- Programmierung Einführung in die Java- Programmierung Dr. Volker Riediger Tassilo Horn riediger horn@uni-koblenz.de WiSe 2012/13 1 Wichtig... Mittags keine Pommes... Praktikum A 230 C 207 (Madeleine + Esma) F 112 F 113

Mehr

Modellierung und Programmierung 1

Modellierung und Programmierung 1 Modellierung und Programmierung 1 Prof. Dr. Sonja Prohaska Computational EvoDevo Group Institut für Informatik Universität Leipzig 19. November 2015 Gültigkeitsbereich (Scope) von Variablen { int m; {

Mehr

.NET Code schützen. Projekt.NET. Version 1.0

.NET Code schützen. Projekt.NET. Version 1.0 .NET Code schützen Projekt.NET Informationsmaterial zum Schützen des.net Codes Version 1.0 Autor: Status: Ablage: Empfänger: Seiten: D. Hoyer 1 / 6 Verteiler : Dokument1 Seite 1 von 1 Änderungsprotokoll

Mehr

mywms Vorlage Seite 1/5 mywms Datenhaltung von Haug Bürger

mywms Vorlage Seite 1/5 mywms Datenhaltung von Haug Bürger mywms Vorlage Seite 1/5 mywms Datenhaltung von Haug Bürger Grundlegendes Oracle9i PostgreSQL Prevayler Memory mywms bietet umfangreiche Konfigurationsmöglichkeiten um die Daten dauerhaft zu speichern.

Mehr

Programmierkurs Java

Programmierkurs Java Programmierkurs Java Dr. Dietrich Boles Aufgaben zu UE16-Rekursion (Stand 09.12.2011) Aufgabe 1: Implementieren Sie in Java ein Programm, das solange einzelne Zeichen vom Terminal einliest, bis ein #-Zeichen

Mehr

Konzepte der Informatik

Konzepte der Informatik Konzepte der Informatik Vorkurs Informatik zum WS 2011/2012 26.09. - 30.09.2011 17.10. - 21.10.2011 Dr. Werner Struckmann / Christoph Peltz Stark angelehnt an Kapitel 1 aus "Abenteuer Informatik" von Jens

Mehr

Visio 2013. Grundlagen. Linda York. 1. Ausgabe, Oktober 2013

Visio 2013. Grundlagen. Linda York. 1. Ausgabe, Oktober 2013 Visio 2013 Linda York 1. Ausgabe, Oktober 2013 Grundlagen V2013 2 Visio 2013 - Grundlagen 2 Einfache Zeichnungen erstellen In diesem Kapitel erfahren Sie wie Sie Shapes einfügen, kopieren und löschen was

Mehr

1 Einleitung. Lernziele. Symbolleiste für den Schnellzugriff anpassen. Notizenseiten drucken. eine Präsentation abwärtskompatibel speichern

1 Einleitung. Lernziele. Symbolleiste für den Schnellzugriff anpassen. Notizenseiten drucken. eine Präsentation abwärtskompatibel speichern 1 Einleitung Lernziele Symbolleiste für den Schnellzugriff anpassen Notizenseiten drucken eine Präsentation abwärtskompatibel speichern eine Präsentation auf CD oder USB-Stick speichern Lerndauer 4 Minuten

Mehr

Die Dateiablage Der Weg zur Dateiablage

Die Dateiablage Der Weg zur Dateiablage Die Dateiablage In Ihrem Privatbereich haben Sie die Möglichkeit, Dateien verschiedener Formate abzulegen, zu sortieren, zu archivieren und in andere Dateiablagen der Plattform zu kopieren. In den Gruppen

Mehr

Er musste so eingerichtet werden, dass das D-Laufwerk auf das E-Laufwerk gespiegelt

Er musste so eingerichtet werden, dass das D-Laufwerk auf das E-Laufwerk gespiegelt Inhaltsverzeichnis Aufgabe... 1 Allgemein... 1 Active Directory... 1 Konfiguration... 2 Benutzer erstellen... 3 Eigenes Verzeichnis erstellen... 3 Benutzerkonto erstellen... 3 Profil einrichten... 5 Berechtigungen

Mehr

Speicher in der Cloud

Speicher in der Cloud Speicher in der Cloud Kostenbremse, Sicherheitsrisiko oder Basis für die unternehmensweite Kollaboration? von Cornelius Höchel-Winter 2013 ComConsult Research GmbH, Aachen 3 SYNCHRONISATION TEUFELSZEUG

Mehr

Bedienungsanleitung. Matthias Haasler. Version 0.4. für die Arbeit mit der Gemeinde-Homepage der Paulus-Kirchengemeinde Tempelhof

Bedienungsanleitung. Matthias Haasler. Version 0.4. für die Arbeit mit der Gemeinde-Homepage der Paulus-Kirchengemeinde Tempelhof Bedienungsanleitung für die Arbeit mit der Gemeinde-Homepage der Paulus-Kirchengemeinde Tempelhof Matthias Haasler Version 0.4 Webadministrator, email: webadmin@rundkirche.de Inhaltsverzeichnis 1 Einführung

Mehr

Lineare Gleichungssysteme

Lineare Gleichungssysteme Lineare Gleichungssysteme 1 Zwei Gleichungen mit zwei Unbekannten Es kommt häufig vor, dass man nicht mit einer Variablen alleine auskommt, um ein Problem zu lösen. Das folgende Beispiel soll dies verdeutlichen

Mehr

Algorithmen & Datenstrukturen 1. Klausur

Algorithmen & Datenstrukturen 1. Klausur Algorithmen & Datenstrukturen 1. Klausur 7. Juli 2010 Name Matrikelnummer Aufgabe mögliche Punkte erreichte Punkte 1 35 2 30 3 30 4 15 5 40 6 30 Gesamt 180 1 Seite 2 von 14 Aufgabe 1) Programm Analyse

Mehr

Anleitung fürs Webmail

Anleitung fürs Webmail Anleitung fürs Webmail 29. Apr. 2010 V 1.0.0 Seite 1 / 19 Inhaltsverzeichnis Das neue Webmail...3 Anmeldung...4 E-Mail-Kontrolle...7 Generelle E-Mail-Weiterleitung/ Umleitung...7 Abwesenheits-Benachrichtigung...8

Mehr

2. Programmierung in C

2. Programmierung in C 2. Programmierung in C Inhalt: Überblick über Programmiersprachen, Allgemeines zur Sprache C C: Basisdatentypen, Variablen, Konstanten Operatoren, Ausdrücke und Anweisungen Kontrollstrukturen (Steuerfluss)

Mehr

Vorkurs C++ Programmierung

Vorkurs C++ Programmierung Vorkurs C++ Programmierung Klassen Letzte Stunde Speicherverwaltung automatische Speicherverwaltung auf dem Stack dynamische Speicherverwaltung auf dem Heap new/new[] und delete/delete[] Speicherklassen:

Mehr

iphone-kontakte zu Exchange übertragen

iphone-kontakte zu Exchange übertragen iphone-kontakte zu Exchange übertragen Übertragen von iphone-kontakten in ein Exchange Postfach Zunächst muss das iphone an den Rechner, an dem es üblicherweise synchronisiert wird, angeschlossen werden.

Mehr

Daten-Synchronisation zwischen dem ZDV-Webmailer und Outlook (2002-2007) Zentrum für Datenverarbeitung der Universität Tübingen

Daten-Synchronisation zwischen dem ZDV-Webmailer und Outlook (2002-2007) Zentrum für Datenverarbeitung der Universität Tübingen Daten-Synchronisation zwischen dem ZDV-Webmailer und Outlook (2002-2007) Zentrum für Datenverarbeitung der Universität Tübingen Inhalt 1. Die Funambol Software... 3 2. Download und Installation... 3 3.

Mehr

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER Inhalt 1 Einleitung... 1 2 Einrichtung der Aufgabe für die automatische Sicherung... 2 2.1 Die Aufgabenplanung... 2 2.2 Der erste Testlauf... 9 3 Problembehebung...

Mehr

NAS 259 Ihre Daten mit Remote Sync (Rsync) schützen

NAS 259 Ihre Daten mit Remote Sync (Rsync) schützen NAS 259 Ihre Daten mit Remote Sync (Rsync) schützen Einen Rsync-Sicherungsauftrag erstellen und ausführen A S U S T O R - K o l l e g Kursziele Nach Abschluss dieses Kurses sollten Sie: 1. Ü ber ein grundlegendes

Mehr

Kommunikations-Management

Kommunikations-Management Tutorial: Wie kann ich E-Mails schreiben? Im vorliegenden Tutorial lernen Sie, wie Sie in myfactory E-Mails schreiben können. In myfactory können Sie jederzeit schnell und einfach E-Mails verfassen egal

Mehr

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang sysplus.ch outlook - mail-grundlagen Seite 1/8 Outlook Mail-Grundlagen Posteingang Es gibt verschiedene Möglichkeiten, um zum Posteingang zu gelangen. Man kann links im Outlook-Fenster auf die Schaltfläche

Mehr

Lizenzen auschecken. Was ist zu tun?

Lizenzen auschecken. Was ist zu tun? Use case Lizenzen auschecken Ihr Unternehmen hat eine Netzwerk-Commuterlizenz mit beispielsweise 4 Lizenzen. Am Freitag wollen Sie Ihren Laptop mit nach Hause nehmen, um dort am Wochenende weiter zu arbeiten.

Mehr

OpenMP am Beispiel der Matrizenmultiplikation

OpenMP am Beispiel der Matrizenmultiplikation OpenMP am Beispiel der Matrizenmultiplikation David J. Meder, Dr. Victor Pankratius IPD Tichy Lehrstuhl für Programmiersysteme KIT die Kooperation von Forschungszentrum Karlsruhe GmbH und Universität Karlsruhe

Mehr

Effiziente Administration Ihrer Netzwerkumgebung

Effiziente Administration Ihrer Netzwerkumgebung Admin Anwender Aufträge, Freigaben Verwaltet Benutzer, Mailboxen, Ordner und vergibt Berechtigungen Anbindung von Fremdsystemen Erzeugt und pflegt Mailboxen und Datenbanken Benutzerinformationen und Konventionen

Mehr

Guide DynDNS und Portforwarding

Guide DynDNS und Portforwarding Guide DynDNS und Portforwarding Allgemein Um Geräte im lokalen Netzwerk von überall aus über das Internet erreichen zu können, kommt man um die Themen Dynamik DNS (kurz DynDNS) und Portweiterleitung(auch

Mehr

1. Einführung. 2. Archivierung alter Datensätze

1. Einführung. 2. Archivierung alter Datensätze 1. Einführung Mit wachsender Datenmenge und je nach Konfiguration, kann orgamax mit der Zeit langsamer werden. Es gibt aber diverse Möglichkeiten, die Software wieder so zu beschleunigen, als würden Sie

Mehr

Einführung in die Java- Programmierung

Einführung in die Java- Programmierung Einführung in die Java- Programmierung Dr. Volker Riediger Tassilo Horn riediger horn@uni-koblenz.de WiSe 2012/13 1 Wichtig... Mittags Pommes... Praktikum A 230 C 207 (Madeleine) F 112 F 113 (Kevin) E

Mehr

Beschreibung und Bedienungsanleitung. Inhaltsverzeichnis: Abbildungsverzeichnis: Werkzeug für verschlüsselte bpks. Dipl.-Ing.

Beschreibung und Bedienungsanleitung. Inhaltsverzeichnis: Abbildungsverzeichnis: Werkzeug für verschlüsselte bpks. Dipl.-Ing. www.egiz.gv.at E-Mail: post@egiz.gv.at Telefon: ++43 (316) 873 5514 Fax: ++43 (316) 873 5520 Inffeldgasse 16a / 8010 Graz / Austria Beschreibung und Bedienungsanleitung Werkzeug für verschlüsselte bpks

Mehr

Session Beans & Servlet Integration. Ralf Gitzel ralf_gitzel@hotmail.de

Session Beans & Servlet Integration. Ralf Gitzel ralf_gitzel@hotmail.de s & Servlet Integration Ralf Gitzel ralf_gitzel@hotmail.de 1 Themenübersicht Ralf Gitzel ralf_gitzel@hotmail.de 2 Übersicht Motivation Das Interface Stateful und Stateless s Programmierung einer Stateful

Mehr

Migration der Abteilungslaufwerke von UKKSRVFILE011 nach FILER2. Anleitung zur Lösung verschiedener Probleme durch den Anwender selber

Migration der Abteilungslaufwerke von UKKSRVFILE011 nach FILER2. Anleitung zur Lösung verschiedener Probleme durch den Anwender selber Migration der Abteilungslaufwerke von UKKSRVFILE011 nach FILER2 Anleitung zur Lösung verschiedener Probleme durch den Anwender selber Inhaltsverzeichnis 1. Migration der Abteilungsordner...2 1.1 Änderung

Mehr

Informationen zum neuen Studmail häufige Fragen

Informationen zum neuen Studmail häufige Fragen 1 Stand: 15.01.2013 Informationen zum neuen Studmail häufige Fragen (Dokument wird bei Bedarf laufend erweitert) Problem: Einloggen funktioniert, aber der Browser lädt dann ewig und zeigt nichts an Lösung:

Mehr

Barcodedatei importieren

Barcodedatei importieren Barcodedatei importieren Inhaltsverzeichnis 1 Schnittstelle Barcodedatei importieren... 2 1.1 Funktion... 2 1.2 Konfiguration... 2 1.2.1 Lizenz... 2 1.2.2 Einstellungen... 2 1.2.3 Vorarbeiten... 3 1.2.3.1

Mehr

Felder, Rückblick Mehrdimensionale Felder. Programmieren in C

Felder, Rückblick Mehrdimensionale Felder. Programmieren in C Übersicht Felder, Rückblick Mehrdimensionale Felder Rückblick Vereinbarung von Feldern: typ name [anzahl]; typ name = {e1, e2, e3,..., en} Die Adressierung von Feldelementen beginnt bei 0 Die korrekte

Mehr

Excel-Anwendung Wartungsplan

Excel-Anwendung Wartungsplan Excel-Anwendung Wartungsplan 1. Eigenschaften 2. Installation 3. Makros in Excel 2010 aktivieren 4. Hinweise zur Eingabe der Daten 5. Dateneingabe 6. Suchblatt 7. Autor 1. Eigenschaften (zurück) Wartungsplan

Mehr

A1.7: Entropie natürlicher Texte

A1.7: Entropie natürlicher Texte A1.7: Entropie natürlicher Texte Anfang der 1950er Jahre hat Claude E. Shannon die Entropie H der englischen Sprache mit einem bit pro Zeichen abgeschätzt. Kurz darauf kam Karl Küpfmüller bei einer empirischen

Mehr

Computerarithmetik ( )

Computerarithmetik ( ) Anhang A Computerarithmetik ( ) A.1 Zahlendarstellung im Rechner und Computerarithmetik Prinzipiell ist die Menge der im Computer darstellbaren Zahlen endlich. Wie groß diese Menge ist, hängt von der Rechnerarchitektur

Mehr

Kurzeinführung Moodle

Kurzeinführung Moodle Kurzeinführung Moodle 1. Einstieg, Kursinhalte, Datei-Download Nachdem Sie sich erfolgreich registriert und eingeloggt haben, gelangen Sie zu Ihrer Hauptseite. Aktivieren Sie Meine Startsteite um Ihren/Ihre

Mehr

Anleitung zur Verwendung der VVW-Word-Vorlagen

Anleitung zur Verwendung der VVW-Word-Vorlagen Anleitung zur Verwendung der VVW-Word-Vorlagen v1.0. Feb-15 1 1 Vorwort Sehr geehrte Autorinnen und Autoren, wir haben für Sie eine Dokumentenvorlage für Microsoft Word entwickelt, um Ihnen die strukturierte

Mehr

Einfache und effiziente Zusammenarbeit in der Cloud. EASY-PM Office Add-Ins Handbuch

Einfache und effiziente Zusammenarbeit in der Cloud. EASY-PM Office Add-Ins Handbuch Einfache und effiziente Zusammenarbeit in der Cloud EASY-PM Office Add-Ins Handbuch Inhaltsverzeichnis 1. Einführung... 3 2. Ribbonmenü... 4 3. Dokument... 5 3.1 Öffnen... 5 3.2 Speichern... 6 3.3 Speichern

Mehr

Anwenderdokumentation AccountPlus GWUPSTAT.EXE

Anwenderdokumentation AccountPlus GWUPSTAT.EXE AccountPlus Inhaltsverzeichnis Inhaltsverzeichnis Anwenderdokumentation AccountPlus GWUPSTAT.EXE (vorläufig) ab Version 6.01 INHALTSVERZEICHNIS...1 1 ALLGEMEINES...2 2 INSTALLATION UND PROGRAMMAUFRUF...2

Mehr

Hilfedatei der Oden$-Börse Stand Juni 2014

Hilfedatei der Oden$-Börse Stand Juni 2014 Hilfedatei der Oden$-Börse Stand Juni 2014 Inhalt 1. Einleitung... 2 2. Die Anmeldung... 2 2.1 Die Erstregistrierung... 3 2.2 Die Mitgliedsnummer anfordern... 4 3. Die Funktionen für Nutzer... 5 3.1 Arbeiten

Mehr

RT Request Tracker. Benutzerhandbuch V2.0. Inhalte

RT Request Tracker. Benutzerhandbuch V2.0. Inhalte RT Request Tracker V2.0 Inhalte 1 Was ist der RT Request Tracker und wo finde ich ihn?...2 2 Was möchten wir damit erreichen?...2 3 Wie erstelle ich ein Ticket?...2 4 Wie wird das Ticket abgearbeitet?...4

Mehr

Es sollte die MS-DOS Eingabeaufforderung starten. Geben Sie nun den Befehl javac ein.

Es sollte die MS-DOS Eingabeaufforderung starten. Geben Sie nun den Befehl javac ein. Schritt 1: Installation des Javacompilers JDK. Der erste Start mit Eclipse Bevor Sie den Java-Compiler installieren sollten Sie sich vergewissern, ob er eventuell schon installiert ist. Gehen sie wie folgt

Mehr

VB.net Programmierung und Beispielprogramm für GSV

VB.net Programmierung und Beispielprogramm für GSV VB.net Programmierung und Beispielprogramm für GSV Dokumentation Stand vom 26.05.2011 Tel +49 (0)3302 78620 60, Fax +49 (0)3302 78620 69, info@me-systeme.de, www.me-systeme.de 1 Inhaltsverzeichnis Vorwort...2

Mehr

Zentrale Installation

Zentrale Installation Einführung STEP 7 wird durch ein Setup-Programm installiert. Eingabeaufforderungen auf dem Bildschirm führen Sie Schritt für Schritt durch den gesamten Installationsvorgang. Mit der Record-Funktion steht

Mehr

C++11 C++14 Kapitel Doppelseite Übungen Musterlösungen Anhang

C++11 C++14 Kapitel Doppelseite Übungen Musterlösungen Anhang Einleitung Dieses Buch wendet sich an jeden Leser, der die Programmiersprache C++ neu lernen oder vertiefen möchte, egal ob Anfänger oder fortgeschrittener C++-Programmierer. C++ ist eine weitgehend plattformunabhängige

Mehr

1 Verarbeitung personenbezogener Daten

1 Verarbeitung personenbezogener Daten .WIEN WHOIS-Politik Inhalt 1 Verarbeitung personenbezogener Daten... 1 2 Zur Verwendung gesammelte Informationen... 1 3 WHOIS-Suchfunktion... 2 3.1 Einleitung... 2 3.2 Zweck... 3 3.3 Identifizieren von

Mehr