[Im Aufbau] benchmark.py

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.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

[Im Aufbau] benchmark.py

Beitragvon sape » Montag 6. November 2006, 09:43

benchmark.py - Vorangegangener Threadtitel: Probleme mit der Strukturierung eines Benchmarks
---------------------------------------------------------------------------

Hi.

Folgendes sei gegeben:

Code: Alles auswählen

import time

def f1():
   [...]

def f2 ():
   [...]

def f3():
   [...]

# usw.... = bis fn

[...]

t = time.clock()
f1()
a1 = time.clock() - t
print "Laufzeit: %3.2f sec" %a1

t = time.clock()
f1()
a2 = time.clock() - t
print "Laufzeit: %3.2f sec" %a2

t = time.clock()
f1()
a3 = time.clock() - t
print "Laufzeit: %3.2f sec" %a3


Dazu sei angemerkt das die Funktionen f1-fn (in dem Fall oben f1 - f3) alle die gleiche Aufgabe verrichten, aber jeweils anders implementiert sind. Die Anzahl der Funktionen ist aber variabel. Dazu später mehr.

Es geht nun herauszufinden welche Funktion schneller ist. Bis hier hin ja kein Problem wie man an den clock() und prints sieht. Da ich aber nun vorhabe eine Eigene Klasse zu schrieben die die Aufgabe des benchens übernimmt nun folgende Überlegung.


Es sei angenommen, dass der Benchmark Part als Klasse implementiert ist.
Hier mal ein ADT:

Code: Alles auswählen

class Bench:
    def __init__(self):
        # eine liste die referenzen von funktionen enhält.
        self.refOfFunc = []
        # die laufzeit jeder einzelnen funktion
        self.delay = []
   
    # RegisterFunction
    def RegFunc(self, ref):
        self.refOfFunc.append(ref)
   
    def StartBench(self):
        [...]


RegFunc soll dabei als Argument eine Referenz einer Funktion übergeben werden, die gebancht werden soll. Dabei speichert RegFunc dann die Referenz in die liste self.refOfFunc.

Ablauf:
Als erstes werden alle Funktionen die gebencht werden sollen an RegFunc übergeben.
Beispiel:

Code: Alles auswählen

b = Bench()
b.RegFunc(f1) # referenz von funktion f1 übergeben.
b.RegFunc(f2)
b.RegFunc(f3)
[...]


Danach wird StartBench aufgerufen. StartBench ruft nun jede Funktion von der self.refOfFunc Liste auf, misst deren Laufzeit und speichert da sin der Liste self.delay.

Im Abschluss wir danach ausgegeben wie viel Sekunden jede Funktion gebraucht hat für ihre Ausführung. Es ist klar das deren Laufzeit durch den Overhead von StartBench beeinflusst wird. Aber das ist irrelevant, weil nur ersichtlich werden soll, in wie weit sich die Funktionen von der Geschwindigkeit unterscheiden. Es sol ja schließlich herausgefunden werden welche Funktionen den Schnelten Algorithmus implementiert haben. (Es geht darum auch einen Kompromiss zwischen Speicher und Geschwindigkeit zu finden)

So nun das große Problem:
Für mich ist es aber noch wichtig zu wiesen um wie viel Prozent nun eine Funktion schneller ist! (Es geht darum auch einen Kompromiss zwischen Speicher und Geschwindigkeit zu finden) Da ich nun nicht der große Mathematiker bin und dadurch alles zu kompliziert machen würde (oder sogar Falsch), ist meine frage ob mir einer dabei helfen kann.

Dabei geht es nicht nur herauszufinden welche nun die schnellste ist, sondern um Folgendes:
Angenommen f1 ist langsamer als f2 und f3, und f2 ist schneller als f1 und f3: Nun soll berechnet werden um wie viel Prozent f2 schneller ist als f1 und f3. Weiterhin sol auch berechnet werden um wie viel Prozent schneller f3 als f1 ist. Und so weiter.

