Geschwindikeit von Python

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.
Antworten
raf.k
User
Beiträge: 4
Registriert: Sonntag 26. August 2007, 21:18
Wohnort: Aachen
Kontaktdaten:

Hi

Ich programmiere momentan eine kleine Software, und habe mich für Python entschieden, da ich da recht einfach zukünftige Konfigurationsdateien verarbeiten kann. Nun hab ich schon die erste Funktion implementiert:

Code: Alles auswählen

def mod_hgKorrektur(bild, hg, a):
	print "Ziehe "+sys.argv[3]+" von "+sys.argv[2]+" ab"
	tempOut = bild[:18]
	tempHg = 1
	temp = 0
	for i in range(xSize*ySize):
		tempHg=ord(hg[18+i*4])
		if tempHg == 0:
			tempHg = 1
		temp = int(float(ord(bild[18+i*4]))/float(tempHg)*float(a)*255.0) #zeile (1)
		#temp = (ord(bild[18+i*4])*255) #zeile (2)
		if temp > 255:
			temp = 255
		tempOut += struct.pack('B', temp)
		tempOut += struct.pack('B', temp)
		tempOut += struct.pack('B', temp)
		tempOut += struct.pack('B', 255)
	return tempOut
So, Zeile (1) und (2) sind austauschbar, wobei (2) natürlich um einiges schneller läuft.
Jetzt von möglichen Optimierungen abgesehen, ist Python für sowas die richtige wahl, oder wäre da eine Compilersprache deutlich schneller?
Das Ding verarbeitet auf dem mir zur verfügung stehenden Computer 100 pics a 1,6mb in ~5:30min.

Gruss, K

Edit: Könnte mir einer ein Verhältnis der Geschwindikeiten von Python und Compiler sprache noch andeuten? ;)
Benutzeravatar
lutz.horn
User
Beiträge: 205
Registriert: Dienstag 8. November 2005, 12:57
Wohnort: Pforzheim

Woher kommnen denn xSize und ySize?
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Ich denke du wirst mit dem Modul array schon schneller sein.

Code: Alles auswählen

from array import array
def mod_hgKorrektur(bild, hg, a):
    print "Ziehe "+sys.argv[3]+" von "+sys.argv[2]+" ab"
    bild = array('B', bild)
    hg = array('B', hg)
    a = float(a)

    #tempOut = bild[:18]
    tempHg = 1
    temp = 0
    for i in range(xSize*ySize):
        tempHg = hg[18+i*4]
        if tempHg == 0:
            tempHg = 1

        temp = int(float(bild[18+i*4]) / tempHg*a*255) #zeile (1)
        #temp = (bild[18+i*4]*255) #zeile (2)
        if temp > 255:
            temp = 255

        a.extend((temp,temp,temp,255))
    return a.tostring()
so in etwa, keine ahnung was deine Funktion genau macht aber so könnte sie vielleicht das richtige machen und auch schneller sein.

gruss
BlackJack

@raf.k: Beim Geschwindigkeitsverhältnis kommt darauf an was Du machen möchtest. Für solche "number crunching"-Aufgaben ist reines Python natürlich um einiges langsamer als zum Beispiel C. Andererseits gibt es `numpy` mit dem Du hier eventuell etwas herausholen kannst.

Ansonsten kann man an Deinem Quelltext einiges verbessern. Unnötige Funktionsaufrufe weglassen und "statische" Berechnungen nur einmal durchführen. Ich gehe die Funktion mal gnadenlos durch. Sorry. :-)

Der Name `a` für das dritte Argument ist ziemlich nichtssagend. `hg` ist auch etwas kurz.

Die Ausgabe in der ersten Zeile der Funktion greift auf die Kommandozeile zurück. Das wird also auch ausgegeben, wenn man die Funktion mit ganz anderen Werten aufruft als diese Ausgabe vermuten lässt. Schlimmer noch: Man kann die Funktion gar nicht aufrufen, wenn es keine Kommandozeilenargumente gibt, also zum Beispiel um sie isoliert zu testen.

Zeilen 4 und 5 können wegfallen weil den entsprechenden Namen in der Schleife auf jeden Fall etwas zugewiesen wird.

Bei der ``for``-Schleife stellt sich die Frage wo `xSize` und `ySize` herkommen. Die sollten als Argumente übergeben werden.

`range` erzeugt eine Liste mit den ganzen Zahlen. Die brauchen wir hier aber gar nicht, weshalb `xrange` speicherschonender ist. Ausserdem wird mit `i` innerhalb der Schleife zweimal die gleiche Berechnung durchgeführt. Man kann gleich in der Schleife die richtigen Werte erzeugen lassen und spart damit zwei Multiplikationen und eine Addition pro Durchlauf.

Bei der Berechnung (1) sind zu viele explizite `float()`-Aufrufe. Es reicht eine Fliesskommazahl in der Rechnung um das gewünschte Ergebnis zu bekommen.

Die beidem ``if``-Abfragen lassen sich mit `min()` und `max()` ausdrücken.

