Sobald ein Algorithmus entworfen ist, stellen sich verschiedene Fragen: Wird er funktionieren? Wird er immer korrekte Ergebnisse liefern? Gibt es redundante Teile? Haben meine Tests alle möglichen FĂ€lle abgedeckt? Wie steht es um die Leistung â ist sie akzeptabel, liegt die funktionale AbhĂ€ngigkeit von der âProblemgröĂe" im vorhergesagten Bereich? Wenn wir eine Optimierung des Codes in Betracht ziehen, wo sollten wir beginnen?
Laufzeitanalyse-Werkzeuge, die Daten wĂ€hrend der AusfĂŒhrung eines Algorithmus erfassen, können helfen, viele dieser Fragen zu beantworten. Deshalb bietet Structorizer jetzt mehrere spezifische Visualisierungsoptionen fĂŒr die Analyse des Algorithmenverhaltens und der Teststrategie.
Testen ist ein wichtiger Ansatz zur Validierung eines Algorithmus. Ein wesentliches Kriterium fĂŒr einen systematischen und erschöpfenden White-Box-Test ist die sogenannte Code-Abdeckung (code coverage) â d. h. sicherzustellen, dass jeder einzelne Pfad eines Algorithmus mindestens einmal durchlaufen wurde.
Neben der sorgfĂ€ltigen Planung eines minimalen Satzes notwendiger Datentupel (Eingabe + erwartete Ausgabe) ist ein praktischer Weg erforderlich, um nachzuweisen und zu demonstrieren, dass alle Pfade durch die durchgefĂŒhrten Tests tatsĂ€chlich ausgefĂŒhrt (abgedeckt) wurden. Genau hier kommt Structorizer ins Spiel: Die Runtime Analysis ist eine Funktion innerhalb des Structorizer-Executors, die die VollstĂ€ndigkeit eines Testsatzes demonstrieren kann.
Aktivieren der Runtime Analysis
Um die Runtime Analysis zu aktivieren, öffnen Sie den Executor. Im Executor-Steuerfeld befinden sich in der dritten Zeile ein KontrollkĂ€stchen und eine Auswahlliste fĂŒr die Art der visualisierten Daten:
Executor-Steuerfeld mit Laufzeitanalyse-Optionen
[v] Collect Runtime Data: Schaltet die Erfassung von AusfĂŒhrungszĂ€hldaten ein (oder aus).
Das Ăndern des KontrollkĂ€stchenzustands ist nur zwischen Tests möglich, niemals wĂ€hrend einer AusfĂŒhrung â unabhĂ€ngig davon, ob diese lĂ€uft oder pausiert ist. Die Auswahlliste auf der rechten Seite ist nur verfĂŒgbar, wenn das KontrollkĂ€stchen âCollect Runtime Data" aktiviert ist. (Das bedeutet, Sie können den Typ der angezeigten Daten wĂ€hrend des Tests wechseln; die ausgewĂ€hlte Visualisierung hat keinen Einfluss auf den Erfassungsprozess. Siehe âWahlfreiheit" weiter unten.)
Hinweis: Wenn Sie das KontrollkĂ€stchen abwĂ€hlen, werden alle erfassten Daten sofort gelöscht und alle laufzeitdatenbezogenen Hervorhebungen ausgeschaltet. Wenn Sie die Hervorhebung nur unterdrĂŒcken, aber das Tracking und die Laufzeitdaten beibehalten möchten, wĂ€hlen Sie stattdessen den Visualisierungsmodus âno coloring" aus.
Wenn die Runtime Analysis aktiviert ist, erfasst und zĂ€hlt sie kumulativ verschiedene Daten ĂŒber die AusfĂŒhrung der Elemente. Sie haben dann die Möglichkeit, verschiedene Aspekte der erfassten Daten zu visualisieren:
Test-Code-Abdeckung (wurde das Element mindestens einmal ausgefĂŒhrt?)
AusfĂŒhrungszĂ€hlung (wie oft wurde das Element wĂ€hrend des Tests durchlaufen?)
Anzahl der vom Element selbst durchgefĂŒhrten Operationen (wie viele atomare Operationen hat das Element ausgefĂŒhrt?)
Aggregierte Anzahl der durchgefĂŒhrten Operationen (wenn das Element zusammengesetzt ist: wie viele Operationen wurden als Teil dieser Struktur angewiesen?)
Da der Bereich der letzten beiden Annotationstypen enorm sein kann (von 0 bis zu vielen Millionen und mehr), ist sowohl eine lineare als auch eine logarithmische Skalierung wĂ€hlbar. Entsprechend dem ausgewĂ€hlten Visualisierungstyp Ă€ndert sich die EinfĂ€rbung der Elemente live wĂ€hrend der AusfĂŒhrung. So können Sie verfolgen, was passiert. Der Verzögerungsschieberegler ermöglicht es, den Prozess ausreichend zu verlangsamen, um den Ănderungen zu folgen, oder die Verzögerung zu reduzieren, um schneller zu Ergebnissen zu gelangen.
WĂ€hrend die Runtime Analysis aktiv ist, erscheinen und wachsen in der oberen rechten Ecke jedes Elements zwei Zahlen (ZĂ€hler), getrennt durch einen SchrĂ€gstrich. Die erste Zahl stellt den AusfĂŒhrungszĂ€hler dar, die zweite die Anzahl der lokalen (oder aggregierten) atomaren Operationen, die vom Element angewiesen werden.
Test-Code-Abdeckung
Wenn Sie in der Auswahlliste shallow test coverage (flache Testabdeckung) oder deep test coverage (tiefe Testabdeckung) wĂ€hlen, werden alle Elemente, die wĂ€hrend der jĂŒngsten Testreihe mindestens einmal ausgefĂŒhrt wurden, hervorgehoben. Die Hervorhebungsfarbe dieser âtestabgedeckten" Elemente ist leuchtendes GrĂŒn.
Teilweise abgedecktes Quadratwurzel-Funktionsdiagramm
Abdeckungsregeln
Die Regeln sind recht einfach:
Einfache Anweisungen und SprĂŒnge (Jumps) werden nach ihrer ersten vollstĂ€ndigen AusfĂŒhrung markiert. Leere Elemente (z. B. leere FALSE-Zweige von IF-Anweisungen) mĂŒssen ebenfalls durchlaufen worden sein, bevor sie grĂŒn werden.
CASE-Selektionen ohne Default-Zweig: Da ein fehlender Default-Zweig nicht bedeutet, dass ein Diskriminatorwert, der keinem der angegebenen Selektorkonstanten entspricht, ein Fehler ist â sondern nur ĂŒbergangen wird â, ist diese Struktur Ă€quivalent zu einer CASE-Selektion mit leerem Default-Zweig. Im Shallow-Coverage-Modus wird eine CASE-Selektion als abgedeckt markiert
CASE-Abdeckung mit verstecktem Default-Zweig
, sobald alle expliziten (d. h. sichtbaren) Zweige abgedeckt sind; eine Deep Coverage kann dagegen erst erreicht werden, nachdem auch der versteckte Default-Zweig mindestens einmal durchlaufen wurde.
Schleifenelemente werden beim Verlassen grĂŒn, wenn ihr jeweiliger Schleifenrumpf vollstĂ€ndig abgedeckt wurde; eine kopfgesteuerte Schleife wird also nicht grĂŒn, wenn der Schleifenrumpf umgangen wurde.
Bei Unterroutinen-CALLs gibt es einen Deep- und einen Shallow-Modus. Im Deep-Modus ist die aufgerufene Unterroutine selbst Teil des Tests und muss erst vollstĂ€ndig abgedeckt sein, bevor das CALL-Element, das sie aufruft, ebenfalls grĂŒn werden darf. Der Shallow-Modus geht dagegen davon aus, dass aufgerufene Unterroutinen zuvor getestet und als korrekt erwiesen wurden â das CALL-Element kann also grĂŒn werden, ohne dass die aufgerufene Routine vollstĂ€ndig abgedeckt sein muss.
Im Shallow-Modus werden alle Unterroutinen als abgedeckt behandelt; Sie können jedoch auch bestimmte Unterroutinen selektiv als formal abgedeckt markieren, die im Arranger abgelegt sind, und fĂŒr die verbleibenden Unterroutinen den Deep-Modus beibehalten.
Verwenden Sie die SchaltflĂ€che âSet Covered" in der Arranger-Symbolleiste (oder das KontextmenĂŒ-Element âTest-covered on/off" im Arranger-Index), um ein ausgewĂ€hltes Diagramm als abgedeckt zu markieren. (Die SchaltflĂ€che ist nur im Testabdeckungs-Tracking-Modus aktiviert.) Ein so markiertes Unterroutinen-Diagramm ist an seinem grĂŒnen AuĂenrahmen erkennbar.
Rekursive CALLs: Wenn im Deep-Modus verfolgt, könnte eine rekursive Routine nie abgedeckt werden (da das GrĂŒnwerden erfordern wĂŒrde, dass der enthaltene rekursive CALL grĂŒn ist, was wiederum erfordert, dass die Routine grĂŒn ist, usw.). Daher wird ein als rekursiv erkannter CALL IMMER im Shallow-Modus verfolgt.
Im Arranger als abgedeckt markierte Unterroutine
Hinweise
Abdeckungsmarkierungen werden beim Speichern eines Diagramms nicht gespeichert. Gleiches gilt fĂŒr die ZĂ€hlwerte.
Das Bearbeiten eines Diagramms löscht alle erfassten Laufzeitdaten und Markierungen, aber die Zahlen werden in der Undo-Liste zwischengespeichert. Wenn Sie die Ănderungen rĂŒckgĂ€ngig machen, wird der vorherige Abdeckungszustand wiederhergestellt.
Komplexes Beispiel
Die folgenden drei Bilder zeigen Phasen einer Code-Abdeckungsanalyse mit Structorizer. Das Hauptprogramm ist ein Testrahmen (Diagramm in der unteren rechten Ecke des Arranger-Fensters) fĂŒr einen rekursiven QuickSort-Algorithmus mit mehreren verschachtelten Unterroutinen (Diagramme im ĂŒbrigen Arranger-Bereich). Das erste Bild zeigt einen laufenden Test (beachten Sie, dass mehrere Unterroutinen-CALLs gleichzeitig auf verschiedenen Ebenen offen sind, erkennbar an der orangefarbenen Elementfarbe). Die verbleibenden zwei Bilder zeigen die Situation nach weiteren ausgefĂŒhrten Tests.
Test-Coverage-Modus im Arranger, Phase 1Test-Coverage-Modus im Arranger, Phase 2Test-Coverage-Modus im Arranger, Phase 3
Nach dieser letzten Phase im Deep-Modus sind alle Unterroutinen vollstĂ€ndig abgedeckt. Dies gilt jedoch nicht fĂŒr das Testprogramm âSORTIERUNG_RAHMEN3", da der Sortieralgorithmus stets korrekt arbeitete, sodass der Fehlerpfad (mit der rosafarbenen Ausgabeanweisung) nie durchlaufen wurde. Um eine Inversionserkennung (d. h. einen Sortierfehler) zu provozieren, könnte die Routine âQuickSort" durch einen Dummy (mit demselben Namen, der aber nicht wirklich sortiert) ersetzt werden, bevor der nĂ€chste Test lĂ€uft. Damit wĂŒrde die Abdeckung vollstĂ€ndig.
AusfĂŒhrungszĂ€hlung (Execution Counts)
Die erste der beiden kleinen Zahlen in der oberen rechten Ecke der Elemente stellt die Anzahl dar, wie oft das Element wĂ€hrend der Tests durchlaufen wurde. (Alle Elemente mit AusfĂŒhrungszĂ€hlung gröĂer als null sind zumindest im flachen Sinne âtestabgedeckt".) Die AusfĂŒhrungszĂ€hlung wird erst inkrementiert, wenn der Kontrollfluss das Element verlassen hat.
Wenn Sie in der Anzeige-Auswahlliste execution counts (AusfĂŒhrungszĂ€hlungen) auswĂ€hlen, werden die Elemente entsprechend dieser ersten ZĂ€hlzahl in einem Spektralbereich von tiefem Blau bis leuchtendem Rot eingefĂ€rbt, wobei Tiefblau fĂŒr 0 und Leuchtendes Rot fĂŒr den höchsten ZĂ€hlwert ĂŒber alle beteiligten Diagramme steht.
Hervorhebung der AusfĂŒhrungszĂ€hlung fĂŒr den NEWTON-Algorithmus
Wie zu sehen ist, wurde die WHILE-Schleife nur einmal betreten und verlassen, aber der Schleifenrumpf musste 6-mal wiederholt werden. Somit ist 1 der kleinste und 6 der höchste vorhandene ZĂ€hler, dazwischen gibt es nichts. Daher treten nur zwei Farben auf. (Da innerhalb des Bereichs 0âŠ5 ein Schritt von 1 relativ grob ist, ist das Blau nicht so dunkel, wie man bei Wert 1 erwarten könnte.)
Diese Analyse zeigt beispielsweise, wo eine Code-Optimierung wesentlich wĂ€re (rote Bereiche) und wo sie nicht in Betracht gezogen werden mĂŒsste (blaue Bereiche).
Operationslast (Operation Load)
Die zweite der beiden kleinen Zahlen in der oberen rechten Ecke eines Elements stellt die Last der âatomaren" Operationen dar (rechts vom SchrĂ€gstrich).
Wenn Sie done operations lin. oder done operations log. auswĂ€hlen, wird die EinfĂ€rbung der Diagrammelemente entweder durch lineare oder logarithmische Skalierung der Anzahl der âatomaren" Operationen vorgenommen. Die logarithmische Skala ist sinnvoller, wenn der Zahlenbereich sehr weit gestreut ist.
DurchgefĂŒhrte Operationen beim Berechnen der Quadratwurzel von 25
Zuordnungsregeln
Die Operationslasten fĂŒr die verschiedenen Elementtypen sind:
Instruction-Element: Anzahl der Anweisungszeilen innerhalb des Elements. Hinweis: Ein leeres Element zeigt immer 0, egal wie oft es durchlaufen wurde! BloĂe Typdefinitionen tragen ebenfalls nicht zu den Operationslasten bei.
Wenn ein Element mehrfach durchlaufen wird, multiplizieren sich die oben genannten Werte entsprechend.
Diese Lasten beinhalten nicht die Lasten der Unterstrukturelemente â es ist nur der eigene Beitrag des jeweiligen Elements. Nur das Programm- (oder Funktions-)Element (d. h. der Ă€uĂere Rahmen) zeigt die aggregierte Last der Operationen, die wĂ€hrend des gesamten Programms / der Routine durchgefĂŒhrt wurden (in Klammern). Die EinfĂ€rbung des Programm- bzw. Routinenelements spiegelt jedoch nicht die aggregierte Last wider, sondern basiert auf der eigenen Operationslast, die ĂŒblicherweise null betrĂ€gt (tiefblau im obigen Bild).
Aus dieser EinfĂ€rbung lĂ€sst sich ablesen, wo der gröĂte Teil der Zeit verbraucht wird.
Hinweis: Bei Elementen vom Typ Alternative sind Benutzer manchmal verwundert, wenn eine hohe ZĂ€hlzahl bei der Bedingung erscheint, aber sehr niedrige Operationslasten auf beiden Zweigen. Diese Situation tritt typischerweise auf, wenn einer der Zweige leer ist â ein leeres Element fĂŒhrt nichts aus, sodass seine Operationslast stets bei null bleibt! (Schauen Sie in solchen FĂ€llen auf den DurchlaufzĂ€hler.)
Hinweis zu rekursiven Algorithmen: Bei rekursiven Algorithmen geben die Operationslasten nur einen Hinweis auf die Zeitverteilung auf der obersten Ebene (bzw. der aktuellen Ebene bei Unterbrechung). Andernfalls wĂ€re die Gesamtsumme nicht korrekt, da die Operationslast der analogen Elemente auf aufgerufenen Ebenen bereits in den rekursiven CALLs aggregiert wurde. Dadurch kann es vorkommen, dass der AusfĂŒhrungszĂ€hler einer Anweisung in einer rekursiven Routine gröĂer ist als die angezeigte Operationslast.
Wenn Sie ein strukturiertes Element einklappen, erhöht sich seine angezeigte Operationslast um die Operationslasten der enthaltenen Elemente â als wĂ€ren sie zusammengeschmolzen. Die EinfĂ€rbung des Elements Ă€ndert sich dabei jedoch nicht.
FĂŒr Folgen deklarativer Elemente im Anzeigemodus âHide mere declarations?" gilt Ă€hnliches wie fĂŒr eingeklappte strukturierte Elemente â allerdings unterscheidet sich die Hintergrundfarbe vom normalen Anzeigemodus. Das Darstellungssurrogat fĂŒr die Deklarationsfolge erscheint als deren Zusammenschmelzung (und wird wie im Modus Gesamtoperationslast eingefĂ€rbt).
Gesamtoperationslast (Total Operations Load)
Wenn Sie einen der Visualisierungsmodi total operations lin. oder total operations log. auswÀhlen, wird die EinfÀrbung der Diagrammelemente durch entweder lineare oder logarithmische Skalierung der aggregierten Operationslast vorgenommen, d. h. die Last strukturierter Elemente umfasst rekursiv auch die Lasten ihrer jeweiligen Unterstruktur. Die aggregierten Operationszahlen bei strukturierten Elementen werden in Klammern eingeschlossen, um daran zu erinnern, dass es sich um zusammengesetzte Daten handelt.
Newton-Algorithmus mit hervorgehobenen aggregierten OperationszÀhlern
Dieser Modus eignet sich gut zur Visualisierung der hierarchischen Aggregation und Lastverteilung. Das Programmelement gibt die Gesamtzahl der (fiktiven) Einheitsoperationen zur DurchfĂŒhrung der Berechnung an und kann als SchĂ€tzung fĂŒr die ZeitkomplexitĂ€t dienen, wenn Sie die funktionale Beziehung zur Anzahl oder GröĂe der zu verarbeitenden Daten finden.
Komplexes Beispiel
Die EinfÀrbung wird auch am komplexen Beispiel eines rekursiven QuickSort-Algorithmus mit mehreren verschachtelten Routinenebenen veranschaulicht.
QuickSort mit AusfĂŒhrungszĂ€hler-AnalyseQuickSort mit logarithmischer Operationslast-Analyse
Wahlfreiheit
Sobald die Runtime Analysis begonnen hat, können Sie frei zwischen den verschiedenen Visualisierungsmodi wechseln, die eingefĂ€rbten Diagramme in verschiedene Grafikformate exportieren, drucken, im Arranger anordnen usw. â bis Sie das KontrollkĂ€stchen âCollect Runtime Data" abwĂ€hlen, wodurch alle erfassten Daten gelöscht und die Anzeige in den Standardmodus zurĂŒckversetzt wird.
Wie bereits fĂŒr den Testabdeckungsmodus erwĂ€hnt, speichert das Speichern eines Diagramms im nativen Dateiformat keine der gesammelten Ergebnisse. Wenn Sie die Ergebnisse dokumentieren möchten, mĂŒssen Sie das/die Diagramm(e) als Bild exportieren.