Für mich scheint das alles in sehr viele if abfragen auszuarten (oder?) und auch weiß ich nicht genau, welches die Simpelste Möglichkeit wäre das zu berechnen. Eigentlich müsste ich der reinfolge nach herausfinden welche nun die langsamsten Funktionen sind (viele if abfragen) und das geordnet speichern. Z.B.: f1 ist am langsamsten, dann kommt f3 und dann kommt f2 die am schnellste ist. Das alles muss herausgefunden werden ohne das der Code zu unübersichtlich wird.

Wie würdet ihr das machen? Oder gibt es vielleicht eine "einfache" Mathematische Formel mit der ich das alles in einen rutsch berechnen kann?

lg
Zuletzt geändert von sape am Donnerstag 9. November 2006, 06:18, insgesamt 4-mal geändert.
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Montag 6. November 2006, 12:40

Wo ist das Problem? Du mist die Zeiten der Implementierungen und vergleichst diese... Vielleicht ist auch die Prozentangaben erstmal egal. Einfach mal die absoluten Zeiten ausgeben und vergleichen. Reicht doch für das erste ;)

Schau mal hier: http://www.python-forum.de/topic-371.html Da haben wir uns lange über ein anderes Problem und die Effizienz unterhalten ;)

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Beitragvon CM » Montag 6. November 2006, 13:23

Wie wäre es mit Pythons timeit-Modul? Prozentrechnung allerdings finde ich so trivial, daß ich darauf nicht näher eingehe .... :wink:

Gruß,
Christian
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Montag 6. November 2006, 13:32

Hi jens.

Das ausgeben der Zeiten ist ja nicht so das Problem ;) Mir geht es aber halt darum zu wissen um wie viel Prozent schneller das ist und das Natürlich für alle Funktionen. Also halt das man weiß wenn z.B f2 schneller ist als f1 und f3, man weis um wie viel Prozent f2 schneller ist als f1 und dann auch um wie viel Prozent schneller als f3. Das gleiche gilt dann auch für f2, das man weiß wie viel schneller es ist als f1 xD Ja ich weiß ich drücke mich heute mal wider eigenartig aus xD

Naja, ich hab ja nicht um sonst f1-fn gewählt ^^. Das n kann ja z.B. für 255 stehen (um mal richtig übertreiben ^^). Das würde bedeuten dass ich dann 255 Funktionen habe! So du kannst dir ja denken wie viel Haufen arbeit das macht einen vergleich zu implementieren, um dann zu wissen wie viel Prozent… :(

Danke für den Link, den werde ich mir mal gleich anschauen.

@CM: Naja du sollst mir ja nicht die Prozentrechnung erklären, das krieg eich ja noch selber hin :P ;) Mir geht es nur darum ob es einen weg gibt den Code so kurz wie möglich zu kriegen und zwar bei den Abhängigkeiten und wie man das auf kürzester art ausrechnen kann xD
Auch dir danke für den Link. Schaue ich mir gleich mal an.

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Montag 6. November 2006, 14:03

Jens ich hab mir den Thread mal durchgelesen. Aber in wie fern soll mir das helfen?
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Beitragvon CM » Montag 6. November 2006, 14:12

... habe ich mich auch gefragt.
Noch was: Beim Link für das timeit-Modul gibt es auch ein Beispiel. Ich glaube wirklich, daß dies die Lösung für Dein Problem sein kann. Wenn nicht, gib' einfach nochmal Bescheid.

Gruß,
Christian
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Montag 6. November 2006, 14:12

Naja, ich weiß nicht ob man sich da überhaupt soviel arbeit machen sollte... Denn oft ist Optimierung überhaupt nicht notwendig!

Wenn du es aber unbedingt machen willst...

Pack doch die Ergebnisse in ein dict und analysiere es... z.B. so:

Code: Alles auswählen

def display_result(result):
    times = result.keys()
    times.sort()

    quickest = times[0]
    for t in times:
        slower = t/quickest * 100
        print "%s: %.2fsec. - %.2f%%" % (result[t], t, slower)


