• Keine Ergebnisse gefunden

4.3 Algorithmen in prozeduraler Formulierung

N/A
N/A
Protected

Academic year: 2022

Aktie "4.3 Algorithmen in prozeduraler Formulierung"

Copied!
9
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 363

4.3 Algorithmen in prozeduraler Formulierung

Dieser Abschnitt behandelt Algorithmen in proze- duraler Formulierung und deren Analyse bzgl.

- Speicherbedarf - Laufzeit

Er liefert eine Einblick in die Speicherverwaltung bei prozeduralen Programmen und gibt eine

Ausblick auf weitere algorithmische Problemstellungen und Methoden.

Vorgehen:

- Einführung in die Algorithmenanalyse - Speicherverwaltung

- Laufzeitverhalten

- Prozedurale Algorithmen und deren Analyse - Klassifizierung und Entwicklung von Algorithmen

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 364

4.3.1 Einführung in die Algorithmenanalyse

Zwei zentrale Größen zur Beurteilung von Effizienz:

- Speicher(platz)bedarf (in kByte od. Speicherzellen) - Ausführungs-/Laufzeit (in Sekunden od. Anzahl

ausgeführter Operationen) Bei einem installierten Programm kann man

diese Größen zu gegebenen Eingaben messen bzw. berechnen.

Wir betrachten im Folgenden nur den Fall, dass alle Eingaben am Anfang der Ausführung erfolgen und alle Ausgaben am Ende.

Begriffsklärung: (Speicherbedarf/Laufzeit)

Sei P ein installiertes Programm und M die Menge der zulässigen Eingaben von P.

Der (max.) Speicherbedarf von P ist eine Funktion sb : M kByte

Man spricht von der Raumkomplexität von P.

Die Laufzeit von P ist eine Funktion:

lz : M Sekunden

Man spricht von der Zeitkomplexität von P.

Bemerkung:

In der Praxis ist die Bestimmung von Speicherbedarf und Laufzeit im Allg. nicht einfach:

1. Messen:

- kann nur endlich viele Funktionswerte liefern;

- wird ggf. durch Systemgegebenheiten beeinflusst.

2. Berechnen ausgehend vom Programm:

- setzt Kenntnisse der Übersetzung und des Systems voraus.

Beispiel: (Messen der Laufzeit)

fun primfaktoren (n:int) =

if n<0 then primfaktoren (~n) else if n<=1 then []

else primfakteinb n 2 and primfakteinb n f =

(* 2 <= f <= n *) if n = f

then [f]

else if n mod f = 0

then f::(primfakteinb (n div f) f) else primfakteinb n (f+1)

Ausgewählte Messergebnisse:

Eingabe Laufzeit in Sek.

Gemessen - unter PolyML

- für Betriebssystem XX

- auf einem Rechner der Bauart YY.

12345678 < 1

10000000 < 1

123456789 < 1

100000000 < 1

1234567890 < 1

1000000000 < 1

12345678901 ~ 2

10000000000 < 1

123456789012 > 20000

100000000000 < 1

1234567890123 ~ 15

1000000000000 < 1

12345678901234 > 20000 10000000000000000000000 < 1

Beobachtung:

Laufzeit hängt im Allg. in komplexer Weise von der Eingabe ab.

(2)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 367

Begriffsklärung: (Kriterien zur Effizienz)

Die Gesamteffizienz eines installierten Programms hängt ab von

- der Speicherbedarfsfunktion sb, - der Laufzeitfunktion lz und

- der Häufigkeit, mit der bestimmte Eingaben auftreten.

Bei häufig auftretenden Eingabewerten ist effizientes Verhalten wichtiger als bei seltenen Eingabewerten.

Bemerkung:

• Ein präziser Umgang mit dem obigen Effizienzbegriff ist in der Praxis schwierig:

- sb und lz sind kaum zu bestimmen;

- die Häufigkeitsverteilung der Eingaben ist oft nicht genau bekannt;

- meist möchte man die Effizienz eines Programms unabhängig von seiner Installation betrachten.

• Die Kriterien bilden aber die Grundlage für:

- den Effizienzvergleich von Programmen - die informelle Beurteilung von Effizienz - abstraktere Effizienzbegriffe

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 368

Abstraktere Effizienzbegriffe:

Statt ein installiertes Programm zu betrachten, sieht man üblicherweise von Details ab und betrachtet Effizienz nur näherungsweise.

Vereinfachungen:

1. Betrachte nicht die Eingabewerte selbst, sondern nur ihre Größe (z.B. Länge von Listen, Stellen- anzahl bei Zahlen).

2. Vernachlässige die Häufigkeitsverteilung der Daten.

3. Vernachlässige konstanten Aufwand, also Aufwand, der unabhängig von den Eingabedaten entsteht.

4. Betrachte nur das Wachstum von sb und lz und vernachlässige konstante Faktoren (Abstraktion von der Leistung eines Rechners und den

Implementierungseigenschaften einer Programmier- sprache).

5. Betrachte nur obere und untere Schranken für sb und lz.

Beispiel: (Analyse der Laufzeit)

Wir analysieren eine Implementierung des Algorithmus

„Sortieren durch Auswahl“ (engl. selection sort) .

Eingabe: Feld f von ganzen Zahlen.

Aufgabe: Sortiere das Feld f aufsteigend.

Algorithmische Idee:

- Bestimme eine Komponente mit Indexixminvon f, die ein minimales Element von f[1] ... f[f.length]

enthält.

- Vertausche f[ixmin] und f[1].

- Sortiere dann den Bereich f[2] ... f[f.length] analog.

void main( String[] arg ) {

int[] feld = new int[arg.length];

for( int i = 0; i<feld.length; i++) { feld[i] = Integer.parseInt( arg[i] );

}

sortieren( feld );

for( int i = 0; i<feld.length; i++) { println( feld[i] );

} } }

Mögliche Hauptprozedur:

void sortieren(/*nonnull*/ int[] f) { bereichsort(f,0,f.length-1);

}

void bereichsort( int[] f, int ug, int og) { if (ug >= og) return;

int ixmin = auswaehlen(f,ug,og);

// Vertauschen int temp = f[ug];

f[ug] = f[ixmin];

f[ixmin] = temp;

// Sortieren des restlichen Felds:

bereichsort(f, ug+1, og);

}

/* Liefert Index mit minimalem Element im Bereich f[ug] .. f[og] von Feld f */

int auswaehlen( int[] f, int ug, int og) { int ixmin = ug;

for( int j = ug+1; j <= og; j++ ) { if( f[j] < f[ixmin] ) {

ixmin = j;

} }

return ixmin;

} }

Rekursive Fassung des Sortierens durch Auswahl:

(3)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 371

void sortieren( /*nonnull*/ int[] f ) { for( int i = 0; i<f.length-1; i++ ) {

// bestimme Komponente mit kleinstem // Element in f[i] ... f[f.length-1]

int ixmin = i;

for( int j = i+1; j<f.length; j++ ) { if( f[j] < f[ixmin] ) {

ixmin = j;

} }

// vertausche die Elemente an den // Positionen i und ixmin

int temp = f[i];

f[i] = f[ixmin];

f[ixmin] = temp;

} } }

Iterative Fassung des Sortierens durch Auswahl:

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 372

Analyse der Laufzeit von sortieren:

In Abhängigkeit von der Größe N des Feldes schätzen wir die Anzahl A(N) der Operationen/

Rechenschritte für den ungünstigsten Fall ab.

Eine Operation ist:

- ein Vergleich - eine Zuweisung

- eine Addition/Subtraktion Vereinfachende Annahme:

- Alle Operationen brauchen die gleiche Zeit.

- Andere Aspekte der Ausführung werden vernachlässigt (Speicherverwaltung, Sprünge) Aufwand B(i,N) der inneren Schleife:

B(i,N)≤ (2+1) + (N-i-1) * (2+2)

Aufwand A(N) des gesamten Rumpfes für N≥2:

A(N) = 3 + Σ (1 + B(i,N) + 3 + 2 )

≤3 + Σ (9 + (N-i) * 4 ) = 3 + (N-1)*9 + 4*Σ i

