Seite 1 von 1

Erhebliche Laufzeit

Verfasst: Montag 30. April 2007, 12:20
von driver
Hallo Zusammen,
Ich habe ein Programm geschrieben, das zwar Recht gut funktioniert, aber erhebliche Laufzeit benötigt.
ich würde mich freuen, wenn mir jemand dabei hilft, diese Laufzeit zu minimieren.
Was kann ich anders machen ? was hab ich falsch gemacht ?



Es geht erstmal um parsen von vier Dateien :
lef_datei , def_datei, pl_datei, routed_datei.
In den Dateien befinden sich verschiedene Informationen. Einerseits über die Module(komponenten auf dem Chip : adder, inverter ...) eines Chips, andererseits über die Verbindungen zwischen diesen Modulen.

Ich habe ein paar Module(Python)erstellt :

Sourcen: http://www.ubuntuusers.de/paste/10038/

Danke im Voraus
MfG driver :)

EDIT(jens): zu lange Sourcen ins paste verschoben.

Verfasst: Montag 30. April 2007, 12:57
von jens
Ich hab mal die Sourcen ins paste verschoben, ansonsten hätte man hier nicht mehr antworten können (Bug mit dem Highlighter)...

Also was mir auffällt:
Du brauchst nicht jede Klasse in eine seperaten Datei zu packen. Du kannst alles zusammen in einer Datei lassen :)

Du nutzt einige als "...Array" benannte Klassen, die nur ein dict() Attribut haben. Ich denke es ist schneller, wenn du direkt ein dict() benutzten würdest.

Die ganzen REs müßtest du am besten einmalig mit re.compile() denfinieren und dann benutzten. Das macht es auch ein wenig schneller.

Ich denke der Aufbau der Daten-Dateien ist ungünstig. Wenn du das anders regelst kannst du auf REs verzichten und das ganze mit Pythons String Funktionen erledigen. Das dürfte um einiges schneller sein...

Verfasst: Montag 30. April 2007, 14:21
von BlackJack
Erstmal würde ich `Decimal` rauswerfen. Ich kann mir nicht vorstellen, dass die Programme von denen die Daten stammen, nicht mit der Fliesskommaarithmetik auskommen, die die Hardware bietet.

Dann solltest Du das Parsen der Dateien in einzelne Funktionen aufteilen und mit dem `profile`- und/oder `hotshot`-Modul herausfinden wo am meisten Zeit verbraucht wird.

Mit ungünstig formulierten regulären Ausdrücken kann man sehr lange Laufzeiten hinbekommen.

Ich kenne die Dateiformate nicht, aber es sieht ein bisschen so aus, als wenn Du beim Verarbeiten des Inhalts der letzten Datei wieder Informationen aus der `def_datei` mittels regulärer Ausdrücke heraus holst, die weiter oben schon einmal geparst wurden, oder es vielleicht sollten.

Zum Stil möchte ich mich jens anschliessen: Nicht jede Klasse braucht eine eigene Datei. Insbesondere bei Klassen, die thematisch so dicht zusammenhängen wie im vorliegenden Fall. Damit fallen dann auch die ganzen bösen '*'-Importe weg.

Ausserdem sollte man alle Attribute möglichst in der `__init__()`-Methode an das Objekt binden. Wenn in anderen Methoden noch welche neu entstehen, dann ist das nicht besonders übersichtlich. Nach `__init__()` sollte die Initialisierung nach Möglichkeit wirklich abgeschlossen und das Objekt benutzbar sein.

Die `*Array`-Klassen sind nicht notwendig, da sie keine weiteren Funktionen enthalten und der Suffix 'Array' ist auch nicht so ganz passend. Bei Arrays erwarten die meisten Leute, dass man dort mit einem Index zugreift und nicht mit einem Schlüssel. Wobei hier noch nicht einmal eine Möglichkeit geschaffen wurde auf die Objekte per Index oder Schlüssel zuzugreifen. Langes "durchgreifen" durch Objekte ist im allgemeinen kein guter Stil, weil man auf diese Weise meistens zu viele Annahmen über Objektinterna macht:

Code: Alles auswählen

x = [self.pinarray.pins.get(pin).x for pin in net.pins]
# =>
x = [self.pinarray[pin].x for pin in net.pins]
Im zweiten Fall wäre man unabhängig von der internen Organisation der Pins im `PinArray`.

OOP-technisch gesehen ist auch noch nicht alles so sauber aufgeteilt. Die Entfernung zwischen zwei Punkten wäre zum Beispiel etwas das prima als Methode auf einem Punkt-Objekt passt. Bei der Formel fehlt glaube ich auch noch etwas.

Am Ende einer Funktion oder eines Programms Namen mit ``del`` zu entfernen macht keinen Sinn.

Verfasst: Montag 30. April 2007, 15:23
von driver
Hallo (jens & BlackJack)
Vielen dank für die schnellen, sehr hilfreichen Antworten.
Ich werde mal schauen, dass ich die nötigen Änderungen durchführe.


MfG driver

Verfasst: Montag 30. April 2007, 15:32
von driver
Bei der Formel fehlt glaube ich auch noch etwas.
Die Segmente sind in dem Fall entweder horizontal oder vertikal .

Verfasst: Montag 30. April 2007, 16:22
von joost
Hi, habs nur überflogen. Generell: import * ist echt teuer. GvR wird schon seine Gründe haben, im Tutorial von dessen Benutzung abzuraten.

Viel Erfolg, Joost

Verfasst: Montag 30. April 2007, 17:03
von Leonidas
joost hat geschrieben:Generell: import * ist echt teuer.
Da wäre ich mir jetzt gar nicht so sicher, ob der wirklich einen großen Unterschied macht. Schließlich wird bei jedem Import immer das Gesammte Modul geladen, der unterschied ist nur welche Namen woran gebunden werden.
joost hat geschrieben:GvR wird schon seine Gründe haben, im Tutorial von dessen Benutzung abzuraten.
Ja: 1) Fehlerquelle 2) Unübersichtlich.

Verfasst: Montag 30. April 2007, 17:43
von birkenfeld
Leonidas hat geschrieben:
joost hat geschrieben:Generell: import * ist echt teuer.
Da wäre ich mir jetzt gar nicht so sicher, ob der wirklich einen großen Unterschied macht. Schließlich wird bei jedem Import immer das Gesammte Modul geladen, der unterschied ist nur welche Namen woran gebunden werden.
Es ist nur teuer auf Funktionsebene, weil der Compiler dann nicht weiß, welche Namen lokal und welche global aufgelöst werden sollen.

Auf Modulebene gibt es so gut wie keinen Unterschied (es muss nur ggf. __all__ ausgelesen werden).

Verfasst: Dienstag 1. Mai 2007, 13:03
von driver
hi,
Dann solltest Du das Parsen der Dateien in einzelne Funktionen aufteilen und mit dem `profile`- und/oder `hotshot`-Modul herausfinden wo am meisten Zeit verbraucht wird.
bei decimal.Decimal wird die meiste Zeit verbraucht :(
Erstmal würde ich `Decimal` rauswerfen
dann habe ich bloss eine Zeichenkette !?

Beispiel : aus der lef_datei

Code: Alles auswählen

MACRO mod8
    CLASS CORE ;
    SIZE 1.32 BY 5.04 ;
    ORIGIN 0.00 0.00 ;
    SYMMETRY x y ;
    SITE core ;
    PIN P0
        DIRECTION INOUT ;
        USE SIGNAL ;
        PORT
        LAYER METAL1 ;
        RECT 0.5400 0.0000 0.7800 5.0400 ;
        END
    END P0
END mod8
die Funktion :

Code: Alles auswählen

def load_celldict(lef_datei):
        result = dict()
        for match_cell in re.finditer( r'^MACRO (mod\d+).+?SIZE (\d+\.\d+) BY (\d+\.\d+).+?^END', lef_datei ,  re.DOTALL | re.MULTILINE ):
             cell = Cell(match_cell.group(1),CONV*decimal.Decimal(match_cell.group(2)), CONV*decimal.Decimal(match_cell.group(3)))
             for match_port in re.finditer(r'PIN (P\d+).+?RECT (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) (\d+\.\d+).+?END P\d+',match_cell.group(0), re.DOTALL ):
                    p1= Point(CONV*decimal.Decimal(match_port.group(2)),CONV*decimal.Decimal(match_port.group(3)))
                    p2= Point(CONV*decimal.Decimal(match_port.group(4)),CONV*decimal.Decimal(match_port.group(5)))
                    rect = Rect(p1, p2)
                    port = Port(match_port.group(1),rect)
                    port.center(rect)
                    cell.add_port(port)
             result[cell.name] = cell
        return result
merci


EDIT : ja klar einfach statt decimal -> float nehmen
danke

Verfasst: Mittwoch 2. Mai 2007, 12:23
von driver
ich habe ein paar Sachen geändert.
Laufzeit von etwa 20 auf 8 sec verkürzt.


Über mögliche Verbesserungsvorschläge würde ich mich freuen :D

http://www.ubuntuusers.de/paste/10143/

mfg driver

Verfasst: Mittwoch 2. Mai 2007, 12:51
von jens
Du hast du re.compile Geschichten noch nicht gemacht...

Aus dem hier:

Code: Alles auswählen

for match_cell in re.finditer( r'^MACRO (mod\d+).+?SIZE (\d+\.\d+) BY (\d+\.\d+).+?^END', lef_datei , re.DOTALL | re.MULTILINE ):
würde ungefähr das hier (ungetestet):

Code: Alles auswählen

lef_re = re.compile(r'^MACRO (mod\d+).+?SIZE (\d+\.\d+) BY (\d+\.\d+).+?^END', re.DOTALL | re.MULTILINE )

for match_cell in lef_re.finditer(lef_datei):
Und das mit allen REs... Das ganze lohnt sich besonders dann, wenn die RE innerhalb einer Schleife mehrfach benutzt wird. Wenn diese nicht vorher durch re.compile() "übersetzt" wurde, wird das bei jedem Schleifendurchlauf erledigt und bremst somit...

Verfasst: Mittwoch 2. Mai 2007, 13:44
von driver
servus jens
ich habs probiert :

http://www.ubuntuusers.de/paste/10148/

mit dem Profile bekomme ich für load_celldict(lef_datei):

ohne re.compile :
4954 function calls in 0.072 CPU seconds
....
80 0.001 0.000 0.003 0.000 re.py:171(finditer)
80 0.001 0.000 0.001 0.000 re.py:219(_compile)

mit re.compile :
4720 function calls in 0.067 CPU seconds
....
2 0.000 0.000 0.000 0.000 re.py:178(compile)
2 0.000 0.000 0.000 0.000 re.py:219(_compile)

danke :D

Verfasst: Mittwoch 2. Mai 2007, 19:14
von jens
Was machen denn alle REs aus?

Verfasst: Freitag 4. Mai 2007, 14:12
von driver
jens hat geschrieben:Was machen denn alle REs aus?
mit

Code: Alles auswählen

import time 
start = time.time() 
.....
time.time()-start 
bekomme ich :
mit re.compile : 15.43 sec
ohne re.compile : 17.04 sec