result = {
    1.234: "f1",
    1.532: "f2",
    1.143: "f3",
}

display_result(result)


Ausgabe:
f3: 1.14sec. - 100.00%
f1: 1.23sec. - 107.96%
f2: 1.53sec. - 134.03%


EDIT: Mit dem Link wollte ich nur zeigen, das es nicht notwendig ist irgendwas mit Prozentrechnung zu machen... Es reicht doch einfach nur die Zeiten zu vergleichen...

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

Beitragvon BlackJack » Montag 6. November 2006, 14:20

Habe mal was mit Prozent geschrieben, allerdings ungetestet:

Code: Alles auswählen

from __future__ import division
import time

class Benchmark(object):
    def __init__(self):
        self.functions = list()
   
    def add_callable(self, function, name=None):
        if name is None:
            name = function.__name__
        self.functions.append([None, name])
   
    def run(self, *args, *kwargs):
        for entry in self.functions:
            function = entry[1]
            start_time = time.time()
            function(*args, **kwargs)
            entry[0] = time.time() - start_time
   
    def print_ranking(self):
        functions = sorted(x for x in self.functions if x[0] is not None,
                           reverse=True)
        if functions:
            reference_time = functions[0][0]
            for time, name in functions:
                print '%6.2f%% %s' % ((100 * time) / reference_time,
                                      name)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Montag 6. November 2006, 15:46

Danke ihr beiden :) Das mit den Dictionary und dan sortieren ist eine Super Idee und erspart viele if abfragen :D Da hätte ich auch selber drauf kommen sollen :oops: Bei den Prozent Berechnung habt ihr aber einen "Fehler". Hab das in (100*Referenzzeit/Zeit) - 100 damit mir auch tatsächlich angezeigt wird um wie viel Prozent eine Funktion zur langsamsten angezeigt wird. Nun muss ich nur noch implementieren, das mir die schnellsten auch zu den anderen Funktionen prozentual angezeigt wird. Aber das mache ich ein anderen mal und poste dann die fertige Klasse :p

Hier mein Code:

Code: Alles auswählen

import time

class Benchmark:
    def __init__(self):
        self._refOfFunc = []       
        self.data = []
        refTime = None
       
    def RegFunc(self, ref):
        self._refOfFunc.append(ref)
           
    def StartBench(self):
        for func in self._refOfFunc:
            t1 = time.clock()
            func()
            self.data.append([time.clock() - t1, str(func).split(" ")[1]])
       
        # Array, von langsamste nach schnellste Zeit, sortieren.
        self.data.sort(reverse=True)
       
        # Referenzzeit setzen = Zeit der Langsamsten Funktion. 
        self.refTime = bench.data[0][0]
       
def f1():
    list = [str(i) for i in xrange(100000)]
       
def f2():
    list = []
    for i in xrange(100000):
        list.append(str(i))
   

def f3():
    list = []
    for i in range(100000):
        list.append(str(i))



bench = Benchmark()
bench.RegFunc(f1)
bench.RegFunc(f2)
bench.RegFunc(f3)
bench.StartBench()

data = bench.data
print "Funktion %s ist am langsamste; Zeit %f sec" %(data[0][1], data[0][0])
data.pop(0)

for i in data:
    print ("Funktion %s ist um %.2f%% schneller; Zeit %f sec"
          %(i[1], (100*bench.refTime/i[0]) - 100, i[0]))


Code: Alles auswählen

Funktion f3 ist am langsamste; Zeit 0.143453 sec
Funktion f2 ist um 5.53% schneller; Zeit 0.135932 sec
Funktion f1 ist um 23.94% schneller; Zeit 0.115744 sec


Nun habe ich aber was nicht bedacht: Und zwar kann ja auch eine funktion Argumente haben dun ich weiß nicht wie ich die übermitteln sol.

def f1(arg1): pass
def f2(arg1, arg2): pass