= 9*N – 6 + 2*N*(N-1) = 2*N + 7*N – 6 A(0) = A(1) = 3

i=0 N-2

i=1 N-1

i=1 N-1

2

O-Notation

Häufig interessiert man sich nur für die Größenordnung des Wachstums der

Aufwandsfunktion A(N), die den Aufwand an Speicher bzw. Zeit in Abhängigkeit von der Problemgröße N beschreibt.

Begriffsklärung: (obere Schranke)

einer Aufwandsfunktion A, wenn gilt:

Es gibt c,d in Nat, sodass für alle N in Nat gilt:

A(N) ≤ c * f(N) + d

Man sagt auch, A wächst wie f bzw. ist von der Größenordnung f. Die Menge aller Funktionen von der Größenordnung f bezeichnet man mit O(f) : O(f) = { g | ∃c,d in Nat:∀N in Nat: g(N)≤c* f(N) + d } .

Bemerkung:

• Entsprechend definiert man auch untere Schranken.

• Vertiefung in „Entwurf und Analyse von Algorithmen“

• Meist schreibt man O(N) statt O(λN.N), O(N ) statt O(λN.N ), O(log N) statt O(log), usw.

2 2

Eine Funktion f: Nat

R

+ heißt obere Schranke

Der Zeitaufwand A vom Sortieren durch Auswahl ist

und damit in O(N ); denn mit c= 3 und d = 6 gilt:

A(N) ≤ 3 * N + 6 A(N)≤2*N + 7*N - 62

Wichtige Komplexitätsklassen:

Beispiel: (Bestimmung oberer Schranken)

2 2

Kompl.klasse Bezeichnung Beispiel

O(1) konstant Hashverfahren

O(log N) logarithmisch binäre Suche in Bäumen

O(N) linear sequentielle Suche

O(N * log N) n log n gute Sortierverfahren O(N ) quadratisch einfache Sortierverfahren O(N ) kubisch Matrixmultiplikation O(2 ) exponentiell Optimierungverfahren

2 3 N

Algorithmen mit einem Aufwand in O(N ) , k≥2, nennt man polynomisch oder engl. polynomial.

k

(4)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 375

Diskussion der O-Notation:

• Die O-Notation liefert eine grobe Klassifikation.

• In der Praxis können konstante Faktoren und Programmiersprachen-spezifische Aspekte entscheidend sein (siehe Beispiel unten).

• Weitere Aspekte für die Effizienzbetrachtung:

- Antwortzeiten interaktiver Softwaresysteme - Kommunikationszeiten

Beispiel: (zur obigen Diskussion)

Wir betrachten zwei Versionen eines Programms, das eine Datei liest und wieder ausgibt.

Die Versionen illustrieren insbesondere:

• Abhängigkeit von programmiersprachlichen Aspekten (hier: Komplexität von scheinbaren und wirklichen Grundoperationen)

• Notwendigkeit des Verständnisses technischer Aspekte zur Beurteilung nicht-funktionaler Eigen- schaften

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 376

Abstrakte Schnittstelle zum Lesen von Dateien:

/* Ein Verbund vom Typ DateiLesePosition repräsentiert eine Position in einer Datei.

An dieser Position kann man das nächste Zeichen lesen.

*/

class DateiLesePosition { ...

}

/* Öffnet Datei mit Namendnameund liefert Referenz vom Typ DateiLesePosition, die die Anfangsposition in der Datei repräsentiert.

Gibt es Fehler beim Öffnen, wirdnull zurückgegeben.

*/

DateiLesePosition oeffneDatei( String dname ){

...

}

/* Ist die aktuelle Position am Dateiende, wird das EOT-Zeichen ‘\4‘ geliefert. Andernfalls wird das Zeichen an aktueller Position geliefert und die Position eins weiter geschaltet.

*/

char naechstesZeichen( DateiLesePosition d ) { ...

}

