============================= BLAS mit gemischen Datentypen ============================= Bisher mussten bei BLAS Funktionen alle Skalare, Vektor- und Matrixelemente den gleichen Datentyp besitzen (z.B. alle den Typ _double_). Mittels Template Funktionen können wir dies verallgemeinern, solange dies mathematisch definiert ist. Dazu betrachten wir als Beispiel die Operation `gecopy` zum Kopieren einer Matrix: ---- LATEX -------------------------------------------------------------------- B \leftarrow A ------------------------------------------------------------------------------- - Sind die Elemente von `A` und `B` jeweils vom Typ `float` und `double` dann kann die Operation durch einen elementweisen _upcast_ durchgeführt werden. Dabei verliert man keine Genauigkeit bei den Fließkommazahlen. In C/C++ wird deshalb bei ---- CODE(type=cc) ----------------------------------------------------------- float x; double y; // ... y = x; // implicit upcast ------------------------------------------------------------------------------ der Upcast automatisch durchgeführt. - Sind die Elemente von `A` und `B` jeweils vom Typ `double` und `float` dann muss durch Runden elementweise ein _downcast_ durchgeführt werden. Bei einem Code wie ---- CODE (file=session12/page01/downcast.cc) -------------------------------- int main() { double x = 1.2; float y; y = x; // implicit downcast: double to float return y; // implicit downcast: float to int } ------------------------------------------------------------------------------ erhält man beim Übersetzen eine Warnung. Zumindest dann, wenn man beim `g++` Compiler entsprechende Warnungen aktiviert: ---- SHELL (path=session12/page01) ------------------------------------------- g++ -Wall downcast.cc g++ -Wall -Wconversion downcast.cc ------------------------------------------------------------------------------ So viel zum Thema `-Wall` bedeutet, dass alle Warnungen ausgegeben werden sollen. - Eine Konvertierung wie beispielsweis von `std::complex` nach `double` ist natürlich im Allgemeinen nicht möglich: ---- CODE (file=session12/page01/downcast2.cc) ------------------------------- #include int main() { std::complex z(1,2); double y; y = z; // error return y; // implicit downcast: float to int } ------------------------------------------------------------------------------ Entsprechend erhält man auch einen Fehler: ---- SHELL (path=session12/page01) ------------------------------------------- g++ downcast2.cc ------------------------------------------------------------------------------ Weitere Vorüberlegungen ======================= - Wie bei jedem Projekt muss in der Anfangsphase ständig re-organisiert werden. Das betrifft zum Beispiel Gliederung in Unterverzeichnisse und einzelne Source-/Header Files: - Das gesamte Projekt bekommt ein eigenes Verzeichnis `hpc`. Dies hat zunächst folgende Unterverzeichnisse: - `hpc/ulmblas` wird alle low-level BLAS Funktionen enthalten. Dies sind die BLAS Funktionen, bei denen ohne Klassen sondern direkt mit Zeigern gearbeitet wird. Hier sollen sich C-Programmierier wohlfühlen, selbst dann wenn diese C++ Funktion Templates verwenden. - `hpc/tests` wird verschiedene Tests enthalten. Natürlich sollen alle Include-Guards entsprechend angepasst werden. Diese sollen den Pfad relativ zum Projektverzeichnis enthalten und natrülich nur aus Großbuchstaben bestehen. Die Schrägstriche ersetzen wir durch Unterstriche. Für `hpc/ulmblas/gecopy.h` erhält man so ---- CODE (type=cc) -------------------------------------------------------- #ifndef HPC_ULMBLAS_GECOPY_H #define HPC_ULMBLAS_GECOPY_H 1 // ... #endif // HPC_ULMBLAS_GECOPY_H ---------------------------------------------------------------------------- - Um Namenskonflikte zu vermeiden, verwenden wir nach wie vor Namensräume. Ein einziger Namensraum wird aber nicht reichen. Wir werden BLAS Funktionen mit low-level Interface (mit Dimensionen und Zeiger) und BLAS Funktionen mit high-level Interface (mit Matrix/Vektor Klassen) implementieren. Wir werden deshlab verschachtelte Namensräume verwenden. Funktionen in `hpc/ulmblas` sollen im Namensraum `hpc::ulmblas` liegen. Für `hpc/ulmblas/gecopy.h` erhält man so ---- CODE (type=cc) -------------------------------------------------------- #ifndef HPC_ULMBLAS_GECOPY_H #define HPC_ULMBLAS_GECOPY_H 1 namespace hpc { namespace ulmblas { // Definition of function gecopy ... } } // namespace ulmblas, hpc #endif // HPC_ULMBLAS_GECOPY_H ---------------------------------------------------------------------------- - Das gesamte Projekt soll später eine eigenständige Bibliothek werden. Da wir Templates verwenden, wird dies eine sogenannte _header-only Library_. Es wird also keine statische `libhpc.a` oder dynamische `libhpc.so` erzeugt. Damit die Bibliothek von einem Benutzer verwendet werden kann, muss dieser beim übersetzen lediglich den _Include-Path_ beim Übersetzen anpassen: ---- SHELL (path=session12/page01/hpc/tests) ------------------------------- g++ -std=c++11 -Wall +++ -I/home/numerik/hpc/ws15/uebungen/session12/page01/ +++ gecopy.cc ---------------------------------------------------------------------------- Dabei ist `/home/numerik/hpc/ws15/uebungen/session12/page01/` in diesem Fall das Verzeichnis, in dem die `hpc` Bibliothek (bestehend aus den Unterverzeichnissen `hpc/`, `hpc/ulmblas`, usw.) liegt. Bei Include-Direktiven soll ab jetzt ausgegangen werden, dass der Include-Pfad das Verzeichnis `hpc` enthält. Ein Header `gecopy.h` in `hpc/ulmblas` kann deshalb stets relativ zum Projektverzeichnis mit ---- CODE (type=cc) -------------------------------------------------------- #include ---------------------------------------------------------------------------- eingebunden werden. Andernfalls müsste ein Test `gecopy.cc` in `hpc/tests` diesen mit ---- CODE (type=cc) -------------------------------------------------------- #include "../ulmblas/gecopy.h" ---------------------------------------------------------------------------- einbinden. Aufgaben ======== - Legt die Verzeichnis `hpc`, `hpc/tests` und `hpc/ulmblas` an. - Implementiert in `hpc/ulmblas` eine low-level BLAS Funktion `gecopy`, die Matrizen kopiert, deren Elemente verschieden sein können. Dabei sollen nur implizite Casts verwendet werden. Warnungen und Fehler sollen also wie oben produziert werden. Eine minimale Optimierung soll an dieser Stelle ebenfalls eingebaut werden. Zumindest dann, wenn beide Matrizen zeilen- oder spaltenweise im Speicher liegen, sollen die Matrizen dementsprechend auch abgelaufen werden. - Wir werden sehen, dass unser bisheriges Design für eine Matrix-Klasse und zugehörige high-level BLAS Funktionen noch nicht flexibel genug sind. Bevor wir diese verbessern benutzen wir primitive Tests, die ohne Klassen arbeiten. Um euer `gecopy` zu testen, könnt ihr nachfolgende, primitive Vorlage verwenden. Natürlich sollet ihr dieses Programm auch verstehen und als Gelegenheit nutzen, um zum Beispiel die Verwendung von Lambdas zu wiederholen: :import: session12/page01/hpc/tests/gecopy.cc - Testet das Verhalten für (mindestens) folgende Fälle: +--------------------------+--------------------------+ | Typ der Elemente von `A` | Typ der Elemente von `B` | +--------------------------+--------------------------+ | float | double | +--------------------------+--------------------------+ | double | float | +--------------------------+--------------------------+ | float | std::complex | +--------------------------+--------------------------+ | std::complex | float | +--------------------------+--------------------------+ | float | std::complex | +--------------------------+--------------------------+ | std::complex | std::complex | +--------------------------+--------------------------+ :navigate: up -> doc:index next -> doc:session12/page02