Ne referenz wird ja so geblidet: ref = f1
aber wenn ich ref=f1() mache wird das Ergebnis in ref gespeichert anstatt die Referenz :-[

Nun habe ich mir überlegt die Argumente auch bei RegFunc zu übermitteln. Aber, bei Python ist das anders als bei Perl. In perl muss die Anzahl der Argumenten nicht fesstehen und wird erst in der Funktion ausgewertet. Bei Python geht das nicht oder? Oder gibt es eine Möglichkeit ein Funktion so zu definieren das die Anzahl der übergebene Argumente egal ist?

So was in der art:
def f1 (arg1, ...)

@BlackJack:
Deine Klasse wirft mir in Zeile 13 einen traceback zurück. Und zwar meint Python das *kwargs und *args ein Syntaxfehler ist:

Code: Alles auswählen

def run(self, *args, *kwargs):
                         ^
SyntaxError: invalid syntax


lg
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Montag 6. November 2006, 17:52

XtraNine hat geschrieben:@BlackJack:
Deine Klasse wirft mir in Zeile 13 einen traceback zurück. Und zwar meint Python das *kwargs und *args ein Syntaxfehler ist:

Code: Alles auswählen

def run(self, *args, *kwargs):
                         ^
SyntaxError: invalid syntax

Ich bin zwar nicht BlackJack aber wenn du ein weiteres Sternchen vor '*kwargs' anfügst, also '**kwargs' machst, dann stimmt die Syntax.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Montag 6. November 2006, 18:09

Ups, sorry, aber ich schrob ja das es ungetestet ist. :-)

Die *args und **kwargs sind übrigens die Antwort auf die Frage nach den Argumenten die man mit übergeben kann und womit sich dann die Funktionen aufrufen lassen. In meinem Beispiel kann man die `run()`-Methode mit einem Satz Argumenten ausführen der für alle Funktionen gleich ist. Man kann die aber natürlich auch beim Registrieren der Funktionen für jede Funktion extra übergeben und speichern.

Das könnte man auch wieder in einer Klasse verpacken (natürlich ungetestet):

Code: Alles auswählen

class TimedFunction(object):
    def __init__(self, function, args=(), kwargs={}, name=None):
        self.function = function
        if name is None:
            name = function.__name__
        self.name = name
        self.args = args
        self.kwargs = kwargs
        self.runtime = None
   
    def __cmp__(self, other):
        return cmp(self.runtime, other.runtime)
   
    def is_timed(self):
        return self.runtime is not None
   
    def call(self):
        start_time = time.time()
        self.function(*self.args, **self.kwargs)
        self.runtime = time.time() - start_time


Die Objekte sind vergleichbar, man kann also nach dem man alle in einer Liste gestartet hat, einfach mit `list.sort()` nach Laufzeit sortieren.

Code: Alles auswählen

def f1(a, b, c): pass
def f2(a): pass

tf1 = TimedFunction(f1, (42, 32), {'c': 'spam'})
tf2 = TimedFunktion(f2, ('eggs',))
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Dienstag 7. November 2006, 13:33

Hi, vielen dank.

Bin nun fertig:

Code: Alles auswählen

import time

class Benchmark:
    def __init__(self):
        self._functions = []
        self.data = []
        refTime = 0.0
       
    def AddFunc(self, ref, *args):
        self._functions.append( [ref.__name__, ref, args] )
           
    def Run(self):       
        for name, ref, args in self._functions:
            t1 = time.clock()
            ref(*args)
            self.data.append([time.clock() - t1, name])

        self.data.sort(reverse=True) 
        self.refTime = bench.data[0][0]
       
    def PrintRanking(self):
        data = self.data[:]
        print "Funktion %s ist am langsamste; Zeit %f sec" %(self.data[0][1], self.data[0][0])
        data.pop(0)
       
        for dat in data: 
            print ("Funktion %s ist um %.2f%% schneller; Zeit %f sec"
                   %(dat[1], (100*self.refTime/dat[0]) - 100, dat[0]))
           