Ineffiziente Programmversion: Datei-Lesen Version 1 public static void main( String[] a ) {

if( arg.length == 1 ) {

DateiLesePosition dlp = oeffneDatei(a[0]);

if( dlp == null ) {

println("Fehler: Datei existiert nicht");

return;

}

String s = "";

char c = naechstesZeichen(dlp);

int count = 1;

while( c != '\4' ){ // '\4' ist EOT s = s + c;

c = naechstesZeichen(dlp);

count++;

if( count%1000==0 ) println(count);

}

println("Datei Inhalt:");

println( s );

} else {

println("Usage: java DateiLesen <file>");

} }

Bemerkung:

Obige Schnittstelle arbeitet nur für Dateien korrekt, die das EOT-Zeichen (end of transmission) nicht enthalten.

public static void main( String[] a ) { if( arg.length == 1 ) {

DateiLesePosition dlp = oeffneDatei(a[0]);

if( dlp == null ) {

println("Fehler: Datei existiert nicht");

return;

}

final int feldgroesse = 100000;

String s = "";

char[] cfeld = new char[feldgroesse];

char c = naechstesZeichen(dlp);

int count = 1;

int index = 0;

while( c != '\4' ){ // '\4' ist EOT cfeld[index] = c;

c = naechstesZeichen(dlp);

count++;

index++;

if( count%1000==0 ) println(count);

if( index == feldgroesse ) { index = 0;

s = s + new String(cfeld);

} }

println("Datei Inhalt:");

println( s );

} else {

println("Usage: java DateiLesen <file>");

} }

Effizientere Programmversion: Datei-Lesen Version 2

(5)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 379

Gemessene Zeit zum Lesen einer 150 kByte großen Datei auf einem AMD Opteron Dual Core 270 Rechner:

V1: 76,1 s V2: 0,4 s Grund:

„+“ auf String kopiert Argumente; dadurch ergibt sich insgesamt für V1 eine quadratische Laufzeit- komplexität in Abhängigkeit von der Seitengröße.

V2 hat zwar theoretisch die gleiche Komplexität, aber diese wirkt sich nur bei sehr großen Dateien aus.

Bemerkung:

Bei der Betrachtung der Komplexität vergisst man leicht, dass sich die Komplexitätsklassen nur auf das asymptotische Verhalten beziehen.

Für kleine N (die man in der Realität oft hat) kann das ganz anders aussehen.

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 380

4.3.2 Speicherverwaltung

Speicher ist eine wichtige Ressource für Softwaresysteme. Viele nicht-funktionale Eigenschaften hängen vom angemessenen Umgang mit Speicher ab.

Wir betrachten grundlegende Aspekte der Speicherverwaltung:

- Einführung: Speicher in der Programmierung - Automatische Speicherbereinigung

- Deallokation von Objekten

Einführung

Wir betrachten hier nur den Speicher, der für die Ausführung von Programmen benötigt wird, und zwar in Form eines Byte-adressierbaren virtuellen Adressraums.

Wichtige Fragen:

- Wofür wird Speicher benötigt?

- Wie ist der Speicher organisiert?

- Wie wird der Speicher verwaltet?

- Wie viel Speicher braucht ein Programm?

Wofür Speicher benötigt wird:

Beispiele: (Verwendung von Speicher)

Speicher wird benötigt für:

- das Programm (unabhängig von Eingabedaten) - Konstanten

- Variablen (global, prozedurlokal, objektlokal) - Verwaltung von Prozedur-/Methodenaufrufen

1. Programm, Konstanten und globale Variable:

Speicherbedarf ist relativ einfach zu bestimmen, da unabhängig von der Eingabe.

String s;

String ss;

public class SpeicherIllustration1 {

public static void main( String[] ins ) { s = ins[0] + " war die Eingabe";

ss = "Zu Seiteneffekten lesen "

+ "Sie die Dokumentation\n"

+ "und fragen Sie Ihren Tutor "

+ "oder Professor";

println( s + "\n" + ss );

} }

2. Verbundkomponenten/Objektlokale Variablen:

Speicherbedarf hängt von der Eingabe ab. Lebens- dauer der Verbunde erstreckt sich von der Erzeugung bis zum Ende des Programmablaufs.

class IntList { int fst;

IntList rst;

}

IntList empty() { return new IntList();

}

