Effiziente Programme

19
Effiziente Programme Performanceverbesserung Erhard Dinhobl Margit Weber Martin Reiterer Stefan Reiterer

description

Wie kann ich Programme effizienter gestalten? Kleine Intro

Transcript of Effiziente Programme

Effiziente Programme

Performanceverbesserung

Erhard DinhoblMargit Weber

Martin ReitererStefan Reiterer

Agenda

Schritte zur Performance-Verbesserung:• Herausheben von Schleifeninvarianten• Caching / Memoziation• Binäre Suche• Memory Mapping der passwd Datei• Direct Memory Access (DMA)• Adressberechnungen vermeiden• Schreibeoperationen vermeiden

Resultierendes Programm Performancevergleich

Ursprünglich char *uid2name (int uid) { ... File *passwd = fopen(...); ...} int main () { ... while (...) { uid2name (...); }}

Verbessert File *passwd; char *uid2name (int uid) { ...}int main () { ... passwd = fopen (...); while (...) { uid2name (...); } fclose (passwd);}

Herausheben von Schleifeninvarianten

Dateien öffnen im Normalfall sehr aufwändig! Datei nicht in jedem Schleifendurchgang neu öffnen

Problem:

Herausheben von Schleifeninvarianten

Ursprünglich passwd Datei wurde bei jedem Aufruf von uid2name mittels I/O-Operationen durchsucht

Verbessert Beim 1. Aufruf:passwd Datei komplett einlesen, notwendige Informationen intern cachen und Datei wieder schließen

Ab 2. Aufruf:Ergebnis aus Cache (Liste, Array) zurückliefern.

Caching / Memoziation

I/O-Operationen sehr aufwändig! Datei bei ersten Aufruf von uid2name parsen und in Prozessspeicher chachen

Problem:

Caching / Memoziation

Binäre Suche

Problem: Lineare Suche in interner Datenstruktur (unsortiertes Array) ist sehr aufwändig. Lösungsvorschlag: Binäre Suche in interner Datenstruktur.

Vorteil • Geringerer Suchaufwand, im

Vergleich zu linearer Suche.• Da passwd Datei "fast" sortiert

ist, muss interne Datenstruktur nicht sortiert werden. Lineare Suche für "Ausreißer"

Nachteile • Sortierung der Datenstruktur

notwendig (hoher Sortieraufwand). In unserem Fall war dies zwar nicht notwendig, konnten aber durch die Rechenoperationen (/2, etc.) nicht viel Rechenzeit gewinnen. Weiters können nicht richtig einsortierte Elemente die gesamte Suche als ganzes in vielen Fällen zunichte machen.

Öffnen der Datei - mmap

Bisherige Version

fopen()pro Zeile getline()

Verbessert

open()(char *) mmap()

mmap() liefert Pointer auf char im Speicher, pro Zeile auslesen fällt weg.

Speicher von mmap() verwenden (while-body)

len = map; //aktuelle Pos. im Speicher while(*len != ':') len++; //bis zum ersten : in Zeile gehen old = map; //aktuelle Pos. speichern map = len + 1; //im Speicher bis nach : weitergehen

map += 2; //:x: überspringen

len = map; //aktuelle Pos. nach :x: setzen while(*len != ':') len++; //weiter bis nächsten :

fach = atoi(map); //nach :x: ist UID map = len + 1; //nach UID positionieren filecn[fach] = old; //old zeigt auf UName

- Trotz Speicherverwendung sehr landsam: ~3.1 Mio Iterationen- Speicher von char-Pointern ebenfalls Verwalten- Gesamte Datei im Speicher (!!) mit strcpy realisieren, genauso langsam/schnell

Speicher von mmap() verwenden II

Ursprünglich linearer Suchaufwand zum Durchsuchen der gecachten Daten (Tuppel aus UID und Benutzername)

Verbessert • Stützung von Modulo-Algebra• Einordnung der gecachten Daten 

in Equivalenzklassen (Kongruenz-Klassen)

• Lineare Suche innerhalb dieser Klassen

• 3K Klassen ~ DMA

Direct Memory-Access

Iterativer Suchaufwand von O(n2) Iterative Suche durch (direkten) Speicherzugriff ersetzen

Problem:

mmap - Direct Memory-Access

Adressberechnungen vermeiden

Problem: Da wir die passwd Datei in einer internen Datenstruktur (Array) verwalten, benötigen wir direkten Zugriff auf einzelne Arrayelemente.

Ursprünglich parsen der passwd Datei: Innerhalb einer while Schleife (siehe später) wird auf Elemente des Arrays mittels Index zugegriffen:

entry[index].name = lname

Verbessert  Mittels Pointer auf die Datenstruktur (Array) zugreifen:entry_ptr->name = lname

 Spart Prozessor die Adressberechnung in jedem Schleifendurchgang. Anmerkung: Diese Optimierung können die meisten Compiler von selbst.

Schreibeoperationen vermeiden

Problem: Schreibeoperationen im Speicher sind sehr zeitaufwändig.                 strtok durch strchr und strncpy ersetzen

Ursprünglich strtok wurde zum parsen der passwd Datei verwendet: char *lname = strtok(line, ":");

 strtok schreibt '\0' in den Speicher

Verbessert 1. strchr zur Positionierung im 

Speicher verwendensearchstr = strchr(data, ':');

• strncpy zum Lesen von n Zeichen im Speicher verwendestrncpy(entry_ptr->name, data, searchstr - data);

Schreibeoperationen vermeiden

strcpy/strncpy

    char tmp[STR_SIZE] = {""};    char tmp2[STR_SIZE] = {""};    len = strchr(map, ':') - map; //UName finden    strncpy(tmp2, map, len); //UName herauskopieren    map += len + 1; //:x: überspringen    map = strchr(map, ':') + 1; //:x: überspringen    len = strchr(map, ':') - map; //UID finden    strncpy(tmp, map, len); //UID herauskopieren    fach = atoi(tmp); //UID konvertieren    strcpy(filecn[fach], tmp2); //in HashTable speichern

Gegenüberstellung der OptimierungenZyklen

Gegenüberstellung der OptimierungenSpeicher

Unsere Lösung

 Caching / Memoziation der passwd Einträge Direkter Zugriff auf interne Datenstruktur (Pointer auf einfach verkettete Listen) Vermeidung von Schreibzugriffen auf den Speicher Verwendung eines Pools von Strukturelementen, anstatt malloc für jedes neue Strukturelement. (Listenerstellung) Nur Pointeroperation zum Parsen der passwd Datei