Numerische Fehlerkorrektur und - STS
Transcription
Numerische Fehlerkorrektur und - STS
Bachelorarbeit Sebastian Elm Numerische Fehlerkorrektur und Programmflusskontrolle zur Behandlung von Soft Errors 29. Juli 2014 betreut durch: Prof. Dr. Sibylle Schupp Hamburg University of Technology (TUHH) Technische Universität Hamburg-Harburg Institute for Software Systems 21073 Hamburg Inhaltsverzeichnis Inhaltsverzeichnis Eidesstattliche Erklärung v 1. Einleitung 1 2. Soft Errors 3 3. Lösen eines linearen Gleichungssystems 3.1. Anwendungsbezug . . . . . . . . . . . . . . 3.2. Verfahren der konjugierten Gradienten . . . 3.2.1. Minimierung der quadratischen Form 3.2.2. Berechnung der Lösung . . . . . . . 3.3. Algorithmus . . . . . . . . . . . . . . . . . . 3.4. Implementierung . . . . . . . . . . . . . . . 3.4.1. Datentyp Vektor . . . . . . . . . . . 3.4.2. Matrix- und Vektoroperationen . . . 3.5. Betrachtung der Effizienz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 . 5 . 5 . 5 . 6 . 7 . 8 . 8 . 9 . 10 4. Error Correction Code 11 4.1. Realisierung der ECC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.2. Betrachtung der Effizienz . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 5. Programmflusskontrolle 5.1. Defensive Programming . . . . . . 5.2. Anwendung zur Korrektur von Soft 5.3. Implementierung . . . . . . . . . . 5.4. Betrachtung der Effizienz . . . . . . . . . Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 13 13 14 15 6. Numerische Fehlerkorrektur 17 6.1. Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 6.2. Betrachtung der Effizienz . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 7. Software-Tests 19 7.1. Äquivalenzklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 7.2. Mehrfachbedingungsüberdeckung . . . . . . . . . . . . . . . . . . . . . . . 20 8. Implementierung eines Tests unter FITIn 8.1. Annotation des Quellcodes . . . . . . . . . . . . . 8.2. Steuerung mittels eines Lua-Skriptes . . . . . . . 8.3. Injektion eines Soft Errors in Kontrollstrukturen 8.4. Testautomatisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 23 24 25 26 iii Inhaltsverzeichnis 9. Test der Behandlungsmethoden 9.1. Testumgebung . . . . . . . . . . . . 9.2. Test der Programmflusskontrolle . . 9.2.1. Spezifikation der Tests . . . . 9.2.2. Realisierung . . . . . . . . . . 9.2.3. Testergebnisse . . . . . . . . . 9.2.4. Evaluation . . . . . . . . . . . 9.3. Test der numerischen Fehlerkorrektur 9.3.1. Spezifikation der Tests . . . . 9.3.2. Realisierung . . . . . . . . . . 9.3.3. Testergebnisse . . . . . . . . . 9.3.4. Evaluation . . . . . . . . . . . 9.3.5. Vergleich mit Oboril et al. . . 9.4. Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 30 30 32 32 33 34 34 34 35 36 36 37 10.Zusammenfassung und Ausblick 39 Abbildungsverzeichnis 41 Literaturverzeichnis 44 Begriffe und Abkürzungen 45 A. Anhang A.1. Die Lösung von Ax = b minimiert die quadratische Form A.2. Implementierung der Matrix- und Vektoroperationen . . A.3. Beispiel eines Kompilierungbefehls . . . . . . . . . . . . A.4. Inhalte des digitalen Mediums . . . . . . . . . . . . . . . iv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 47 48 50 50 Eidesstattliche Erklärung Ich, Sebastian Elm, versichere an Eides statt, dass ich die vorliegende Bachelorarbeit mit dem Titel Numerische Fehlerkorrektur und Programmflusskontrolle zur Behandlung von Soft Errors selbstständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel verwendet habe. Diese Arbeit wurde in dieser oder ähnlicher Form bisher keiner anderen Prüfungskommission vorgelegt. Buxtehude, den 29. Juli 2014 (Unterschrift) Sebastian Elm <[email protected]> Matrikelnummer: 21157971 Studiengang: Informatik-Ingenieurwesen B. Sc. v 1. Einleitung Soft Errors (=induzierte Bitflips) besitzen u. a. durch extreme Umgebungsbedingungen, bspw. von rechnergestützten Anwendungen im Weltraum, eine große Bedeutung [9]. Durch Soft Errors kann sich der Programmablauf und das Berechnungsergebnis ungewollt und unkontrolliert verändern; mitunter sind auch Programmabstürze die Folge. Zusätzlich sind Soft Errors nicht reproduzierbar, was die Fehlerdiagnose erschwert [6]. Daher sollen in dieser Arbeit - motiviert durch den Artikel von Oboril et al. ([9]) - Ansätze auf Seiten der Software zur Behandlung von Soft Errors anhand eines Beispielproblems vorgestellt werden. Das Beispiel ist ein aus [9] übernommenes Modell, was die Lösung eines linearen Gleichungssystems beinhaltet [9]. Lineare Gleichungssysteme werden häufig zur Berechnung von Differentialgleichungen in Wettervorhersagen oder Umströmungsberechnungen von Körpern benötigt [14]. Das Verfahren der konjugierten Gradienten (engl.: conjugated gradients method, kurz: CG-Verfahren) ist eine Möglichkeit zur Lösung der linearen Gleichungssysteme [12]. Eine technische Maßnahme zur Prävention von Soft Errors besteht in der Verwendung von Speicher, der durch einen Error Correction Code (ECC) geschützt wird [9, 10]. Diese bieten jedoch keinen allumfassenden Schutz [9, 6]. Der erste in der Arbeit vorgestellte softwarebasierte Ansatz, die Programmflusskontrolle, adressiert das Problem von Soft Errors in Kontrollstrukturen. Dazu werden, ähnlich dem Defensive Programming [2], Assertions eingefügt. Durch die neuartige Anwendung können die Kontrollstrukturen überprüft und ggf. der letzte Ausführungszustand wiederhergestellt werden. Der Ansatz soll anhand des CG-Verfahrens implementiert und untersucht werden. Zudem soll die Programmflusskontrolle bezüglich der These, sie schütze die Kontrollstrukturen des CG-Verfahrens vor einem Soft Error, getestet werden. Der zweite, in dem Papier von Oboril et al. vorgestellte, Ansatz behandelt in erster Linie die durch Soft Errors gefährdete Datenintegrität. Dabei handelt es sich um eine Erweiterung des CG-Verfahrens; es wurde eine einschließende Schleife zur numerischen Fehlerkorrektur eingeführt [9]. Die numerische Fehlerkorrektur soll in dieser Arbeit implementiert und analysiert werden; zusätzlich wird sie auf die Wirksamkeit bei einem Soft Error getestet. Die Tests werden mittels FITIn realisiert; FITIn ist ein von Clemens Terasa und Marcel Heing-Becker entwickeltes, auf Valgrind basierendes Werkzeug [4]. FITIn erlaubt es, Soft Errors in Variablen und Speicherstellen zu injizieren und damit die Auswirkung eines solchen auf die Anwendung zu simulieren, zu analysieren und zu testen. Zum Testen wird das White-Box-Testing [1] eingesetzt. Zusätzlich werden Äquivalenzklassen zur Reduktion der Anzahl der Testfälle verwendet. Es wird gezeigt, dass die Tests der Verfahren die jeweilige These bestätigen und Aussagen über Effektivität und Effizienz erlauben. Daraufhin erfolgt eine Beurteilung der Programmflusskontrolle und der numerischen Fehlerkorrektur. Abschließend werden die Ergebnisse kurz zusammengefasst; zudem werden Einschränkungen und weiterführende Ansätze aufgezeigt. 1 2. Soft Errors Das Phänomen der Soft Errors ist, auch wenn derzeit hochaktuell (vgl. [9, S. 144]), schon länger bekannt; erste Beobachtungen über diese Art Fehler wurden bereits im Jahr 1975 getroffen [6, S. 128]. Mit Soft Error wird ein temporärer Fehlzustand eines Bits, also eine temporäre Negierung des Zustands bezeichnet; die Ursache sind sogenannte single event upsets (zu deutsch: einmalige Veränderungen). Diese entstehen unter Anderem durch den Einfluss von Strahlung auf CMOS-Transistoren, wie sie in aktuellen Prozessoren eingesetzt werden. Beispiele für die Strahlung sind komische Strahlung oder elektromagnetische Strahlung, die aus Schaltkreisen emittiert wurde. Treffen energetische Teilchen auf einen Transistor, kann dieser kurzgeschlossen werden und schalten, was zu einer Zustandsänderung führt. Bricht der Strom der Teilchen ab, wechselt der Transistor in seinen ursprünglichen Zustand zurück. Aufgrund der fehlenden Möglichkeit, diesen Fehler vorherzusagen, und der nur temporären Beeinflussung erhielt diese Art Fehler die Bezeichnung Soft Error [6, S. 128]. Auf Ebene der Software kann ein Soft Error mitunter fatale Folgen haben. Da ein Soft Error in jedem Speicherelement, vom Hauptspeicher bis zum Prozessorcache, auftreten kann, reichen die Folgen von der Änderung der letzten Nachkommastelle in einer Berechnung bis zum kompletten Programm- oder Systemabsturz. Für ersteres reicht es aus, dass ein Soft Error kurz vor oder während des Leseprozesses aus einer Datenvariable für eine Berechnung erfolgt; ist die Datenvariable jedoch der Speicher für eine Speicheradresse oder eine Funktionsadresse, resultieren aus dem Soft Error Zugriffe auf falsche Speicherbereiche oder das Ausführen der falschen Funktion [9, 6]. Zur Verdeutlichung ist in Abbildung 1 das Ablaufdiagramm einer if-Abfrage dargestellt. Bei normaler Ausführung würde die Bedingung x > 0 ausgewertet werden und x > 0 wahr A falsch B Abbildung 1.: Ablaufdiagramm einer if-Abfrage dementsprechend dann entlang des korrekten Pfades die Durchführung fortgeführt. Ist beispielsweise die Bedingung x > 0 erfüllt, so wäre das dann Pfad A. Allerdings besteht bei diesem einfachen Vorgang schon an mehr als zwei Stellen die Gefahr, dass ein Soft 3 2. Soft Errors Error den Fortgang beeinflussen könnte: zum Einen könnte ein Soft Error beim Auslesen des Wertes von x diesen so beeinflussen, sodass die Überprüfung einen anderen Ausgang nimmt, als angenommen. Eine andere Möglichkeit bestünde darin, dass der Soft Error direkt in dem Ergebnis der Überprüfung auftritt und dadurch den Pfad verändert. Bei Rückblick auf das vorige Beispiel, in dem x > 0 erfüllt ist, wäre dies dann Pfad B. Weiterhin ist es theoretisch möglich, dass ein Soft Error das Ziel des Sprungbefehls verändert, und somit keiner der beiden gezeigten Pfade genommen wird, oder dass das Programm komplett abstürzt. Aufgrund der oben dargestellten Folgen ist es sinnvoll, Soft Errors zu verhindern oder zu behandeln. Dafür sollen ab Kapitel 4 entsprechende Verfahren vorgestellt werden. 4 3. Lösen eines linearen Gleichungssystems In dieser Arbeit werden ab Kapitel 5 zwei Ansätze auf Seiten der Software zur Behandlung von Soft Errors vorgestellt werden. Zur Veranschaulichung der Auswirkungen wird ein Anwendungsfall verwendet, der u.a. der Industrie entstammt [9, 14]: das Lösen eines linearen Gleichungssystems, welches auf Differentialgleichungen beruht. 3.1. Anwendungsbezug Differentialgleichungen spielen eine wichtige Rolle in der heutigen Industrie. So ist schon die Anwendung in der numerischen Strömungsberechnung sehr vielfältig: als Werkzeug zur virtuellen Simulation werden sie u.a. in der Forschung, Entwicklung, Luftfahrt oder Automobilindustrie eingesetzt; aber auch in der Medizin oder der Wettervorhersage finden sie ihre Anwendung [9, 14]. Dort besitzen die modellierten Probleme häufig eine hohe Komplexität sowie große Dimension und eine daraus resultierende lange Berechnungszeit zur Simulation. Daher ist es sinnvoll, diese vor Soft Errors zu schützen, um Kosten und Zeit zu sparen [9, S. 144]. 3.2. Verfahren der konjugierten Gradienten Das Verfahren der konjugierten Gradienten (engl.: conjugated gradients method, kurz: CG-Verfahren) ist eine Lösungsmethode für lineare Gleichungssysteme (LGS)(Gleichung 1). Es eignet sich besonders für symmetrische, positiv definite Systemmatrizen. [12, 9]. Das CG-Verfahren basiert auf der iterativen Berechnung eines Vektors, der von einem gegebenen Startpunkt aus mit jedem Schritt der exakten Lösung angenähert wird [12]. Im Folgenden soll dieses Verfahren genauer erläutert werden. A∗x=b (1) 1 f (x) = xT Ax − bT x + c 2 (2) In Gleichung 1 ist eine Formel für lineare Gleichungssysteme, in Gleichung 2 die Formel für eine quadratische Form des LGS angegeben; dabei ist A eine Matrix der Dimension N xN , x sowie b sind Vektoren der Dimension N und c ist eine skalare Konstante. 3.2.1. Minimierung der quadratischen Form Ausgehend von der in Gleichung 2 dargestellten Formel für eine quadratische Form kann gezeigt werden, dass die Lösung des LGS in 1 äquivalent zur Minimierung von 2 ist (siehe 5 3. Lösen eines linearen Gleichungssystems Anhang A.1). Diese Minimierung kann durch das Ableiten von f (x) und anschließender Berechnung und Einordnung der Nullstellen erfolgen. Es gilt: f (x) = min → f 0 (x) = 0 (3) Bei einem Gleichungssystem wie der quadratischen Form besteht die Ableitung der Funktion aus dem Gradienten (Gleichung 4). Dieser gibt, im geometrischen Kontext, in jedem Punkt die Richtung des steilsten An- bzw. Abstiegs an. f 0 (x) = ∂ ∂x1 f (x) ∂ ∂x2 f (x) ... ∂ ∂xn f (x) (4) Angewendet auf Gleichung 2 ergibt sich f 0 (x) = 12 AT x + 21 Ax − b, welches sich aus Gründen der Symmetrie zu Gleichung 5 reduzieren lässt. f 0 (x) = Ax − b (5) Dies wird zur Minimierung mit 0 gleichgesetzt (Gleichung 6) und anschließend die Lösung mit dem CG-Verfahren berechnet [12]. 0 = Ax − b → f 0 (x) = min (6) 3.2.2. Berechnung der Lösung Zur Berechnung von x in Gleichung 6 verwendet das CG-Verfahren, ausgehend von einem gegebenen Startvektor, A-orthogonale Vektoren, die in jeder Iteration berechnet werden. Dabei bezeichnen die A-orthogonale Vektoren die Richtung mit dem betragsmäßig größten Gradienten. Zwei Vektoren di und dj sind orthogonal respektiv der Matrix A, wenn Gleichung 7 erfüllt ist [12, S. 22]. (7) dTi Adj = 0 Diese werden nach dem Verfahren des steilsten Abstiegs zur Lösung aufsummiert, sodass in jeder Iteration eine Dimension der Lösung minimiert wird [12]. xi+1 = xi + αi di (8) Die Neuberechnung des Lösungsvektors x ist in Gleichung 8 dargestellt. di bezeichnet dabei die Richtung des steilsten Abstiegs (ausgehend von xi ) und ist der in diesem Schritt berechnete A-orthogonale Vektor; αi ist der Skalierungsfaktor zur Minimierung des Abstands in dieser Richtung. Zur Bestimmung von αi wird die Tatsache ausgenutzt, dass di orthogonal zur Matrix und zum vorherigen Vektor xi ist. di wird dafür entlang der Richtungsableitung minimiert 6 3.3. Algorithmus [12]: 0= d f (xi+1 ) dα 0 = f 0 (xi+1 )T d xi+1 dα 0 = −(ri+1 )T di 0 = dTi ri+1 αi = − dTi xi dTi di (9) Zur Berechnung der Vektoren di kann nun das Verfahren nach Gram-Schmidt verwendet werden. Jedoch ist dies nicht die effizienteste Variante zur Bestimmung der orthogonalen Vektoren, da bei Gram-Schmidt sämtliche vorherigen Vektoren d1 , d2 , . . . , di−1 zur Berechnung des Vektors di benötigt und somit abgespeichert werden müssen [12, S. 25]. Stattdessen werden für das CG-Verfahren die Richtungen di aus den Residuen der Matrix ri berechnet. Das Residuum ri (Gleichung 10) ist orthogonal zu den vorherigen Residuen r1 , r2 , . . . , ri−1 , und es kann direkt aus der Matrix und dem vorhergehenden Residuum berechnet werden [12, S. 30]. ri = b − Axi (10) Nach Skalierung von di für di+1 über β kann dies wie folgt zusammengefasst werden [12, S. 32]: d0 = r0 = b − Ax0 (11) riT ri dTi Adi (12) xi+1 = xi + αi di (13) ri+1 = ri − αi Adi (14) αi = − βi+1 = T r ri+1 i+1 riT ri di+1 = ri+1 + βi+1 di (15) (16) 3.3. Algorithmus Das in Kapitel 3.2 vorgestellte CG-Verfahren ist sehr effizient bezüglich Laufzeit und Speicherverwendung (siehe Kapitel 3.5). Daher ist es ein häufig implementiertes Verfahren. Der Pseudocode dazu ist in Abbildung 2 zu sehen. Im Folgenden werden sämtliche Variablen, die einem Code-Stück entnommen sind, in Schreibmaschinenschrift dargestellt. Variablen, die aus Gleichungen entnommen wurden, sind in kursiv dargestellt. Dort ist die Hauptschleife sowie die Berechnung der Startwerte dargestellt. Aufgrund der fehlenden mathematischen Symbole in einem Quelltext wurden die Variablen ausgeschrieben (bspw. alpha für α). h·, ·i steht für das Skalarprodukt zweier Vektoren x und 7 3. Lösen eines linearen Gleichungssystems 1 x = x_0 S t a r t v e k t o r 2 R = r = b − Ax , rho = <r , r >, b e t a = 0 , p = 0 3 f o r k = 1 t o MAX_iter and | | r | | > e p s ∗ e p s ∗ | | R | | do 4 p = r + b e t a ∗p 5 q = A∗p 6 a l p h a = rho/<rho , q> 7 x = x + a l p h a ∗p 8 r = r − a l p h a ∗q 9 rho_old = rho , rho = <r , r> 10 b e t a = rho / rho_old 11 end Abbildung 2.: Pseudo-Code des Verfahren der konjugierten Gradienten [9, S. 146] P y, definiert durch N i=1 xi yi , und ||·|| bezeichnet die Eulersche 2-Norm || · ||2 . Damit ist weitestgehend eine direkte Zuordnung zwischen der Variablen und dem Algorithmus in Kapitel 3.2.2 möglich. ρ gibt das Skalarprodukt von r mit sich selbst an; q enthält das Produkt von Matrix A und Vektor p. p entspricht dem Vektor d in den Gleichungen 11 bis 16. Zu den Variablen aus Kapitel 3.2.2 kommt noch R hinzu; dies wird für die Abbruchbedingung in Zeile 3 benötigt. R enthält das Residuum des Startvektors und spiegelt den anfänglichen Gradienten wider. Der Algorithmus bricht unter anderem ab, sobald eine gewisse Genauigkeit ||R||2 (vgl. Zeile 3) erreicht wird [9]. bezeichnet dabei den gewünschten maximalen Fehler. Der maximale Fehler wird aus den Gradienten des Startvektors und des Ergebnisvektors berechnet. Dies lässt sich wie folgt veranschaulichen: das Verfahren bricht dann ab, wenn die Änderung (also die Steigung) an der Lösung eine Grenze unterschreiten. Der andere Teil der Abbruchbedingung besteht aus einer oberen Grenze MAX_iter für die Anzahl an Iterationen; diese beträgt in dieser Arbeit zehn. Der Grund dafür ist die Ungenauigkeit der Maschinenberechnungen, da sich mit diesen nur gerundete Ergebnisse erzielen lassen [5, 12]. 3.4. Implementierung Sämtliche Programme und Algorithmen sind, sofern nicht anders angegeben, in der Hochsprache C verfasst, und mit dem C89-Standard kompiliert (siehe Kapitel 9.1). Dies bedingt eine wichtige, umfassende Besonderheit bei der Implementierung: da im C-Standard die Datentypen Matrix und Vektor nicht definiert wurden, musste eine eigene Implementierung der Datentypen und der notwendigen Operationen benutzt werden. 3.4.1. Datentyp Vektor Als Ersatz für den fehlenden Standard wurde eine Array(/Feld)-Implementierung des Datentyps double (Abbildung 3) gewählt. Für die Matrix kam ein mehrdimensionales 8 3.4. Implementierung Array zum Einsatz. Die Gründe dafür sind die einfache Definition und Deklaration, sowie die simple Verwendung mit einem hohen Grad an Kontrolle, gerade bei der operationellen Verarbeitung. Zudem ist auch ein sehr geringer Speicheroverhead durch die Verwendung von ’Call by Reference’ möglich. Nach der Definition der Matrix (Zeile 1) und des Vektors (Zeile 2) mit den Dimensionen N xN und N ist noch der Speicher dafür zu reservieren und mit einem definierten Startwert zu initialisieren (Zeile 9-13). Dies geschieht mit Hilfe von malloc (Zeile 5-7). Da die Matrix ein mehrdimensionales Array ist, wird zuerst eine Zeile reserviert, und anschließend ausgehend von dem jeweiligen ersten Element die zugehörige Spalte. Nach den Berechnungen wird der allozierte Speicher in umgekehrter Reihenfolge wieder freigegeben (Zeile 16-18). 3.4.2. Matrix- und Vektoroperationen Im CG-Verfahren wurden vier Operationen angewendet, die auf Matrizen oder Vektoren arbeiten: die Matrix-Vektor-Multiplikation Av, die Vektor-Skalar-Multiplikation vc, das Skalar-Produkt h·, ·i und die Eulersche Norm || · ||. Diese wurden in eigenen Funktionen implementiert, die alle dem gleichen Schema folgen: zuerst werden die beiden Operanden als Parameter übergeben. Zusätzlich wird, wenn das Ergebnis ein Vektor ist, ein solcher 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /∗ D e f i n i t i o n e i n e r Matrix a l s z w e i d i m e n s i o n a l e s Array ∗/ double ∗∗A; /∗ D e f i n i t i o n e i n e s V e k t o r s a l s Array ∗/ double ∗v ; /∗ S p e i c h e r a l l o k a t i o n ∗/ A = m a l l o c ( s i z e o f ( double ∗ ) ∗ N ) ; f o r ( i = 0 ; i < N; i ++) A[ i ] = m a l l o c ( s i z e o f ( double ) ∗ N ) ; b = m a l l o c ( s i z e o f ( double ) ∗ N ) ; /∗ W e r t i n i t i a l i s i e r u n g mit 0 ∗/ f o r ( i = 0 ; i < N; i ++) { v [ i ] = 0; f o r ( j = 0 ; j < N; j ++) A[ i ] [ j ] = 0 ; } /∗ S p e i c h e r f r e i g a b e ∗/ f o r ( i = 0 ; i < N; i ++) f r e e (A[ i ] ) ; f r e e (A ) ; free (v ); Abbildung 3.: Implementierung der Datentypen Matrix und Vektor als Array(s) 9 3. Lösen eines linearen Gleichungssystems übergeben, um das Ergebnis darin zu speichern. Sollte das Ergebnis kein Vektor sein, wird eine Variable angelegt und diese am Ende zurückgegeben. Nachdem eine Ergebnisvariable festgelegt wurde, wird diese mit Null initialisiert. Dann wird das Ergebnis berechnet und in der Ergebnisvariable abgespeichert bzw. zurückgegeben (vgl. Anhang A.2). 3.5. Betrachtung der Effizienz Das CG-Verfahren ist einer der effizientesten Algorithmen zur Lösung von linearen Gleichungssystemen [8, S. 101]. Dazu tragen vor allem der geringe Speicherverbrauch sowie die geringe Laufzeit bei. Wie schon in Kapitel 3.2.2 angedeutet, ist es beim CG-Verfahren nicht notwendig, sämtliche vorhergehenden Vektoren zur Berechnung des nächsten linear unabhängigen Vektors abzuspeichern. Damit verbleiben nur die Matrix, der Vektor der rechten Seite, sowie der Lösungsvektor, die während der gesamten Berechnung vorzuhalten sind. Aus Gründen der geringeren Laufzeit kann auch das Matrixprodukt (vgl. Abbildung 2, Zeile 5) zusätzlich abgespeichert werden, was den zu speichernden Daten einen weiteren Vektor hinzufügt [12][8, S. 107]. Bezüglich der Laufzeit ist das CG-Verfahren - wie schon oben angedeutet - sehr effizient: in jeder Iteration wird durch die Orthogonalität der Summanden eine Komponente des Lösungsvektors minimiert, sodass nach spätestens N Schritten der mathematische Algorithmus garantiert terminiert und die mathematisch exakte Lösung liefert. Bei einer symmetrischen Lösung terminiert das Verfahren schon nach dN e Schritten, wenn eine passende obere Schranke für den Fehler eingebaut wurde [12, 8]. Daraus folgt, dass das CG-Verfahren bei großen Dimensionen ein rechenintensives Verfahren ist. 10 4. Error Correction Code Eine Möglichkeit zur Behandlung von Soft Errors besteht in der Verwendung eines Error Correction Codes (zu deutsch: fehlerkorrigierender Code, kurz: ECC). Diese aus der digitalen Codierungstheorie stammende Technik basiert auf dem Hinzufügen von Redundanzinformationen, um daraus in Kombination mit den - im Falle eines Soft Errors fehlerhaften Informationen das Original wiederherzustellen [10]. 4.1. Realisierung der ECC Der einfachste Ansatz ist das Hinzufügen eines Paritätsbits. Dabei werden immer zwei benachbarte Bits mit einem zusätzlichen Bit versehen, welches den Zustand beider Bits widerspiegelt: bspw. kann dem Paritätsbit eine 0 zugewiesen werden, wenn die beiden benachbarten Bits gleich sind, ansonsten eine 1. Somit ist die Anzahl an Einsen in diesen drei Bits immer gerade; daher kann im Falle eines Soft Errors in einem der benachbarten Bits dieser durch das Paritätsbit erkannt werden [10]. Leider ist es nicht möglich, aus einem Paritätsbit den vorherigen Zustand widerherzustellen, da nicht ersichtlich ist, welches der beiden Bits den Soft Error erfahren hat. Daher werden andere Verfahren verwendet, in denen sieben oder acht Bits zur Korrektur von 32 oder 64 Bit großen Blöcken benutzt werden [11]. Dafür werden beim Schreiben in den Speicher die zugehörigen Bits von einem zusätzlichen Controller mit dem ECC-Code versehen. Wird aus einem ECC-geschützten Speicher gelesen, wird aus den gelesenen Daten erneut ein ECC-Code errechnet, welcher mit dem ursprünglichen ECC-Code verglichen wird. Ist ein einzelner Soft Error aufgetreten, wird dies in dem Vergleich erkannt und durch den Controller korrigiert [11]. 4.2. Betrachtung der Effizienz Damit sind mit ECC geschützte Speicher in der Lage, mindestens einen Soft Error zu korrigieren; es gibt auch Implementierungen, die mehr als einen Fehler entdecken und korrigieren können [6, S. 134]. Allerdings bietet das keinen umfassenden Schutz; vor allem Register und Ausführungseinheiten besitzen meist keine ECC-Implementierung und sind somit anfällig für Soft Errors [9, 6, S. 134]. Daher ist es sinnvoll, den Schutz vor Soft Errors auf eine weitere Ebene, der Software-Ebene, auszuweiten [9]. 11 5. Programmflusskontrolle Im Gegensatz zum teils sehr kostenintensiven ECC-Schutz, sollen nun zwei verschiedene Behandlungsmöglichkeiten auf Seiten der Software betrachtet werden: zum Einen die Programmflusskontrolle, abgeleitet vom defensiven Programmieren (Defensive Programming), zum Anderen die Numerische Fehlerkorrektur (siehe Kapitel 6). 5.1. Defensive Programming Defensive Programming (zu deutsch in etwa: schützender Programmierstil), nach Firesmith ([2]) auch ein Teil des Defensive Development (zu deutsch soviel wie: schützende Entwicklung), bezeichnet eine Art und Weise des Programmierens, in der die Implementierung sich selbst vor unsachgemäßem Gebrauch oder Fehlern schützt. Um das zu erreichen, werden vor der Ausführung der Befehle eine große Anzahl an Voraussetzungen überprüft. Dazu werden meist Assertions (zu deutsch: Zusicherungen, Sicherstellungen) verwendet [2]. In Abbildung 4 ist beispielhaft eine Assertion im Quellcode einer Funktion zur Be1 f l o a t d i v i d e ( f l o a t a , int b ) { 2 A s s e r t i o n ( "B n i c h t n u l l " ) ; 3 return a / b ; 4 } Abbildung 4.: Beispiel einer Assertion in einer Funktion zur Division rechnung der Division dargesellt. Für den Fall, dass der Divisor Null beträgt, muss die Division anders behandelt werden, da hier der Quotient nicht definiert ist. Dieser Fall wird in Zeile 2 überprüft. 5.2. Anwendung zur Korrektur von Soft Errors Da es wünschenswert ist, die Soft Errors gerade im Bereich des Ausführungspfades eines Programms zu verhindern oder zu korrigieren, wird die defensive Programmierung auf Kontrollstrukturen angewendet. Das wird im Folgenden als Programmflusskontrolle bezeichnet. Dazu wird die Abfrage-Bedingung der for-Schleife mit Assertions überprüft. Hierbei handelt es sich um sogenannte Postconditions, also Assertions, die nach erfolgreicher Berechnung erfüllt sein müssen [2, S. 260]. Um dies beim CG-Verfahren anwenden zu können, muss die Kontrollstruktur for modifiziert werden. In Abbildung 5 ist eine einfache for-Schleife dargestellt, die in jeder Iteration eine Berechnung durchführt und anschließend die Laufvariable um den Wert 1 inkrementiert. Dies wird solange durchgeführt, bis die maximale Anzahl MAX an Iterationen erreicht ist. 13 5. Programmflusskontrolle Um die Ausführung dieser Kontrollstruktur bei einem Soft Error zu korrigieren, müssen 1 i = 0; 2 3 while ( 1 ) { 4 f o r ( ; i < MAX; i ++) { 5 Assertion ( " Letzte Schrittweite korrekt ?" ) ; 6 // Ggf . R e s e t d e r Parameter und d e r S c h l e i f e 7 Assertion ( " S o l l t e S c h l e i f e beendet s e i n ?" ) ; 8 // Ggf . Beendigung d e r S c h l e i f e 9 [ Berechne ] 10 } 11 Assertion ( " Letzte Sc h r i t t w e i t e korrekt ?" ) ; 12 // Ggf . R e s e t d e r Parameter und d e r S c h l e i f e 13 A s s e r t i o n ( " Abbruchbedingung f o r −S c h l e i f e k o r r e k t ? " ) ; 14 // Ggf . R e s e t d e r Parameter und d e r S c h l e i f e 15 } Abbildung 5.: Kontrollstruktur for nach der Anwendung der Programmflusskontrolle zwei wichtige Punkte überprüft werden: erstens, die Abbruchbedingung, und zweitens, die Schrittweite. Dazu werden die Assertions an geeigneten Punkten eingefügt und eine einschließende Schleife gelegt, die im Bedarfsfall ein Zurücksetzen der Schleife oder die Wiederholung einer oder mehrerer Iterationen veranlasst. Dies ist in Abbildung 5 realisiert. Sollte ein Soft Error in der Berechnung oder der Auswertung der Abbruchbedingung auftreten, sodass diese zum Verlassen der for-Schleife führt, wird die Abbruchbedingung erneut ausgewertet und dementsprechend agiert: wird die Abbruchbedingung immer noch eingehalten, so wird die for-Schleife erneut durchlaufen (ggf. mit Zurücksetzen der Parameter aus Backup-Variablen); ist dies nicht der Fall, kann mit dem weiteren Programm fortgefahren werden. Ähnlich verhält es sich bei der Überprüfung der Schrittweite, auch wenn diese an zwei Punkten überprüft werden muss (Zeile 6 und 9). Wird ein Soft Error an diesen Stellen durch eine veränderte Schrittweite ersichtlich, so werden die Parameter sowie die Schleife zurückgesetzt; tritt kein Soft Error auf, ist keine Beeinflussung des Programms vorhanden. 5.3. Implementierung Um den Einfluss eines Soft Errors auf die Steuerungsvariablen wie die Laufvariable oder die Schrittweite möglichst gering zu halten, werden diese mit dem Schlüsselwort volatile versehen. Dies erzwingt das Laden des Variableninhalts aus dem Speicher bei jedem Zugriff sowie das sofortige Zurückschreiben der Variable nach Beendigung der Berechnung [5, S. 13ff]. Dadurch wird ein Auftreten eines Soft Errors auf die Zeit der Berechnung beschränkt. 14 5.4. Betrachtung der Effizienz Zudem kann die Implementierung der Programmflusskontrolle in Bezug auf das CGVerfahren noch optimiert werden. Dabei steht in dieser Arbeit ein geringer Overhead (vgl. Kapitel 5.4) im Fokus. Ein Schritt zur Optimierung besteht in der Verringerung der Backup-Variablen. Da der Algorithmus des CG-Verfahrens in jeder Iteration acht Variablen berechnet (vgl. Kapitel 3.3), und auch die Laufvariable berücksichtigt werden muss, müssten bei naiver Herangehensweise die Zwischenzustände aller neun Variablen gespeichert werden, um ein Zurücksetzen zu ermöglichen. Jedoch kann die Zahl der Backup-Variablen auf ein Backup der Laufvariablen reduziert werden. Der Grund dafür ist die Funktionsweise des CG-Algorithmus: da in jedem Schritt Berechnungen unabhängig von der Laufvariablen getätigt werden und es hier auf die Anzahl der Berechnungen ankommt, kann diese Reduzierung durchgeführt werden. Zusätzlich sind die Assertions so entworfen, dass diese unnötige Berechnungsiterationen 1 while ( 1 ) { 2 f o r ( ; Bedingung ; Inkrement ) { 3 Berechne V a r i a b l e n 4 } 5 i f ( Bedingung ) { 6 Reset d e r L a u f v a r i a b l e n 7 } 8 break ; 9 } Abbildung 6.: Verhinderung unnötiger Iterationen durch die Kombination if-break verhindern (siehe Abbildung 6). Angenommen, es tritt ein Soft Error in einer Kontrollstruktur während der Ausführung auf, so kann dieser entweder in der Bedingung der for-Schleife (Zeile 2) oder in der Bedingung der if-Abfrage (Zeile 5) auftreten. Im ersten Fall wird die Assertion diesen Soft Error erkennen und die berechnende Schleife zurücksetzen. Im zweiten Fall wird die Bedingung der berechnenden Schleife erreicht, diese wird mit false evaluiert und die Schleife verlassen. Danach evaluiert die Assertion korrekt mit true und das Programm wird fortgesetzt. 5.4. Betrachtung der Effizienz Wie schon im vorigen Kapitel (Kapitel 5.3) aufgezeigt, ist die Programmflusskontrolle in Bezug auf das CG-Verfahren eine Möglichkeit zur Korrektur von Soft Errors in Kontrollstrukturen mit geringem Overhead. Die Hauptgründe dafür sind der geringe zusätzliche Speicheraufwand, welcher sich auf ein Backup der Laufvariablen beschränkt, und die Verhinderung von unnötigen Iterationen bei der Berechnung. Damit ist die Programmflusskontrolle für das CG-Verfahren gut geeignet. 15 5. Programmflusskontrolle Bei der Übertragung der Programmflusskontrolle auf andere Algorithmen ist es wahrscheinlich, dass die gleiche Effizienz wie beim CG-Verfahren nicht mehr erreicht wird. Auch wenn der Laufzeit-Overhead ähnlich gering sein wird, ist der Speicheraufwand möglicherweise ein vielfaches größer. Ein einfaches Gegenbeispiel findet sich in Abbildung 7. Dort ist eine Funktion zur Berechnung der Summe der Zahlen von eins bis zu einem 1 int sum = 0 ; 2 f o r ( int i = 0 ; i < MAX; i ++) { 3 sum += i ; 4 } Abbildung 7.: Algorithmus zur Summe der Zahlen von 1 bis MAX Höchstwert MAX abgebildet. Sollte man nun die Programmflusskontrolle auf diesen Algorithmus anwenden, so müsste man nicht nur ein Backup der Laufvariablen erzeugen, sondern auch ein Backup der Summe, um so den vorhergehenden Wert wiederherstellen zu können. Damit eignet sich die Programmflusskontrolle für diesen Algorithmus nicht, da der doppelte Speicherplatz benötigt wird. Daraus folgt, dass die Programmflusskontrolle bei nur rechenintensiven Anwendungen geeignet ist. Bei speicherintensiven Anwendungen ist das nicht der Fall, sofern sich diese nicht - ähnlich wie das CG-Verfahren - optimieren lassen. 16 6. Numerische Fehlerkorrektur Anstelle der Programmflusskontrolle kann auch die Numerische Fehlerkorrektur verwenden werden. Diese adressiert im Gegensatz zur Programmflusskontrolle nicht die korrekte Ausführung der einzelnen Pfade, sondern die Korrektheit der Lösung als Ganzes. Das bedeutet, dass ein Soft Error sowohl in den Kontrollstrukturen als auch den Datenvariablen auftreten könnte. Bei der numerischen Fehlerkorrektur handelt es sich um eine mathematische Transformation des Ausgangsproblems zu einem Defekt-Problem: anstatt die Ausgangsgleichung direkt zu lösen, wird der Lösungsvektor durch einen fehlerbehafteten Vektor ersetzt [9]. Ax = b (17) A(x + d) = b (18) Ad = b − Ax (19) Ad = r (20) Dabei bezeichnet d den angenommenen Fehler. Die Gleichung (20) wird nun mit dem beschriebenen CG-Verfahren gelöst und das Ergebnis zur Korrektur des Lösungsvektors benutzt. Anschließend wird die Genauigkeitsbedingung überprüft und die nächste Iteration gestartet [9, S. 147f]. Dabei ist anzumerken, dass das CG-Verfahren nicht nur zur Verdeutlichung dient, sondern direkt ein Teils des Verfahrens ist. 6.1. Algorithmus 1 2 3 4 5 6 7 x = x_0 R = r = b − while | | r | | Setze A ∗ x = x + d r = b − A end while A ∗ x > e p s ∗ | | R | | do d = r i n CG−V e r f a h r e n e i n ∗ x Abbildung 8.: Pseudo-Code der numerischen Fehlerkorrektur (vgl. [9, S. 148]) Abbildung 8 stellt den Pseudo-Code der numerischen Fehlerkorrektur dar. Während in Zeile 1 und 2 die aus dem CG-Verfahren bekannten Startbedingungen berechnet werden, ist in den darauf folgenden Zeilen die einschließende Schleife dargestellt. Die Bedingung für die gewünschte Genauigkeit wurde eins zu eins übernommen; direkt darauf erfolgt der Aufruf des Lösungsalgorithmus mit dem fehlerbehafteten Vektor d. Nach erfolgreichem Abschluss wird der Lösungsvektor um das Ergebnis korrigiert und das Residuum für den 17 6. Numerische Fehlerkorrektur nächsten Schritt berechnet. So wird um das CG-Verfahren eine einschließende Schleife gelegt, die die gleiche Bedingung (Genauigkeitsbedingung) wie das originale CG-Verfahren besitzt. Die einzige Ausnahme bildet die untere Schranke , die bei der numerischen Fehlerkorrekture nicht quadratisch afutritt. Somit wird, wenn kein Soft Error auftritt, auch keine weitere Iteration benötigt. Im Falle eines Soft Errors im CG-Verfahren ist die gewünschte Genauigkeit nicht mehr erreicht, und das Verfahren mit dem aktualisierten Residuum erneut gestartet [9]. Dabei wird keine Unterscheidung über das Auftreten eines Soft Errors gemacht, solange dieser innerhalb des CG-Verfahrens auftritt. Damit besteht die Möglichkeit, dass das CG-Verfahren durch einen Soft Error sehr viel länger zur Terminierung benötigt, oder die Terminierung überhaupt nicht mehr eintritt [9, S. 148]. 6.2. Betrachtung der Effizienz Durch die Verwendung einer einfachen, äußeren Schleife für die numerische Fehlerkorrektur wird der Speicheraufwand reduziert: so sind zusätzlich zu den Variablen des CGVerfahrens nur der fehlerbehaftete Vektor d, die Residuen r und R sowie der Lösungsvektor x abzuspeichern. Bei effizienter Programmierung ist es möglich, die zusätzlichen Variablen von vier auf eine zu senken; dies wird durch Wiederverwendung der Residuen und des fehlerbehafteten Vektors im CG-Verfahren ermöglicht. Zusätzlich ist, wie schon im vorangegangenen Kapitel 6.1 angedeutet, diese Implementierung der numerischen Fehlerkorrektur berechnungseffizient: im Falle eines Soft Errors wird, ausgehend vom letzten Ergebnis, die Berechnung erneut gestartet. Somit gehen mögliche - bis zu diesem Zeitpunkt erzielte - Fortschritte nicht verloren. Im Normalfall, also ohne ein Auftreten eines Soft Errors, besteht der zusätzliche Berechnungsaufwand nur aus dem einmaligen Überprüfen der Genauigkeitsbedingung; hier ist also auch nur ein sehr geringer Overhead vorhanden [9, S. 148]. 18 7. Software-Tests Mit dem Einsatz von Software in sehr vielfältigen und unterschiedlichen Gebieten steigt die Notwendigkeit einer zuverlässigen, robusten und nach Möglichkeit fehlerfreien Implementierung. Dies führt zur Einführung eines Teilgebiets des Software Engineering, dem Testen von Software. Durch die vielen Faktoren, die Software beeinflussen können, wird der Entwurf einer vollständig fehlerfreien Software nahezu unmöglich. Daher sollte es beim Testen nicht das Ziel sein, die korrekte Funktionsweise eines Programms oder eines Algorithmus, sondern die Abwesenheit von Fehlern zu zeigen. Das ist gerade bei der Verwendung von Software in kritischen Prozessen, in denen Menschen involviert sind und zu Schaden kommen können, äußerst wichtig [7]. Bei dem Design von Tests werden hauptsächlich zwei Arten des Testens unterschieden: das Black-Box-Testing und das White-Box-Testing [1]. Bei Ersterem wird die zu testende Funktion, das Programm/Modul oder das System als schwarze Box behandelt, deren genaue Funktionsweise unbekannt ist. Hier werden die Testfälle enstprechend der Programmspezifikation entworfen, dementsprechend Eingaben durchgeführt und die Ergebnisse mit den Erwartungen verglichen. Im Gegensatz dazu steht das White-Box-Testing, bei dem eine andere Herangehensweise verwendet wird: die Tests werden anhand des konkreten Quellcodes entworfen, um bestimmte Eigenschaften zu überprüfen (siehe Kapitel 7.2). Bei beiden gilt: je höher die jeweilige Überdeckung, desto größer ist die Qualität. Während beim Black-Box-Testing die Überdeckung der Eingabe gemeint ist, handelt es sich beim White-Box-Testing um eine möglichst hohe Ausführungsrate der Programmpfade mit einem gebenen Kriterium, die erzielt werden soll [7]. Es wird angenommen, dass eine Kombination aus beiden Varianten optimal ist [7]. 7.1. Äquivalenzklassen Angenommen, es sei ein Algorithmus zu testen, der einen Soft Error in der Addition zweier Zahlen bitgenau entdecken soll (Abbildung 9). Dies generiert schon zahlreiche Testfälle, die untersucht werden müssten. Der maximale Zahlenbereich der Variablen a 1 short int a = 5 , int b = 7 , int c = 0 ; 2 c = a + b ; /∗ S o f t Error−I n j e k t i o n i n d i e s e A d d i t i o n ∗/ 3 [ Entdecke S o f t E r r o r ] Abbildung 9.: Addition zweier Zahlen, in die ein Soft Error injiziiert werden soll. und b erstreckt sich von −215 − 1 bis 215 ; der benötigte Speicherbereich beträgt damit 32 Bit pro Zahl [5, S. 22]. Für einen vollständigen Test müsste in jedes Bit beider Zahlen jeweils einmal ein Soft Error injiziert werden; die Gesamtzahl der Testfälle betrüge bei dieser einfachen Addition 64. 19 7. Software-Tests Damit umfassende und umfangreiche Software-Tests von komplexen Programmen realisierbar bleiben, werden für die Variation der Eingabe Äquivalenzklassen benutzt. Äquivalenzklassen bieten die Möglichkeit, die Eingabemenge zu partitionieren, sodass sich durch die Auswahl eines Vertreters für jede Partition die Menge der Testfälle reduziert. Alle Elemente einer Partition besitzen die gleichen Eigenschaften und erzeugen daher auch die gleiche Ausgabe. Das bedeutet, dass ein Auftreten eines Fehlers bei einem Element einer Partition den gleichen Fehler bei allen anderen Elementen derselben Partition impliziert [1, S 67f]. Bei dem in Abbildung 9 dargestellten Beispiel bietet es sich an, dieses Verfahren anzuwenden. Da das Erkennungsverfahren bitgenau arbeitet, macht es keinen Unterschied, in welches Bit einer Variable der Soft Error injiziert wird. Somit ist es möglich, alle Bits einer Variable in einer Partition zusammenzufassen. Daraus resultieren dann zwei Partitionen (jeweils eine für die Variablen a und b), aus denen jeweils ein Vertreter ausgewählt wird. Aufgrund dessen verbleiben von den ursprünglich 64 verschiedenen Testfällen nur noch 2, was eine erhebliche Verbesserung bedeutet. 7.2. Mehrfachbedingungsüberdeckung Eine der Möglichkeiten, das White-Box-Testing durchzuführen, besteht in der Verwendung der Pfadüberdeckung. Dafür werden die Testfälle so entworfen, dass sämtliche Ausführungspfade des Programm(-teils) mindestens einmal durchlaufen werden. Allerdings kann durch das Kriterium der Pfadüberdeckung durch die exponentiell hohe Anzahl an Pfaden eine sehr große Menge an Testfällen generiert werden, weshalb eine Variante verwendet wird: die Bedingungsüberdeckung [7]. Bei der Bedingungsüberdeckung wird jede mögliche Kombination aus Eingaben in einer Bedingung verwendet, um alle möglichen Ergebnisse der Abfrage hervorzurufen und damit sämtliche Pfade mindestens einmal zu durchlaufen. Ist mehr als eine Bedingung in der Abfrage enthalten, wird dies Mehrfachbedingungsüberdeckung genannt [1]. Zur Ver1 if (x > 0 | | y > 0 ) { 2 [ Berechne ] 3 } Abbildung 10.: Einfache if-Abfrage mit zwei Bedingungen deutlichung ist in Abbildung 10 eine if-Abfrage mit zwei Teilbedingungen zu finden: x > 0 und y > 0. Um hier eine vollständige Mehrfachbedingungsüberdeckung zu erreichen, sind vier Testfälle notwendig: 1. x > 0 und y > 0 2. x > 0 und y <= 0 3. x <= 0 und y > 0 20 7.2. Mehrfachbedingungsüberdeckung 4. x <= 0 und y <= 0 Für die vier Fälle können entsprechend ihrer Definition die korrekten Werte bestimmt werden, um die jeweiligen Anforderungen zu erfüllen. 21 8. Implementierung eines Tests unter FITIn Um mittels FITIn einen Soft Error in ein Programm zu injizieren, sind zwei Schritte notwendig: als Erstes die Annotierung der Variable, in der die Soft Error-Injektion erfolgen soll; als zweites die Steuerung des Werkzeugs FITIn durch ein Lua-Skript, in dem der genaue Zeitpunkt der Manipulation festgelegt werden kann. 8.1. Annotation des Quellcodes Für den ersten Schritt ist demnach der Quellcode des zu testenden Programms notwendig; die Annotierung erfolgt über Makros, welche durch Einbinden des FITIn-Headers zur Verfügung gestellt werden [4, S. 65]. In Abbildung 11 ist eine beispielhafte Annotation 1 #include <s t d i o . h> 2 #include " f i _ c l i e n t . h" 3 4 int main ( void ) { 5 int a = 0 ; 6 FITIN_MONITOR_VARIABLE ( a ) ; 7 a = a + 1; 8 FITIN_UNMONITOR_VARIABLE ( a ) ; 9 p r i n t f ( "The v a l u e o f a i s %d\n" , a ) ; 10 return 0 ; 11 } Abbildung 11.: Quellcode mit beispielhafter Annotation eines Quellcodes durchgeführt worden: während in Zeile 2 nur die in FITIn integrierte Header-Datei eingebunden wird, wird in den Zeilen 5 und 7 die eigentliche Annotierung durchgeführt. Die beiden Makros FITIN_MONITOR_VARIABLE und FITIN_UNMONITOR_VARIABLE sorgen für die korrekte Erkennung der Variablenzugriffe in dem durch die Makros eingeschlossenen Intervall. Das Makro FITIN_MONITOR_VARIABLE (a) wird von FITIn dazu verwendet, die Adresse des zugehörigen Speicherbereichs an Valgrind weiterzuleiten und Callbacks dafür zu registrieren; diese werden dann durch FITIn verarbeitet (vgl. Kapitel 8.2). Im Gegensatz dazu wird FITIn durch das Makro FITIN_MONTIOR_VARIABLE (a) dazu angehalten, ab diesem Zeitpunkt die Lesezugriffe der Variablen a nicht mehr zu verarbeiten [4, S. 10ff]. 23 8. Implementierung eines Tests unter FITIn 8.2. Steuerung mittels eines Lua-Skriptes Der zweite Schritt erfordert die Erstellung eines sogenannten Kontroll-Skriptes, welches in der Skript-Sprache Lua zu verfassen ist. In Abbildung 12 ist ein passendes Kontroll-Skript für das in Abbildung 11 enthaltene Programm dargestellt. FITIn ruft bei einem erkann1 t r e a t _ s u p e r b l o c k = f u n c t i o n ( a d d r e s s , fnname , 2 f i l e n a m e , dirname , linenum ) 3 return fnname == "main" 4 end 5 6 monitor_address = f u n c t i o n ( a d d r e s s , a n n o t a t e d ) 7 return annoted 8 end 9 10 f l i p _ v a l u e = f u n c t i o n ( s t a t e , a d d r e s s , c o u n t e r , s i z e ) 11 i f ( c o u n t e r == 1 ) then 12 return {32} 13 else 14 return {} 15 end 16 end Abbildung 12.: Ein beispielhaftes Kontroll-Skript in Lua zur Steuerung von FITIn ten Lesezugriff auf einen Speicherbereich verschiedene Lua-Callbacks auf, die wichtigsten sind in Abbildung 12 zu finden: treat_superblock, monitor_address und flip_value. Der Callback treat_superblock entscheidet über den weiteren Aufruf der anderen Callbacks im Falle eines lesenden Variablenzugriffs. Da FITIn sämtliche Variablenzugriffe registriert, ist es hiermit möglich, nur Variablenzugriffe eines bestimmten Blocks, beispielsweise einer Funktion oder einer Datei, zu selektieren und weiter zu verarbeiten. Dies dient vor allem der Performance; sollten alle Variablenzugriffe verfolgt werden, ist ein enormer zusätzlicher Speicher- und Rechenaufwand notwendig. In diesem Beispiel weist das Skript FITIn an, alle Variablenzugriffe innerhalb der main-Funktion zu behandeln. Da es selbst innerhalb eines Blocks zu mehrfachen, verschiedenen Variablenzugriffen kommt, ist es mittels des Callbacks monitor_address möglich, zu entscheiden, wie die einzelnen Zugriffe behandelt werden sollen. Hier wird bei vorhandener Annotation der Variable im Quellcode, welche durch den Wahrheitswert im Parameter annotated angegeben ist, die Manipulation ermöglicht. Die aufgeführte Manipulation wird durch das letzte Callback flip_value durchgeführt. Dieser wird nur aufgerufen, wenn die vorhergehenden Callbacks eine Verarbeitung der Variable erlauben, i.e. wenn die Variable in einem erlaubten Block enthalten ist und eine Annotation im Quellcode erfahren hat. Sind diese Bedingungen erfüllt, kann über die Zurückgabe einer positiven Ganzzahl eine Bitmaske definiert werden, nach der der Bitflip des Soft Errors erfolgen soll. Zur kon- 24 8.3. Injektion eines Soft Errors in Kontrollstrukturen trollierten Manipulation stehen verschiedene Parameter zur Verfügung, die wichtigsten sind address und counter. Während address die Adresse des erkannten Speicherzugriffs enthält, anhand derer zwischen verschiedenen Variablen unterschieden werden kann, ist in counter die Iteration des Zugriffs enthalten. Da FITIn jeden Zugriff auf die Variable registriert und hierbei immer einen Zähler inkrementiert, ist es dadurch möglich, den Zeitpunkt der Soft Error-Injektion auf die Operation genau festzulegen. In dem gezeigten Beispiel (vgl. Abbildung 11) wird in dem durch die Makros eingeschlossenen Bereich genau einmal lesend auf die Variable a zugegriffen (Zeile 6: a = a + 1). Der Callback führt hier beim Lesen der Variable vor der Berechnung von a + 1 die Injektion auf das Bit mit dem Wert 32 durch (also das sechstniedrigste Bit: 25 ), was zu einer Änderung der Variable für die Berechnung von dem Wert 0 zum Wert 32 führt und dadurch die Berechnung das Ergebnis 33 liefert. Das Ergebnis wird nun wieder in der Variable a abgespeichert; dies ruft keine erneute Behandlung durch FITIn hervor. 8.3. Injektion eines Soft Errors in Kontrollstrukturen Um die angestrebte Injektion eines Soft Errors in die Abfrage-Bedingung einer Kontrollstruktur zu erreichen, muss diese ab einer gewissen Komplexität aus der Kontrollstruktur extrahiert werden. Im ersten Beispiel (Abbildung 13) ist die Kontrollstruktur if mit einer simplen Bedin1 i f ( x < 0) { [ . . . ] } Abbildung 13.: Einfache if-Abfrage als Beispiel für eine Kontrollstruktur gung dargestellt; es ist eine einzelne Variable x beteiligt, welche eine einzelne Bedingung zu erfüllen hat (x < 0). Zur erfolgreichen Manipulation reicht es, wenn in x das höchstwertigste Bit (engl: Most Significant Bit, kurz: MSB) durch einen Soft Error negiert wird. Da das MSB bei einer vorzeichenbehafteten, ganzen Zahl das Vorzeichen eindeutig repräsentiert, wird bei einem Soft Error im MSB das Vorzeichen der Variable negiert und somit der Ausgang der Abfrage umgekehrt. Im Gegensatz dazu ist beim zweiten Beispiel (Abbildung 14) diese einfache Steuerung 1 i f ( x < 10 && y > −10) { [ . . . ] } Abbildung 14.: Komplexere if-Abfrage mit zwei Variablen von FITIn nicht möglich, da hier zwei verschiedene Variablen Einfluss auf den Ausgang der Abfrage haben. Daher muss hier die Bedingung aus der Abfrage extrahiert und selbige ein wenig umgeschrieben werden; dies ist in Abbildung 15 zu erkennen. Die Bedingung aus der Abfrage wurde eins zu eins einer Variablen als Wert zugewiesen. Dies wird durch die Behandlung von Wahrheitswerten im C89- und C99-Standard ermöglicht [5, S. 55]. Im Standard sind für true und false keine eigenen Datentypen 25 8. Implementierung eines Tests unter FITIn 1 2 int c o n d i t i o n = x < 10 && y > −10; i f ( condition ) { [ . . . ] } Abbildung 15.: Extraktion der Bedingung aus der Kontrollstruktur vorgesehen; sie werden stattdessen durch eine 0 oder eine 1 in einem Integer abgespeichert. Dadurch wird dann die Manipulation durch FITIn stark vereinfacht: es ist durch eine Negierung des LSB (20 ) möglich, aus den beiden Werten true und false den jeweils anderen zu erzeugen. Des Weiteren wird diese Variable als Bedingung für die Abfrage eingesetzt. Nach dem C-Standard hat sich hier nichts geändert, und die eingesetzte Variable erzeugt eine voll funktionsfähige Abfrage [5]. Die Extraktion der Abfrage-Bedingung kann in unterschiedlicher Granularität durchgeführt werden; es ist auch möglich, den Teilbedingungen jeweils eine Variable zuzweisen, um diese dann getrennt voneinander testen zu können. In der letzten Abbildung (Abbildung 16) wurde zusätzlich zur Definition der Teilbe1 2 3 4 int condition_X = x < 1 0 ; int condition_Y = y > −10; int condition_XY = condition_X && condition_Y ; i f ( condition_XY ) { [ . . . ] } Abbildung 16.: Extraktion der Teilbedingungen aus der Kontrollstruktur dingungen auch die Kombination der beiden Teilbdingungen zur vollständigen - aus den vorhergehenden Beispielen bekannten - Bedingung eingeführt, um erschöpfendes Testen zu erlauben. 8.4. Testautomatisierung Da das erschöpfende Testen sehr umfangreich sein kann (siehe Kapitel 7), ist es hilfreich, das Testen in manchen Fällen zu automatisieren. Gerade wenn in alle Bits einer Variablen einmal ein Soft Error injiziert werden muss, lohnt sich der Einsatz einer Automatisierung über ein Bash-Skript. Das Bash-Skript (vgl. Abbildung 17). führt die Modifikationen des Steuerungsskripts für Tests durch. Danach werden die Tests dann gestartet. Der genaue Ablauf ist wie folgt: in Zeile 6 wird die Schleife gestartet, die alle Fälle behandelt. In Zeile 10 wird das von FITIn genutzte Steuerungsskript auf ein vorgefertigtes Template zurückgesetzt, in dem nur noch die Bitmaske für den Soft Error eingefügt werden muss (Zeile 12-13). Dies geschieht durch Verwendung des in Ubuntu integrierten Werkzeugs sed. Danach wird nur noch Valgrind mit FITIn als Tool aufgerufen (Zeile 15ff). Zur Auswertung der Tests werden die dazu relevanten Daten in ein für alle Fälle gemeinsames Dateilog geschrieben. 26 8.4. Testautomatisierung 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #! / b i n / bash echo "Bash−T e s t i n g with FITIn " ; # Clear log rm . /NF. l o g # Loop through a l l 64 c a s e s fo r i i n { 1 . . 6 4 } do echo " T e s t i n g Case $ i " ; # Clear lua s c r i p t cp −f . / NF_caseTemplate . l u a . / NF_caseBash . l u a # Replace t e m p l a t e ’X ’ with c u r r e n t case number case_number=$ ( ( i − 1 ) ) s e d −i " s /X/ $case_number / g" . / NF_caseBash . l u a # Run f i t i n . . / . . / . . / v a l g r i n d − f i t i n / b i n / v a l g r i n d −−v e r b o s e −−t o o l= f i t i n \ −−l o g − f i l e =NF_caseBash . l o g −−c o n t r o l −s c r i p t=NF_caseBash . l u a \ . / NF_TestBits $ i done Abbildung 17.: Bash-Skript zur Automatisierung der Tests 27 9. Test der Behandlungsmethoden Im Folgenden werden die beiden Ansätze Programmflusskontrolle und Numerische Fehlerkorrektur zum Schutz vor Soft Errors getestet. Dazu werden für beide Tests die gleichen Bedingungen gelten: 1. Es wird auf eine konkrete These hin getestet. 2. Das CG-Verfahren verwendet den in Abbildung 18 dargestellten Datensatz. 3. Die Tests werden auf dem gleichen System durchgeführt (siehe Kapitel 9.1). In Bezug auf den Testdatensatz Datensatz muss noch hinzugefügt werden, dass dieser von einer unabhängigen Implementierung überprüft wurde [15]. Bei normaler Berechnung (also ohne Soft Error) werden zur Berechnung des Ergebnisses drei Iterationen benötigt. 72 −36 0 0 0 1 −36 72 −36 1 0 0 0 −36 72 −36 0 · x = 1 , 0 1 0 −36 72 −36 0 0 0 −36 0 1 0.0694 0.1111 x= 0.1250 0.1111 0.0694 Abbildung 18.: Der für die Berechnung verwendete Testdatensatz 9.1. Testumgebung Um die Tests nachvollziehen zu können, ist eine genaue Dokumentation der zum Einsatz kommenden Soft- und Hardware notwendig. In dieser Arbeit wurde der Linux-Kernel 3.2 eingesetzt; dieser wurde mit dem Betriebssystem Ubuntu in der Version (12.04, 32 bit) verwendet. Ubuntu wurde in der Virtualisierungssoftware Vmware Player (Version 6.0.1) installiert. Im Ubuntu-Betriebssystem wurden die Algorithmen und Tests mittels der GNU Compiler Collection (Version 4.8.1) kompiliert und ausgeführt; es wurde der C89-Standard verwendet (vgl. Anhang A.3). Es wurde mit FITIn (Stand Juni 2014) getestet und die Steuerung von FITIn während der Tests durch Lua-Skripte durchgeführt. Die Host-Maschine verfügt über einen Prozessor der aktuellen Haswell-Generation von Intel (Intel Core i7-4500U) und wird unter Windows 8.1 Pro in der 64 bit-Version betrieben. Davon wurden der virtuellen Maschine zwei Kerne des Prozessors zugeteilt. Die Prozessor-Befehle wurden durch die automatisch ausgewählte Virtualisierungs-Engine verarbeitet. 29 9. Test der Behandlungsmethoden 9.2. Test der Programmflusskontrolle Eine Kombination aus White-Box-Testing und Äquivalenzklassen soll nun zum Testen verwendet werden. Um den Umfang in einem adäquaten Rahmen zu halten, soll folgende These überprüft werden: Die Programmflusskontrolle schützt die Kontrollstrukturen im CG-Verfahren vor einem Soft Error. Dafür werden folgende Annahmen getätigt: • Die Anzahl der Soft Errors beschränkt sich auf 0 oder 1. • Es werden nur die Kontrollstrukturen des CG-Verfahrens betrachtet. Daraus folgt, dass Soft Error in sämtlichen Variablen außerhalb der Kontrollstrukturen nicht in Betrachtung sind. Darauf basierend werden im Folgenden die Testfälle spezifiziert. 9.2.1. Spezifikation der Tests k=0 k < MAX && ||r||2 > ||R||2 k += step false true Berechne Vektor Rückgabe der Lösung Abbildung 19.: Ablaufdiagramm des CG-Verfahrens (gekürzt) In Abbildung 19 ist das Ablaufdiagramm des CG-Verfahrens dargestellt. Es ist zu erkennen, dass das Verfahren nur eine Kontrollstruktur enthält; eine for-Schleife (vgl. auch Kapitel 3.3). Diese enthält zwei Teilbedingungen: k < M AX und ||r||2 > ||R||2 . Damit sind in der Schleife die beiden Variablen k und step enthalten. Um diese Kontrollstruktur gemäß der Hypothese lückenlos zu testen, sind jeweils ein Soft 30 9.2. Test der Programmflusskontrolle Error in die Abbruchbedingung, die Laufvariable k und die Inkrementierung der Laufvariable zu injizieren. Dies generiert die folgendenden elf Testfälle; die die folgende Form besitzen: Ort des Soft Errors - Resultat, Beschreibung 1. step - zu großer Iterationsschritt, Schleifenbedingung gilt weiterhin 2. step - zu großer Iterationsschritt, Schleifenbedingung gilt nicht mehr 3. step - zu kleiner Iterationsschritt (step >= 0), Schleifenbedingung gilt weiterhin 4. step - zu kleiner Iterationsschritt (step < 0), Schleifenbedingung gilt weiterhin (dies gilt nur, falls k >= 0 gilt) 5. step - zu kleiner Iterationsschritt (underflow, step < 0), Schleifenbedingung nicht wie vorgesehen erfüllt (da k < 0) 6. k - k > 0, Schleifenbedingung gilt weiterhin 7. k - k > 0, Schleifenbedingung gilt nicht mehr 8. k - k < 0, Schleifenbedingung gilt weiterhin, der Anfangswert ist jedoch unterschritten 9. ||r||2 > ||R||2 - Schleifenbedingung gilt weiterhin 10. ||r||2 > ||R||2 - Schleifenbedingung gilt nicht mehr 11. k < M AX && ||r||2 > ||R||2 - nach letzter regulärer Iteration, Schleifenbedingung gilt weiterhin 12. keiner - kein Soft Error tritt auf Es wird auch der Fall betrachtet, dass ein Soft Error nach der letzten regulären Iteration auftritt. Hier ist nur noch ein Soft Error in der Abbruchbedingung relevant (Testfall 11). Dieser bewirkt, dass nach der letzten gültigen Iteration (Iteration 3), eine weitere Iteration angestoßen wird, indem er die Schleifenbedingung wieder in Kraft setzt. Jedoch müssen nicht alle Testfälle umgesetzt werden; dies wird im Folgenden dargestellt und erläutert. Bei Betrachtung der Testfälle 6 bis 8 wird schnell deutlich, dass diese eine starke Ähnlichkeit mit den Testfällen 1 bis 5 besitzen. Bei den Tesfällen 1 bis 5 wird die Laufvariable mit einem (durch einen Soft Error) veränderten Inkrement verrechnet. Selbiges lässt sich auf die Testfälle mit einem Soft Error in k übertragen; diese lassen sich durch ein verändertes Inkrement nachstellen. Daher können die Testfälle 6 bis 8 übersprungen werden. Weiterhin kann auch der Testfall 4 übersprungen werden; der Grund dafür besteht in der fehlenden Möglichkeit, diesen Fall mit einem Soft Error zu realisieren. Ein Soft Error kann nur ein Bit negieren; um damit aus einem kleinen k eine negative Zahl zu erzeugen, müsste das höchstwertigste Bit negiert werden. Daraus folgt jedoch eine sehr große, negative Zahl (−215 − 1 + k), welche aber die Definition des Testfalls 4 nicht mehr erfüllt. 31 9. Test der Behandlungsmethoden Durch die Beschränkung auf das Auftreten eines einzelnen Soft Errors ist es nicht notwendig, die Kombination von verschiedenen Testfällen zu betrachten. 9.2.2. Realisierung Durch Vergleichsbetrachtungen konnte die Anzahl der zu testenden Fälle reduziert werden. Dennoch wäre für eine erschöpfende Testsuite gemäß der These immer noch eine sehr viel größere Zahl an Tests notwendig, da jedes einzelne Bit der Variablen step und k einmal in jeder Iteration durch einen Soft Error modifiziert werden muss. Es ist jedoch möglich, dies durch die Einführung von Äquivalenzklassen (Kapitel 7.1) signifikant zu verringern. Dazu kann die Beschreibung der verschiedenen Testfälle herangezogen werden. In den Testfällen 1 und 2 ist es ausreichend, ein hochwertiges Bit zu negieren; es ist irrelevant welches, solange die gestellte Bedingung erfüllt wird. In Fall 3 wird das niedrigstwertige Bit negiert, und damit das Inkrement für diesen Schleifendurchlauf auf 0 gesenkt. In Fall 5 führt das Flip des höchstwertigen Bits zum gewünschten Ergebnis: ein negatives Inkrement, die Größe ist hier irrelevant. Durch die Extraktion der Teilbedingung (vgl. Kapitel 8.3) in Fall 9 ist auch hier die Stelle des Soft Errors irrelevant, solange dies nicht das niedrigstwertige Bit ist, da dies sonst Testfall 10 entspricht. Schließlich gelten in Fall 11 die gleichen Bedingungen wie bei Fall 9; es wird ein Soft Error in ein Bit, das nicht das niedrigstwertigste ist, injiziert. Hier muss jedoch, im Gegensatz zu den vorherigen Fällen, darauf geachtet werden, dass der Soft Error während des vierten Aufrufs geschieht, um eine zusätzliche Iteration auszulösen. Abschließend folgt eine Auflistung der Testfälle, die tatsächlich implementiert werden müssen. 1. step - zu großer Iterationsschritt, Schleifenbedingung gilt weiterhin 2. step - zu großer Iterationsschritt, Schleifenbedingung gilt nicht mehr 3. step - zu kleiner Iterationsschritt (step >= 0), Schleifenbedingung gilt weiterhin 5. step - zu kleiner Iterationsschritt (underflow, step < 0), Schleifenbedingung nicht wie vorgesehen erfüllt (da k < 0) (Fall 5) 9. ||r||2 > ||R||2 - Schleifenbedingung gilt weiterhin (Fall 9) 10. ||r||2 > ||R||2 - Schleifenbedingung gilt nicht mehr (Fall 10) 11. k < M AX && ||r||2 > ||R||2 - nach letzter regulärer Iteration, Schleifenbedingung gilt weiterhin (Fall 11) 9.2.3. Testergebnisse Mit den definierten Tests wurden dann die in Abbildung 20 dargestellten Ergebnisse erzielt. In der Spalte außen links ist die Nummerierung des Falls dargestellt, diese kann eins zu eins mit der Beschreibung in Kapitel 9.2.1 in Relation gesetzt werden. Die Spalte darauf enthält den Reset; ist ein Reset aufgetreten (bzw. ist die Programmflusskontrolle 32 9.2. Test der Programmflusskontrolle ausgelöst worden), so wird in Klammern die Anzahl der Resets angegeben. Anschließend wird der Grund des Resets angegeben. Darauf folgen noch die Korrektheit des Ergebnisses sowie die Gesamtzahl an benötigten Iterationen, die zur Berechnung des Ergebnis benötigt wurden. Es ist anzumerken, dass in sämtlichen Fällen, in denen ein Reset auftritt, die Anzahl Fall 1 Reset (Anzahl) Ja (1) 2 Ja (1) 3 Ja (1) 5 Ja (1) 9 10 Nein Ja (1) 11 Ja (1) 12 Nein Grund des Resets Falsche Schrittweite Auslösen der Schleifenbedingung Falsche Schrittweite Falsche Schrittweite Auslösen der Schleifenbedingung Auslösen der Schleifenbedingung Korrektes Ergebnis ja Gesamtzahl Iterationen 3 ja 3 ja 3 ja 3 ja ja 3 3 ja 3 ja 3 Abbildung 20.: Testergebnisse für die Programmflusskontrolle maximal 1 beträgt. Auch der in der Tabelle angegebene Grund des Resets entspricht den in den Testspezifikationen festgelegten Bedingungen für die einzelnen Fälle: bei Fall 2 und 10 ist die Schleifenbedingung ausgehebelt, in den anderen Fällen ist der Grund die falsche Schrittweite. Zusätzlich führen alle Testfälle trotz eines Soft Errors zum korrekten Ergebnis, wobei keine weiteren Iterationen benötigt werden. Die Tatsache, dass bei der Programmflusskontrolle keine weiteren Iterationen notwendig sind, ist jedoch nur für das CG-Verfahren gesichert. 9.2.4. Evaluation Anhand der Testergebnisse ist eine positive Auswertung möglich. Da in allen Fällen das Ergebnis korrekt war, ist die These (siehe Kapitel 9.2) ebenfalls korrekt. Weiterhin zeigt sich ein sehr gute Effizienz, da keine zusätzlichen Iterationen in einem der Fälle notwendig war. Zudem sind die Veränderungen des Quellcodes durch das geringe Ausmaß an zusätzlichem Code schnell und einfach durchzuführen. Damit ist die Programmflusskontrolle 33 9. Test der Behandlungsmethoden ein gutes Verfahren, um das CG-Verfahren vor einem Soft Error zu schützen. 9.3. Test der numerischen Fehlerkorrektur Nach dem Testen der Programmflusskontrolle folgt nun der Test der numerischen Fehlerkorrektur. Auch hier soll von einer konkreten These ausgehend getestet werden: Die numerische Fehlerkorrektur schützt das Ergebnis des CG-Verfahrens vor einem SoftError in den Komponenten des Lösungsvektors. Dabei werden Einschränkungen durch folgende Annahmen vorgenommen: • Die Anzahl an Soft Errors beschränkt sich auf 0 oder 1. • Der Soft Error injiziert einen Fehler nach dem letzten Schleifendurchlauf in den Lösungsvektor. • Der Umfang beschränkt sich auf den verwendeten Testdatensatz (vgl. Abbildung 18). Durch die Injektion des Soft Errors in den Lösungsvektor nach der letzten Schleifeniteration werden verschiedene Fälle abgedeckt. Der Soft Error steht damit stellvertretend für einen Soft Error in einer Variable des CG-Verfahrens. Der genaue Ort im CG-Verfahren ist dabei irrelevant, solange aus dem Soft Error eine Veränderung der Lösung resultiert. 9.3.1. Spezifikation der Tests Eine naive Herangehensweise an das Testen der These bedeutet die Injektion eines Soft Errors in jedes Bit jeder Komponente des Lösungsvektors; dies würde bei dem verwendeten Datensatz (Abbildung 18, die Lösung hat die Dimension fünf) ca. 320 Testfälle erzeugen. Diese Zahl kann aber wieder durch die Verwendung von Äquivalenzklassen verringert werden. Da die einzelnen Komponenten des Lösungsvektors die gleiche Größenordnung besitzen, ist es irrelevant, in welche Komponente der Soft Error injiziert wird. Daher wird von einem Soft Error in der ersten Komponente ausgegangen, was die Testfälle auf 64 - ein Testfall pro Bit – reduziert. Dabei bezeichnet Testfall i eine Injektion eines Soft Errors in das Bit mit der Maske 2i . 9.3.2. Realisierung Die Realisierung der ersten Partition ist die einfachste: dazu reicht es aus, das Vorzeichenbit der Zahl zu negieren; der Soft Error wird also in das MSB injiziert [13, S. 4]. Bei den beiden anderen Partitionen wird ein Bash-Skript zur automatisierten Abarbeitung der verschiedenen Bitstellen benutzt. Das Skript erlaubt das Einsetzen des jeweiligen Bits in das Lua-Steuerungsskript und führt danach den Test aus (siehe Kapitel 8.4). Da 34 9.3. Test der numerischen Fehlerkorrektur zum Ändern der Größenordnung ein Soft Error im Exponenten der Zahl notwendig ist, sind hier die elf Bits nach dem Vorzeichen zu adressieren (also MSB – 1 bis MSB – 12) [13, S. 4]. Im Gegensatz dazu ist für eine Manipulation des Wertes die Mantisse zu adressieren; demnach also die verbleibenden Bits (MSB – 12 bis zum LSB) [13, S. 4]. 9.3.3. Testergebnisse Die mit der numerischen Fehlerkorrektur erzielten Ergebnisse sind in den Abbildungen 21 bis 23 dargestellt. Dabei wurden zur Terminierung der numerischen Fehlerkorrektur Genauigkeiten von = 10− 3, = 10− 5, = 10− 10 und = 10− 15 verwendet. Der Test der numerischen Fehlerkorrektur mit einer Genauigkeit von 10−3 erzeugte die −6 x 10 2.5 1 0.9 0.8 Neuberechnung der Lösung Relativer Fehler 2 1.5 1 0.5 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 0 10 20 30 40 Nummer des Testfalls 50 60 70 0 0 10 20 30 40 Nummer des Testfalls 50 60 70 Abbildung 21.: Der relative Fehler (links) und die Anzahl an Neustarts (rechts) bei einer Genauigkeit von = 10−3 und = 10−5 gleichen Daten wie mit einer Genaugikeit von 10−5 . Daher sind beide Tests in einer Grafik zusammengefasst. Es ist klar zu erkennen, dass die numerische Fehlerkorrektur zweimal ausgelöst wird und einen Neustart verursacht. Dies geschieht bei den Testfällen 32 und 64. Zudem verdoppelt sich der relative Fehler mit steigender Wertigkeit des negierten Bits stetig. Bei einem Neustart ist der Fehler wieder nahezu 0. Der maximale relative Feher beträgt ca. 2 ∗ 10−6 . Im Vergleich zu den vorherigen Genauigkeiten ist bei einem von 10−10 die Zahl der Neustarts stark erhöht. Circa ein Drittel der Testfälle löst die numerische Fehlerkorrektur aus. Der maximale relative Fehler ist auf 2.5 ∗ 10−10 gesunken. Insgesamt zeigt der relative Fehler das gleiche Bild wie bei = 10−3 . Jedoch sind die Spitzen nach links verschoben, da mehr Neustarts den Fehler der Testfälle verringert haben. Genau wie bei = 10−3 ist der Fehler bei einem Neustart nahezu 0. Abschließend ist die Testreihe für = 10−15 angegeben (Abbildung 37). Es ist zu erkennen, dass ca. 94% der Testfälle mindestens einen Neustart verursachen. Während bei der einen Hälfte nur ein Neustart auftritt, wird bei der anderen Hälfte das CG-Verfahren 35 9. Test der Behandlungsmethoden −10 x 10 1 0.9 2.5 Neuberechnung der Lösung 0.8 Relativer Fehler 2 1.5 1 0.7 0.6 0.5 0.4 0.3 0.2 0.5 0.1 0 0 10 20 30 40 Nummer des Testfalls 50 60 70 0 0 10 20 30 40 Nummer des Testfalls 50 60 70 Abbildung 22.: Der relative Fehler (links) und die Anzahl an Neustarts (rechts) bei einer Genauigkeit von = 10−10 zweimal neu aufgerufen. In zwei Fällen beträgt die Iterationszahl der numerischen Fehlerkorrektur drei. Der maximale Fehler ist auf 3.5 ∗ 10−14 gesunken. Auch hier ist der Fehler bei einem Neustart nahezu 0. 9.3.4. Evaluation In den Abbildungen 21 bis 23 wird bestätigt, dass sich die numerische Fehlerkorrektur als Verfahren zum Schutz vor einem Soft Error im Lösungsvektor des CG-Verfahrens eignet. Zudem ist der verbleibende Fehler nach einem Neustart sehr gering (nahe 0). Daraus lässt sich folgern, dass die numerische Fehlerkorrektur ein effektives Verfahren zum Schutz vor einem Soft Error ist. Des Weiteren sind drei Neustarts die höchste Anzahl, die zur korrekten Bestimmung der Lösung benötigt werden. Dies zeigt einen geringen Laufzeitoverhead, was ebenfalls für das Verfahren spricht. 9.3.5. Vergleich mit Oboril et al. In Artikel [9] von Oboril et al. ist ebenfalls ein Test mit verschiedenen Testfällen beschrieben. Von Oboril et al. wurde jedoch ein anderer Ansatz als in dieser Arbeit gewählt: anstelle eines systematischen, bitgenauen Tests wurde eine randomisierte Soft Error-Injektion durchgeführt. Dazu wurde aus einem Vektor zufällig eine Komponente ausgewählt, und darin ein Bit zufällig negiert. Dabei wurden Wahrscheinlichkeiten, mit denen ein Soft Error auftritt, in Betracht gezogen. So traten die Soft Errors in den einzelnen Testfällen mit einer Wahrscheinlichkeit von 10−6 , 10−7 , 10−8 usw. bis 10−12 auf. Zudem wurde eine Matrix der Dimension 2 ∗ 106 und ein von 10−10 verwendet. Um einen Soft Error zu korrigieren, waren meist mehrere Iterationen der numerischen Fehlerkorrektur notwendig [9]. 36 9.4. Fazit −14 x 10 3.5 3 3 Neuberechnung der Lösung 2.5 Relativer Fehler 2.5 2 1.5 1 2 1.5 1 0.5 0.5 0 0 10 20 30 40 Nummer des Testfalls 50 60 70 0 0 10 20 30 40 Nummer des Testfalls 50 60 70 Abbildung 23.: Der relative Fehler (links) und die Anzahl an Neustarts (rechts) bei einer Genauigkeit von = 10−15 Aufgrund der abweichenden Herangehensweise durch Oboril et al. ist ein Vergleich zu den Ergebnissen dieser Arbeit nur sehr begrenzt möglich. Allein die benötigte Anzahl an Iterationen der numerischen Fehlerkorrektur bei einem Soft Error ist vergleichbar. Hier besitzt das Ergebnis dieser Arbeit einen geringen Vorteil, da maximal eine Iteration benötigt wird. Bei einer sehr großen Genauigkeit (10−15 ) steigt die Zahl der notwendigen Iterationen der numerischen Fehlerkorrektur auf drei. Oboril et al. verweisen hier lediglich darauf, dass in jedem Fall mehrere Iterationen des Verfahrens notwendig sind [9]. Es besteht jedoch die Möglichkeit, dass bei Oboril et al. in einer Iteration der numerischen Fehlerkorrektur wieder ein Soft Error aufgetreten ist. Damit würde es sich im Endeffekt um zwei oder mehr Soft Errors handeln. 9.4. Fazit Im letzten Kapitel 9 sind die beiden Verfahren Programmflusskontrolle und numerische Fehlerkorrektur jeweils bezüglich einer konkreten These getestet worden. Die Testergebnisse und die Analysen in den jeweiligen Kapiteln der Verfahren unterstützen folgendes Fazit: die Programmflusskontrolle bietet erfolgreich Schutz vor einem Soft Error in einer Kontrollstruktur. Gleiches gilt analog für die numerische Fehlerkorrektur bei einem Soft Error im Lösungsvektor des CG-Verfahrens. Die Verwendung von durch ECC geschützten Speicher ist nur eingeschränkt hilfreich, da kein umfassender Schutz besteht. Da die numerische Fehlerkorrektur nicht unterscheidet, an welcher Stelle im CG-Verfahren der Soft Error genau eintritt, scheint es der Programmflusskontrolle gegenüber überlegen zu sein. Allerdings besitzt die Fehlerkorrektur einen entscheidenen Nachteil: ein Soft Error kann die Konvergenz des CG-Verfahrens so beeinflussen, dass dieses nicht oder erst sehr viel später terminiert. Damit ist es möglich, dass ein enormer Laufzeitoverhead ensteht. 37 9. Test der Behandlungsmethoden Da die Programmflusskontrolle nur vor einem Soft Error in einer Kontrollstruktur schützt, besitzen weder die Programmflusskontrolle noch die numerische Fehlerkorrektur insgesamt einen ausschlaggebenden Vorteil. Daher wäre eine Kombination beider Verfahren für umfassenderen Schutz optimal. 38 10. Zusammenfassung und Ausblick In dieser Arbeit wurde ein Beitrag zu dem aktuellen Thema der Soft Errors geleistet. Durch Strahlung induziert, beeinflussen Soft Errors durch die temporäre Negierung eines Bits den Programmablauf mit teils massiven Folgen. Daher ist eine Behandlung und Korrektur wünschenswert; der weitverbreiteste Ansatz ist die Verwendung von ECCgeschütztem Speicher. Jedoch bietet dies keinen vollständigen Schutz, weshalb hier zwei andere, softwarebasierte Ansätze vorgestellt wurden: die Programmflusskontrolle als Abwandlung des Defensive Programming, und die numerische Fehlerkorrektur. Beide Algorithmen wurden im Zusammenhang mit dem Verfahren der konjugierten Gradienten (CG-Verfahren) implementiert; dabei diente das CG-Verfahren zur Verdeutlichung der Auswirkungen. Im Anschluss daran wurde die Funktionsweise der Programmflusskontrolle untersucht. Sie wurde bezüglich des Schutzes vor einem Soft Error in den Kontrollstrukturen des CG-Verfahren mit einer hundertprozentigen Überdeckung getestet. Das Testergebnis belegt den Schutz durch die Programmflusskontrolle, und zeigt einen geringen Laufzeitoverhead. Des Weiteren wurde die numerische Fehlerkorrektur anhand der These getestet, sie biete Schutz vor einem Soft Error im Lösungsvektor des CG-Verfahrens. Auch hier bestätigt das Testergebnis die Eignung der numerischen Fehlerkorrektur in diesem Anwendungsfall sowie einen geringen Laufzeitoverhead. Es wurde argumentativ gezeigt, dass eine Kombination der beiden Verfahren optimal ist. Diese Arbeit beschränkt sich darauf, die Programmflusskontrolle sowie die numerische Fehlerkorrektur in den für sie vorgesehenen Gebieten zu betrachten. In weiterführenden Betrachtungen könnte es von Interesse sein, dies auf eine erhöhte Anzahl von Soft Errors oder einen anderen Ort der Modifikation, bspw. das Korrekturverfahren selbst, auszuweiten. Des Weiteren ist auch eine Kombination der beiden Ansätze möglich, um möglicherweise einen umfassenderen Schutz zu gewährleisten. Zuletzt ist die Frage, inwiefern die Programmflusskontrolle und die numerische Fehlerkorrektur auf andere Algorithmen übertragen werden können, noch offen. Auch die damit verbundenen Effizienzund Zusatzaufwandsbetrachtungen sind sicherlich interessant. 39 Abbildungsverzeichnis Abbildungsverzeichnis 1. Ablaufdiagramm einer if-Abfrage . . . . . . . . . . . . . . . . . . . . . . . 3 2. 3. Pseudo-Code des Verfahren der konjugierten Gradienten [9, S. 146] . . . . Implementierung der Datentypen Matrix und Vektor als Array(s) . . . . . 8 9 4. 5. 6. 7. Beispiel einer Assertion in einer Funktion zur Division . . . . . . . . . Kontrollstruktur for nach der Anwendung der Programmflusskontrolle Verhinderung unnötiger Iterationen durch die Kombination if-break . Algorithmus zur Summe der Zahlen von 1 bis MAX . . . . . . . . . . 8. Pseudo-Code der numerischen Fehlerkorrektur (vgl. [9, S. 148]) . . . . . . 17 9. 10. Addition zweier Zahlen, in die ein Soft Error injiziiert werden soll. . . . . 19 Einfache if-Abfrage mit zwei Bedingungen . . . . . . . . . . . . . . . . . 20 11. 12. 13. 14. 15. 16. 17. Quellcode mit beispielhafter Annotation . . . . . . . . . . . . . . . Ein beispielhaftes Kontroll-Skript in Lua zur Steuerung von FITIn Einfache if-Abfrage als Beispiel für eine Kontrollstruktur . . . . . . Komplexere if-Abfrage mit zwei Variablen . . . . . . . . . . . . . . Extraktion der Bedingung aus der Kontrollstruktur . . . . . . . . . Extraktion der Teilbedingungen aus der Kontrollstruktur . . . . . . Bash-Skript zur Automatisierung der Tests . . . . . . . . . . . . . . . . . . . . 18. 19. 20. 21. Der für die Berechnung verwendete Testdatensatz . . . . . . . . Ablaufdiagramm des CG-Verfahrens (gekürzt) . . . . . . . . . . Testergebnisse für die Programmflusskontrolle . . . . . . . . . . Der relative Fehler (links) und die Anzahl an Neustarts (rechts) Genauigkeit von = 10−3 und = 10−5 . . . . . . . . . . . . . Der relative Fehler (links) und die Anzahl an Neustarts (rechts) Genauigkeit von = 10−10 . . . . . . . . . . . . . . . . . . . . . Der relative Fehler (links) und die Anzahl an Neustarts (rechts) Genauigkeit von = 10−15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . einer . . . einer . . . einer . . . 22. 23. . . . . . . bei . . bei . . bei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 14 15 16 23 24 25 25 26 26 27 . 29 . 30 . 33 . 35 . 36 . 37 41 Literaturverzeichnis Literaturverzeichnis [1] Burnstein, Ilene: Practical Software Testing. Springer Science+Business Media, Inc., 2003 (Springer Professional Computing) [2] Firesmith, Donald G.: A Comparison of Defensive Development and Design by Contract. In: Technology of Object-Oriented Languages and Systems, IEEE Computer Society, August 1999, S. 258 – 267 [3] GTB Working Party Glossary: ISTQB/GTB Standardglossar der Testbegriffe. German Testing Board e.V., 2013 [4] Heing-Becker, Marcel: Bitfehlerinjektion in Register auf der Basis von FITIn. Hamburg, Technische Universität Hamburg-Harburg, Bachelorarbeit, Juni 2013 [5] Joint Technical Committee, Subcommittee SC 22: ISO/IEC 9899:TC2. Mai 2006 , Kapitel 6.3.1.2 Boolean Type, S. 55 [6] Karnik, Tanay; Hazucha, Peter ; Patel, Jagdish: Characterization of soft errors caused by single event upsets in CMOS processes. In: Dependable and Secure Computing, IEEE Transactions on 1 (2004), April, Nr. 2, S. 128–143 [7] Myers, Glenford J.; Sandler, Corey ; Badgett, Tom: The art of software testing. 3. Ausgabe. New Jersey : John Wiley & Sons, Inc., 2012 [8] Nocedal, Jorge; Wright, Stephen J.: Conjugate Gradient Methods. In: Numerical Optimization. 2. Ausgabe. Springer New York, 2006 (Springer Series in Operations Research and Financial Engineering), Kapitel 5, S. 101–134 [9] Oboril, Fabian; Tahoori, Mehdi B.; Heuveline, Vincent; Lukarski, Dimitar ; Weiss, Jan-Philipp: Numerical Defect Correction as an Algorithm-Based FaultTolerance Technique for Iterative Solvers. In: 17th IEEE Pacific Rim International Symposium on Dependable Computing, IEEE Computer Society, Dezember 2011, S. 144–153 [10] Pless, V.: Introduction to the Theory of Error-Correcting Codes. Wiley, 1998 (A Wiley-interscience publication) [11] Qin, Feng; Lu, S. ; Zhou, Yuanyuan: SafeMem: exploiting ECC-memory for detecting memory leaks and memory corruption during production runs. In: HighPerformance Computer Architecture, 2005. HPCA-11. 11th International Symposium on, 2005, S. 291–302 [12] Shewchuk, Jonathan Richard: An Introduction to the Conjugate Gradient Method Without the Agonizing Pain. August 1994. – Carnegie Mellon University 43 Literaturverzeichnis [13] Stevenson, David et al.: IEEE Standard for Binary Floating-Point Arithmetic. In: ANSI/IEEE Std 754-1985 (1985) [14] Tu, Jiyuan; Yeoh, Guang Heng ; Liu, Chaoqun: Computational Fluid Dynamics. 2. Auflage. Butterworth-Heinemann, 2012 [15] Universität Ulm, Fakultät Mathematik: CG-Verfahren. Internet. http://www.mathematik.uni-ulm.de/numerik/teaching/ws07/NUM1b/prog/ anleitung01/. Version: 2007/2008, Abruf: 22. Juli 2014 44 Literaturverzeichnis Begriffe und Abkürzungen CG-Verfahren ECC GCC MSB LGS LSB VM Verfahren der konjugierten Gradienten Error Correction Code GNU C Compiler Most Significant Bit, deutsch: höchstwertigstes Bit Lineares Gleichungssystem Least Signifcant Bit, deutsch: niedrigstwertigstes Bit Virtuelle Maschine 45 A. Anhang A.1. Die Lösung von Ax = b minimiert die quadratische Form Sei A symmetrisch. Sei x ein Punkt, der der linearen Gleichung A ∗ x = b (1) genügt und die quadratische Form f (x) = 12 xT Ax − bT x + c (2) minimiert. e sei ein Fehlerterm. Dann gilt: 1 f (x + e) = (x + e)T A(x + e) − bT (x + e) + c 2 1 T 1 = x Ax + eT Ax + eT Ae − bT x − bT e + c 2 2 1 1 = xT Ax − bT x + c + eT b + eT Ae − bT e 2 2 1 T = f (x) + e Ae 2 [12, S. 54] 47 A. Anhang A.2. Implementierung der Matrix- und Vektoroperationen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 /∗ A d d i e r t z w e i Vektoren e l e m e n t w e i s e ∗/ void add ( double ∗a , double ∗b , double ∗ s o l ) { int i = 0 ; double ∗tmp = m a l l o c ( s i z e o f ( double ) ∗ N ) ; f o r ( i = 0 ; i < N; i ++) { tmp [ i ] = a [ i ] + b [ i ] ; } f o r ( i = 0 ; i < N; i ++) { s o l [ i ] = tmp [ i ] ; } f r e e ( tmp ) ; } /∗ S u b t r a h i e r t z w e i Vektoren e l e m e n t w e i s e ∗/ void s u b t r a c t ( double ∗a , double ∗b , double ∗ s o l ) { double ∗tmp = m a l l o c ( s i z e o f ( double ) ∗ N ) ; int i = 0 ; f o r ( i = 0 ; i < N; i ++) { tmp [ i ] = a [ i ] − b [ i ] ; } f o r ( i = 0 ; i < N; i ++) { s o l [ i ] = tmp [ i ] ; } f r e e ( tmp ) ; } /∗ M u l t i p l i z i e r t e i n e n Vektor mit einem S k a l a r ∗/ void mvs ( double ∗a , double x , double ∗ s o l ) { int i = 0 ; double ∗tmp = m a l l o c ( s i z e o f ( double ) ∗ N ) ; f o r ( i = 0 ; i < N; i ++) tmp [ i ] = 0 ; f o r ( i = 0 ; i < N; i ++) 48 A.2. Implementierung der Matrix- und Vektoroperationen 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 { tmp [ i ] = a [ i ] ∗ x ; } fo r ( i = 0 ; i < N; i ++) { s o l [ i ] = tmp [ i ] ; } f r e e ( tmp ) ; } /∗ M u l t i p l i z i e r t e i n e Matrix mit einem Vektor ∗/ void mmv ( double ∗∗A, double ∗b , double ∗ s o l ) { int i = 0 , j = 0 ; double ∗tmp = m a l l o c ( s i z e o f ( double ) ∗ N ) ; fo r ( i = 0 ; i < N; i ++) tmp [ i ] = 0 ; fo r ( i = 0 ; i < N; i ++) { fo r ( j = 0 ; j < N; j ++) { tmp [ i ] += A[ i ] [ j ] ∗ b [ j ] ; } } fo r ( i = 0 ; i < N; i ++) { s o l [ i ] = tmp [ i ] ; } f r e e ( tmp ) ; } /∗ B i l d e t d i e E u l e r s c h e Norm e i n e s V e k t o r s ∗/ double norm ( double ∗a ) { double r e s = 0 ; int i = 0 ; fo r ( i = 0 ; i < N; i ++) { r e s += a [ i ] ∗ a [ i ] ; } res = sqrt ( res ); return r e s ; } 49 A. Anhang 86 87 88 89 90 91 92 93 94 95 96 97 98 99 /∗ B i l d e t das S k a l a r −Produkt z w e i e r Vektoren ∗/ double s c a l a r _ p r o d u c t ( double ∗a , double ∗b ) { double r e s = 0 ; int i = 0 ; f o r ( i = 0 ; i < N; i ++) { r e s += a [ i ] ∗ b [ i ] ; } return r e s ; } A.3. Beispiel eines Kompilierungbefehls 1 g c c −s t d=c89 −g −O1 −I " . / v a l g r i n d − f i t i n / i n c l u d e / v a l g r i n d " \ 2 −Wall −Wextra −o CM_Condition ∗ . c −lm A.4. Inhalte des digitalen Mediums Auf dem mitgelieferten digitalen Medium ist das Folgende enthalten: • eine elektronische Version dieser Bachelorarbeit • Quellcode, Kompilierungsbefehle und Tests der Software aus dieser Bachelorarbeit Das Medium enthält eine Ordnerstruktur, in der der Quellcode und die Tests der Programme zu finden sind, die für diese Arbeit implementiert wurden. Ausgehend vom Wurzelverzeichnis bachelor folgt das Verzeichnis cg. Für die folgenden Skripte wird davon ausgegangen, dass in dem Verzeichnis bachelor ein Unterverzeichnis valgrind-fitin mit der Installation von Valgrind und dem Tool FITIn zu finden ist. In dem Verzeichnis cg liegt der Quellcode. Während testCG_CM.c die Annotation der Programmflusskontrolle zur Beeinflussung der Bedingung enthält, ist in testCG_CM_step.c der Code für die Beeinflussung der Schrittweite vorhanden. Des Weiteren ist in testCG_NF.c der Code für die numerische Fehlerkorrektur enthalten. Zusätzlich befindet sich im Ordner cg ein Ordner tests, der in zwei weitere Ordner aufgeteilt ist: CM und NF. CM enthält die Steuerungsskripte, Kompilier- und Ausführungsdateien für die verschiedenen Testfälle. Zur Kompilierung wird entweder compile_condition oder compile_step verwendet; zur Ausführung kann der entsprechende Testfall aufgerufen werden. Analog dazu enthält NF die Tests und den Kompilierungsbefehl für die numerische Fehlerkorrektur. Die Kompilierungsbefehle und Tests sind unter Ubuntu lauffähig (siehe Kapitel 9.1). 50