IntList append( int i, IntList xl ) { IntList il = new IntList();

il.fst = i;

il.rst = xl;

return il;

}

public class SpeicherIllustration2 {

public static void main( String[] ins ) { IntList il = append(3,empty());

il = append( 134, il );

il = append( 9, il );

int i = Integer.parseInt( ins[0] );

while( i > 0 ) {

il = append( i, il );

i--;

} } }

(6)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 383

3. Lokale Variablen und Prozeduraufrufverwaltung:

Speicherbedarf hängt von der Eingabe ab. Lebens- dauer der lokalen Variablen erstreckt sich vom Prozeduraufruf bis zum Ende der Prozedur- ausführung.

Als Beispiel betrachten wir die rekursive Fassung des Sortierens durch Auswahl (vgl. Folie 370).

Speicher wird benötigt für jede Prozedurinkarnation (vgl. Begriffsklärung auf Folie 321) und zwar

- für den Rückgabewert und die aktuellen Parameter, - für die Aufrufverwaltung (z.B. Aufrufstelle),

- für die lokalen Variablen.

Speicherbereich für eine Prozedurinkarnation:

Rückgabewert Aktuelle Parameter Verwaltungsinformation Lokale Variable

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 384

Speicherorganisation:

Die Speicherorganisation ist bei den meisten prozeduralen bzw. objektorientierten Programmier- sprachen ähnlich:

virtueller Adressraum

BS-Kern

Programm

Halde

Laufzeitkeller

globale Größen globale, statische Variablen, Konstanten, ...

Zwischenergebnisse, prozedurlokale

Größen, Objekte mit be- schränkter Lebensdauer (dynamische) Verbunde, Objekte, ...

Wie Speicher verwaltet wird:

1. Globaler Speicher und Keller werden automatisch verwaltet (der Übersetzer erzeugt dafür Code).

2. Je nach Programmiersprache wird die Halde (engl. heap) unterschiedlich verwaltet:

- mit automatischer Speicherbereinigung, - durch den Programmierer (Deallokation).

Operationen zur Verwaltung der Halde:

Anfordern von Speicher bei Objekterzeugung:

liefere Speicherbereich ausreichender Größe.

Freigabe von Speicher:

- Wenn kein Speicher mehr verfügbar, gebe die Speicherbereiche von Objekten frei, die nicht mehr erreichbar sind.

- Gebe Speicher von Objekten auf Anweisung des Programms frei (Deallokation).

Beachte:

Die Speicherverwaltung kostet auch Laufzeit.

Fazit:

Automatische Speicherbereinigung

1. Wie viel Speicher ein Programm in Abhängigkeit von der Eingabe genau braucht, hängt von Details der Sprachimplementierung und Plattform ab.

2. Mit einem generellen Verständnis der relevanten Techniken lässt sich der Speicherbedarf aber gut abschätzen.

Begriffsklärung: (Autom. Speicherbereinigung)

Verfahren zur automatischen Speicherbereinigung (engl. automatic garbage collection) ermitteln periodisch oder bei Bedarf, welche Objekte nicht mehr erreichbar (s.u.) sind und geben deren Speicherplatz frei.

Weiteres Ziel ist es, den freien Speicher zu kompaktifizieren.

Immer mehr Programmiersprachen bieten

automatische Speicherbereinigung (insbesondere funktionale, logische und objektorientierte Sprachen):

• Vereinfachung der Programmierung

• Aufwand an Speicher und Zeit ist vertretbar.

• Sicherheitsaspekte

(7)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 387

Beispiel: (Autom. Speicherbereinigung)

void main( String[] ins ) { int count = 1;

while( true ) {

int[] feld = new int[1000000];

println(count++);

} }

1. Programm, das unerreichbare Objekte erzeugt:

Dies Programm bekommt keine Speicherprobleme.

2. Programm, dessen erzeugte Objekte erreichbar sind:

Dies Programm terminiert mit OutOfMemoryError.

class ListOfArray { int[] elem;

ListOfArray next;

}

void main( String[] ins ) { ListOfArray la = null;

int count = 1;

while( true ) {

ListOfArray tmp = new ListOfArray();

tmp.elem = new int[1000000];

tmp.next = la;

la = tmp;

println(count++);

} }

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 388

Begriffsklärung: (Erreichbarkeit)

Ein Verbund bzw. Objekt X heißt von einer Variablen v direkt erreichbar, wenn v eine Referenz auf X enthält.

X heißt von v erreichbar, wenn es von v direkt erreichbar ist oder wenn es einen Verbund/ ein Objekt Y mit Komponente w gibt, so dass X von w direkt erreichbar ist und Y von v erreichbar ist.

Die Menge der Wurzelvariablen zu einem Ausfüh- rungszustand A umfasst alle globalen Variablen sowie die aktuell im Keller vorhandenen lokalen Variablen und Parameter.

Ein Objekt heißt erreichbar in einem Ausführungs- zustand A, wenn es von einer Wurzelvariablen zu A erreichbar ist.

Verbunde/Objekte können nur von Wurzelvariablen oder von Verbundkomponenten/Instanzvariablen referenziert werden.

Bemerkung:

Deallokation von Verbunden/Objekten

Begriffsklärung: (De-/Allokation)

Die Bereitstellung des Speicherbereichs bei der Erzeugung von Verbunden und Objekten nennt man Allokation (engl. allocation). Die Freigabe solcher Speicherbereiche Deallokation (engl.

deallocation).

Die meisten prozeduralen Programmiersprachen unterstützen De-/Allokation durch den Programmierer:

• Vorteil:

- ermöglicht effiziente Benutzung von Speicher

• Nachteile:

- zusätzlicher Programmieraufwand - potentielle Fehlerquelle

- führt leicht zu Sicherheitslücken

Wir betrachten hier De-/Allokation von Objekten in C++.

Verbunde und Zeiger in C++:

C++ unterscheidet zwischen Verbunden und und Zeigern/Referenzen auf Verbunde.

#include <iostream>

class Punkt { public:

int x;

int y;

};

Punkt* puenktchen() { Punkt* p = new Punkt();

p->x = 1;

p->y = 2;

Punkt q;

q.x = 3;

q.y = 4;

return p;

}

int main() {

Punkt* r = puenktchen();

cout << "X-Koordinate: " << r->x << '\n';

cout << "Y-Koordinate: " << r->y << '\n';

return 0;

}

Beispiel: (Verbunde und Zeiger)

(8)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 391

Wie in Java, alloziert der Operator „new“ in C++

Speicher für neue Verbunde. Als Ergebnis liefert er einen Zeiger auf den neuen Verbund.

Ist K ein Verbundtyp, dann bezeichnet in C++

K* den Typ der Zeiger auf Verbunde vom Typ K.

Den Speicherplatz, den man mitnewalloziert hat, kann man durch Aufruf des Operatorsdeletewieder freigeben, wenn er nicht mehr gebraucht wird.

// ... wie auf Folie 390 int main() {

Punkt* r = puenktchen();

cout << "X-Koordinate: " << r->x << '\n';

cout << "Y-Koordinate: " << r->y << '\n';

delete r;

return 0;

}

Beispiel: (Verbunde und Zeiger, Fortsetzung)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 392

Zum Vergleich mit Java (s. Folie 387) betrachten wir zwei Varianten eines C++ Programms mit und ohne Deallokation. Sei Klasse Vektor gegeben:

Beispiel: (Wirkung der Deallokation)

class Vektor { public:

int elems [1000000];

};

int main() { while( true ) {

Vektor* vp = new Vektor();

}

return 0;

}

Folgendes Programm führt zu einem Abbruch wegen Speicherüberlaufs:

Deallokation der Vektorverbunde verhindert den Speicherüberlauf:

int main() { while( true ) {

Vektor* vp = new Vektor();

// mache irgendwas mit dem Vektor:

delete vp;

}

return 0;

}

Verwendung von Deallokation:

Ein Programmteil P kann einen Speicherbereich freigeben, wenn:

- P den Speicherbereich nicht mehr benötigt, - P den Speicherbereich kontrolliert, d.h. sicher sein

kann, dass er von keiner anderen Stelle benötigt wird.

Weitere Aspekte der Deallokation:

• Deallokation wird in einigen Sprachen durch weitere Sprachmittel unterstützt (z.B. Destruktoren in C++).

• Deallokation und automatische Speicherbereinigung lassen sich kombinieren:

- Anweisungen an den Garbage Collector - Soft und weak references in Java

• Deallokation bezieht sich nicht nur auf Speicher-, sondern auch auf andere Ressourcen.

• Ein systematischer Umgang mit einer Ressource bedeutet zu klären,

- wer die Ressource kontrollieren soll, - wer Zugriff auf die Ressource erhält.

4.3.3 Laufzeitverhalten

Dieser Abschnitt ergänzt die in 4.3.1 vorgestellten Aspekte zum Laufzeitverhalten.

Das Laufzeitverhalten eines Programms wird bestimmt durch:

- die Anweisungen des Programms - den Übersetzer

- die Laufzeitumgebung, insbesondere die die Speicherverwaltung

- die Systemumgebung.

Speicherverwaltung kostet Zeit:

Speicherverwaltung ist aufwendig, wenn der ver- fügbare Speicher knapp wird:

- Garbage Collector muss häufig aufgerufen werden.

- Das Aufsuchen ausreichend großer freier Speicher- bereiche wird aufwendiger.

(9)

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 395

Systemumgebung beeinflusst das Laufzeitverhalten:

Insgesamt ist der Aufwand in der Praxis nicht leicht abzuschätzen, weil

- der Speicherverbrauch der Bibliotheksklassen und anderer fremder Programmteile häufig nicht klar spezifiziert ist;

- die Details der Speicherverwaltung eine wichtige Rolle spielen.

Problematisch ist das insbesondere bei Echtzeit- anforderungen.

Zur Gesamtbeurteilung des Laufzeitverhaltens eines Softwaresystems muss auch die Systemumgebung berücksichtigt werden:

- Benutzerinteraktion - Anzahl von Benutzern - Kommunikationszeiten

- Laufzeitverhalten der Plattform - Interaktion mit anderen Systemen

13.12.2006 © A. Poetzsch-Heffter, TU Kaiserslautern 396

Fazit:

- Präzise Bestimmung der Effizienz ist im Allg.

schwierig und von vielen technischen Aspekten abhängig; aber auch nur bei ausgewählten Anwendungen nötig.

- Durch geeignete Abstraktion kann man nachvoll- ziehbare Aussagen über die Effizienz eines Algorithmus‘, Programms oder Softwaresystems machen.

Referenzen

ÄHNLICHE DOKUMENTE

Ist eine Oberklasse einer serialisierbaren Klasse nicht serialisierbar, so werden ihre privaten Felder nicht serialisert. Beim Deserialisieren wird der Konstruktor ohne Argumente

Findest du einen Fehler? Dann vergleiche deinen Text mit dieser Vorlage. Findest du jetzt noch Fehler, dann mache eine sorgfältige Berichtigung, damit du die Fehler beim nächsten

Das Ein- und Ausschalten der Rasterfunktionen können Sie direkter auch über Ansicht ► Raster und Ansicht ► Fanglinien, über das Kontextmenü eines Objekts mit den

Abschließend zu 4.3 betrachten wir wichtige Phasen der Algorithmenentwicklung an einem Beispiel... auch Kante für umgekehrte Richtung). - Jede Kante bekommt als Bewertung die Zeit

In 1970er wurde für den Mehrzweckspeicher Gumpen an der Waldnaab (Bayern) eine KNU durchgeführt.. - In einem Einzugsgebiet von einigen

Betrachtet man die Entropie S = S (T, B) als eine Funktion der Temperatur und des angelegten Feldes, so ist offen- sichtlich, dass sich in einem adiabatischen Prozess

Pr ¨adiktion durch zeitliche Modellierung der Bewegung Nachf ¨uhrung der Kamera bei großen Bewegungsradien der Objekte: ”Halte Objekt m ¨oglichst im

Ein nicht zu unter- schätzender positiver Nebeneffekt ist dabei auch, dass Schülerinnen und Schüler, die Nachhilfe erteilen, den bisher durchgenommenen