`struct.pack()` drei mal mit den gleichen Werten aufrufen, in jedem Schleifendurchgang die 255 erneut als Byte verpacken und das ``+=`` für jedes Byte sind Zeitfresser. ``+=`` bedeutet hier: Nimm die alte Zeichenkette, kopiere sie im Speicher und hänge ein Byte an und gib die alte Zeichenkette wieder frei. Das ist sehr viel unnötiges herumschaufeln von Bytes, bloss um eines anzuhängen. Anstelle von `struct.pack()` kann man hier auch einfach das Gegenstück zur `ord()`-Funktion nehmen: `chr()`.

Das alles berücksichtigt könnte die Funktion so aussehen (ungetestet):

Code: Alles auswählen

def mod_hg_korrektur(bild, hg, x_size, y_size, a):
     print 'Ziehe %s von %s ab' % (sys.argv[3], sys.argv[2])
     temp_out = list()
     for i in xrange(18, 18 + x_size * y_size * 4, 4):
         temp_hg = max(1, ord(hg[i]))
         temp = min(255, int(float(ord(bild[i])) / temp_hg * a * 255))  # (1)
         #temp = ord(bild[i]) * 255  # (2)
         temp_out.append(chr(temp) * 3 + '\xff')
     return bild[:18] + ''.join(temp_out)
Wobei die Idee von rayo (`array.array`) oder gar `numpy` das ganze sicher auch noch etwas vereinfachen und letzeres bestimmt schneller ist.
BlackJack

Ungetestet mit `numpy`:

Code: Alles auswählen

from itertools import imap, islice
import numpy

def mod_hg_korrektur(bild, hg, x_size, y_size, a):
    s = slice(18, 18 + x_size * y_size * 4, 4)
    tmp_a = numpy.array(imap(ord, islice(bild, s.start, s.stop, s.step)))
    tmp_b = numpy.fromstring(hg[s], numpy.uint8)
    tmp_b.clip(1, 255, tmp_b)
    tmp_a /= tmp_b
    tmp_a *= (a * 255)
    tmp_a.clip(0, 255, tmp_b)
    return bild[:18] + tmp_b.tostring()
raf.k
User
Beiträge: 4
Registriert: Sonntag 26. August 2007, 21:18
Wohnort: Aachen
Kontaktdaten:

Hi

Danke erstmal für die ausführlichen Antworten

Um mal das ominöse x/ysize aufzulösen: Die sind ganz oben im Programm als statische Variablen definiert. Man kann diese Daten allerdings aus dem Array "Bild" herausholen.

a ist ein zusätlicher Parameter "alpha" der aber bis jetzt noch keine verwendung findet und deshalb weggelassen werden kann (wird er auch in der version mit Zeile (2).

@rayo: ich weiss jetzt nich genau was tostring macht, aber ich schätze mal, aus a=255 wird dann "255" was ich ja nich möchte, blackjack hat da mit chr() schon ganz recht, aber wozu einfach, wenns kompliziert geht.

Also, ich werd mir mal Blackjacks tipps zu Herzen nehmen, und es hoffentlich mal Freitag ausprobieren können.

NumPy werd ich mir auch anschauen, allerdings les ich grad, dass die eigentliche doku kostet. Gibt es dennnoch genug docs im Netz um da durchzusteigen?

Und das Programm selber tut folgendes: Es lädt eine Datei (Bilddaten um genau zu sein) und rechnet auf den Pixeln rum, dh rund 400k shorts in meinem fall die berücksichtigt/berechnet werden müssen und 16mio die geschrieben werden müssen.

Also, nochmal danke für die hilfe, und wahrscheinlich bis Freitag ;)

K
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

tostring() wandlet 255 in "\xFF", also das gleiche wie chr(255), mit array kannst du das ganze hin und her wandeln in einem befehl machen, sollte also einiges schneller sein.

probiers doch einfach aus
BlackJack

@raf.k: Schau mal in den Style Guide. Wenn da `X_SIZE` und `Y_SIZE` gestanden hätte, hätte wohl niemand nachgefragt. ;-)

Die `tostring()`-Methode auf `array.array`-Objekten gibt den Speicher als Zeichenkette zurück. Man kann sich die Objekte als "Ansicht" von einer Bytekette als Array vom angegeben Typ vorstellen. Im vorliegenden Fall wird die Umwandlung ord/chr automatisch beim Zugriff auf die Elemente gemacht und man spart die Funktionsaufrufe.

Code: Alles auswählen

In [333]: a = array.array('B', 'Hallo')

In [334]: a[0]
Out[334]: 72

In [335]: a[0] = 64

In [336]: a.tostring()
Out[336]: '@allo'
Die `numpy`-Doku kostet zwar Geld, aber es gibt Doku für die Vorgänger `numarray` und `numeric`. Da gilt vieles immer noch. Ausserdem hat das Projekt eine Mailingliste mit vielen netten, hilfsbereiten Leuten.
Antworten