if __name__ == '__main__':
    def f1(loops):
        list = [str(i) for i in xrange(loops)]
       
    def f2(loops):
        list = []
        for i in xrange(loops):
            list.append(str(i))

    def f3(loops):
        list = []
        for i in range(loops):
            list.append(str(i))

    loops = 999999
    bench = Benchmark()
    bench.AddFunc(f1, loops)
    bench.AddFunc(f2, loops)
    bench.AddFunc(f3, loops)
    bench.Run()
    bench.PrintRanking()


Docstrings folgen noch, nach dem ich die Klasse noch verbessert habe.

thx & lg
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Dienstag 7. November 2006, 14:28

Hab ein wenig rumgespielt, finde es nicht gut, das man beim testen keine Ausgabe hatte :)

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import time

class Benchmark:
    def __init__(self):
        self._functions = []
        self.data = []
        self.refTime = 0.0

    def AddFunc(self, ref, comment, *args):
        self._functions.append( [ref.__name__, comment, ref, args] )

    def Run(self):
        for name, comment, ref, args in self._functions:
            print "run: %10s - %-10s" % (name, comment),
            t1 = time.clock()
            ref(*args)
            t2 = time.clock() - t1
            self.data.append([t2, comment, name])
            print "OK (%.2fsec.)" % t2

        self.data.sort(reverse=True)
        self.refTime = bench.data[0][0]
        print "-"*80

    def PrintRanking(self):
        print "Funktion: %10s - %-10s ist am langsamste; Zeit %f sec" %(
            self.data[0][1], self.data[0][2], self.data[0][0]
        )
        for t, comment, name in self.data[1:]:
            percent = (100*self.refTime/t) - 100
            print "Funktion: %10s - %-10s ist um %.2f%% schneller; Zeit %f sec" % (
                name, comment, percent, t
            )


if __name__ == '__main__':
    def f1(loops):
        list = [str(i) for i in xrange(loops)]

    def f2(loops):
        list = []
        for i in xrange(loops):
            list.append(str(i))

    def f3(loops):
        list = []
        for i in range(loops):
            list.append(str(i))

    loops = 1000000
    bench = Benchmark()
    bench.AddFunc(f1, "LC", loops)
    bench.AddFunc(f2, "xrange", loops)
    bench.AddFunc(f3, "range", loops)
    bench.Run()
    bench.PrintRanking()




Ausgabe:

Code: Alles auswählen

run:         f1 - LC         OK (1.15sec.)
run:         f2 - xrange     OK (1.34sec.)
run:         f3 - range      OK (1.36sec.)
--------------------------------------------------------------------------------
Funktion:      range - f3         ist am langsamste; Zeit 1.363573 sec
Funktion:         f2 - xrange     ist um 1.51% schneller; Zeit 1.343345 sec
Funktion:         f1 - LC         ist um 18.92% schneller; Zeit 1.146660 sec


Interessant finde ich, das die langsamste f3 die CPU nicht vollständig ausnutzt. Zumindest zeigt der Taskmanager das so an...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Dienstag 7. November 2006, 14:49

Hallo jens :)

Ja stimmt, habe ich nicht dran gedacht bei Run auch ne Ausgabe zu machen, damit man sieht ob sich da was tut.

Die Idee mit dem comment gefällt mir auch gut! :)

Interessant finde ich, das die langsamste f3 die CPU nicht vollständig ausnutzt. Zumindest zeigt der Taskmanager das so an...
hm, muss ich gleich mal kucken.

EDIT: Hab das gerade getestet udn bei mir wird bei jedem Run die CPU voll ausgenutzt. ich mah mal nen Sleep dazwischen um zu sehen ob das nciht nur ein Zufall ist.
Zuletzt geändert von sape am Dienstag 7. November 2006, 14:57, insgesamt 1-mal geändert.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Beitragvon Rebecca » Dienstag 7. November 2006, 14:57

jens hat geschrieben:Interessant finde ich, das die langsamste f3 die CPU nicht vollständig ausnutzt. Zumindest zeigt der Taskmanager das so an...

top zeigt mir an, dass alle drei Funktionen die CPU voll ausnuzten.

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot]