Erhebliche Laufzeit

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Erhebliche Laufzeit

Beitragvon driver » Montag 30. April 2007, 12:20

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.
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Montag 30. April 2007, 12:57

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...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Beitragvon BlackJack » Montag 30. April 2007, 14:21

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.
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Beitragvon driver » Montag 30. April 2007, 15:23

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
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Beitragvon driver » Montag 30. April 2007, 15:32

Bei der Formel fehlt glaube ich auch noch etwas.


Die Segmente sind in dem Fall entweder horizontal oder vertikal .
joost
gelöscht
Beiträge: 134
Registriert: Sonntag 29. April 2007, 13:28

Beitragvon joost » Montag 30. April 2007, 16:22

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
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Montag 30. April 2007, 17:03

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.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Beitragvon birkenfeld » Montag 30. April 2007, 17:43

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).
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Beitragvon driver » Dienstag 1. Mai 2007, 13:03

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
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Beitragvon driver » Mittwoch 2. Mai 2007, 12:23

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
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Mittwoch 2. Mai 2007, 12:51

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...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Beitragvon driver » Mittwoch 2. Mai 2007, 13:44

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
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Mittwoch 2. Mai 2007, 19:14

Was machen denn alle REs aus?

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Beitragvon driver » Freitag 4. Mai 2007, 14:12

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

Wer ist online?

Mitglieder in diesem Forum: Bananasplit