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.
Erhebliche Laufzeit
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
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...
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...
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:
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.
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]
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.
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
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:Generell: import * ist echt teuer.
Ja: 1) Fehlerquelle 2) Unübersichtlich.joost hat geschrieben:GvR wird schon seine Gründe haben, im Tutorial von dessen Benutzung abzuraten.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
- birkenfeld
- Python-Forum Veteran
- Beiträge: 1603
- Registriert: Montag 20. März 2006, 15:29
- Wohnort: Die aufstrebende Universitätsstadt bei München
Es ist nur teuer auf Funktionsebene, weil der Compiler dann nicht weiß, welche Namen lokal und welche global aufgelöst werden sollen.Leonidas hat geschrieben: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:Generell: import * ist echt teuer.
Auf Modulebene gibt es so gut wie keinen Unterschied (es muss nur ggf. __all__ ausgelesen werden).
hi,
Beispiel : aus der lef_datei
die Funktion :
merci
EDIT : ja klar einfach statt decimal -> float nehmen
danke
bei decimal.Decimal wird die meiste Zeit verbrauchtDann 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.
dann habe ich bloss eine Zeichenkette !?Erstmal würde ich `Decimal` rauswerfen
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
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
EDIT : ja klar einfach statt decimal -> float nehmen
danke
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
http://www.ubuntuusers.de/paste/10143/
mfg driver
Laufzeit von etwa 20 auf 8 sec verkürzt.
Über mögliche Verbesserungsvorschläge würde ich mich freuen
http://www.ubuntuusers.de/paste/10143/
mfg driver
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Du hast du re.compile Geschichten noch nicht gemacht...
Aus dem hier:
würde ungefähr das hier (ungetestet):
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...
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 ):
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):
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
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
mitjens hat geschrieben:Was machen denn alle REs aus?
Code: Alles auswählen
import time
start = time.time()
.....
time.time()-start
mit re.compile : 15.43 sec
ohne re.compile : 17.04 sec