Seite 1 von 2

Verfasst: Freitag 12. Januar 2007, 15:07
von sape
cracki hat geschrieben:sape:

du erzeugst eine riesige source datei anstatt die strings zur laufzeit zu erzeugen. klar brauchts laden da unmengen von zeit.
Ich geb es auf. Du hast es nicht begriffen. :roll: -> Kuckst du Sourcecode!
cracki hat geschrieben: deine benchmarks beruhen auf monstroesen formatstrings. wie waers wenn du kleine formatstrings nimmst, dafuer aber mehrere tausend iterationen misst und dann die zeiten vergleichst?
Es geht nicht darum die Interpolatiosvariante gegen die Additionsvariante in mehreren Tausenden Iterationen zu messen, sondern außerhalb einer schleife. Das was du meinst wurde bereits schon gemacht. Es soll lediglich gemessen werden wie lange ein ``return x + y + z + ...`` braucht und wie lange ``return "%s%s%s ... %(x, y, z ...``. Ist der Quelletext den so unverständlich? Sind meine Erklärungen DEN SO unverständlich??
cracki hat geschrieben:
sape hat geschrieben:
cracki hat geschrieben:dazu kommt, dass du mit python nicht die noetige erfahrung hast. du wuesstest also garnicht, wie du zu benchmarkenden code so schreibst, dass du auch das messen kannst was du willst.
Ich glaube das kannst Du nicht beurteilen, wie viel Erfahrung ich habe.
lieg ich denn sooo falsch mit meiner vermutung?
Ja!

Verfasst: Freitag 12. Januar 2007, 15:52
von birkenfeld
blackbird hat geschrieben:
Leonidas hat geschrieben:
birkenfeld hat geschrieben:
cracki hat geschrieben:sape, leider kann ich deine scripts nicht sehen, weil pocoo gerade kaputt ist.
Hu?
Er meint eigentlich LodgeIt. So gibt dieser Link eine ViewDoesNotExist-Exception:
fixed
testcase added

Verfasst: Freitag 12. Januar 2007, 17:22
von cracki
sape:
eine "29 MB" grosse .py datei zu erzeugen ist doch schon ein bisschen albern, nicht?
warum wolltest du die strings nicht zur laufzeit erzeugen? waer viel schneller gegangen.

Verfasst: Samstag 13. Januar 2007, 15:17
von Python 47
@Sape:

Glaub mir ich kenn cracki gut genug um beurteilen zu können das er zu 99% Recht mit seinen Posts hat, um es genau zu sagen, habe ich es noch nie erlebt, das cracki falschen/schlechten Source Code gepostet oder in seiner Antwort falsch lag, in diesem, wie auch in anderen Boards.

Und es ist auch sinnlos, dass du mit cracki diskutierst, da er eh Recht hat.

Und woran er es erkennen kann, das du noch nicht viel Erfahrung mit der Sprach hast? Ganz einfach an deinen geposteten Codes.

Das hier soll kein Post sein, der sich gegen Sape richtet.

Verfasst: Sonntag 14. Januar 2007, 02:24
von sape
Python 47 hat geschrieben: Glaub mir ich kenn cracki gut genug um beurteilen zu können das er zu 99% Recht mit seinen Posts hat,
Hab mir seine bisherigen Post durchgelesen. Ich denke die sprechen für sich.

Und um zu beurteilen ob einer zu 99% immer recht hat, muss man entweder genauso gut oder besser sein. Ich kann mir nicht vorstellen, das du oder ich oder sonstwer beurteilen können, das er in jeden Gebiet immer zu 99% Recht hat. Dazu sind die Interessen zu unterschiedlich. Und selbst wenn ihr die gleiche Interessen habt, müsstest du in genau diesen Interesengebieten genauso (oder gar besser) bescheid Wissen um auf so eine Meinung zu kommen.

Ist das der Fall bei dir oder spricht da nur die pure Sympatie aus dir?
Python 47 hat geschrieben: um es genau zu sagen, habe ich es noch nie erlebt, das cracki falschen/schlechten Source Code gepostet
1. Falschen Sourcecode? -> Nein, habe ich auch noch nicht bei ihm gesehen.
2. Unpassenden Sourcecode -> Ja.
3. Schlechten Sourcecode -> Auch ja.

Davon abgesehen habe ich auch schon schlechten und unpassenden (Weil ich einen Post nicht verstanden habe) Code gepostet. Und mal nebenbei: Hier sind viele Python-Profis die auch schon mal schlechten und unpassenden Code gepostet habe, das sich oft erst gegen ende eines Threads ergeben hat ;) Und ich rede hier wirklich von den richtigen Profis ;) -> Niemand ist unfehlbar. Jeder macht Fehler.

Daher würde ich an deiner stelle nicht in seinen Namen Sprechen, da ich glaube das er selber nicht will, dass du eine Meinung über ihn verbreitest die er selber so nicht sehen will. -> So nach dem Motte: "Ich bin unfehlbar" <- Ich glaube kaum das er selber von sich so anmaßend denkt. :)
Python 47 hat geschrieben: oder in seiner Antwort falsch lag, in diesem, wie auch in anderen Boards.
Ich habe es schon erlebt. Und? Ist ja nichts schlimmes. Dagegen ist keiner gewappnet.
Python 47 hat geschrieben: Und es ist auch sinnlos, dass du mit cracki diskutierst, da er eh Recht hat.
Man beachte das Wort "eh" in deinem Satz.

Kannst du das auch begründen warum er eh Recht hat? Kannst du nachvollziehen warum er recht hat? Oder hat er deiner Meinung nach immer Recht, egal was die anderen Sagen? Bist du nicht ein wenig voreingenommen?

Heißt das, egal was cracki sagt, ich habe nicht das Recht mit ihm zu diskutieren, weil er immer Recht hat (Wie gesagt man beachte das Wort "eh" in deinem Satz)? -> Auch hier würde ich dir empfehlen, nicht in seinem Namen so eine Aussage zu machen, da ich mir beim besten willen nicht vorstellen kann das er möchte das du so einen scheiß über ihm verbreitest. Den ich glaube kaum das cracki sich als einen Menschen sieht der immer (oder ehe ;)) recht hat.
Python 47 hat geschrieben: Und woran er es erkennen kann, das du noch nicht viel Erfahrung mit der Sprach hast? Ganz einfach an deinen geposteten Codes.
Woher weißt du das er genau dass als Grundlage genommen hat? Er hat doch schon angefangen über da Thema zu reden und sich eine "Meinung" gebildet (basierend auf einem Vorurteil), bevor er meinen Code gelesen hat, und nebenbei bemerkt, bevor er meine Post richtig gelesen hatte. Erst später, nach dem er das erledigt hatte, war eine vernümftige Diskussion möglich, da er sich mit den Code und den Fakten auseinander gesetzt hatte. Aber davor, war das nur eine Wertung ohne jeglicher Grundlage (Vorurteil), da er sich nicht die Mühe gemacht hatte vorab erstmal alles zu Analysieren, bevor er eine Behauptung aufstellt (Was sehr unprofessionelle ist.).
Python 47 hat geschrieben: Das hier soll kein Post sein, der sich gegen Sape richtet.

Okay, eigentlich wollte ich folgendes nicht schreiben. Aber da Du nun mit diesem Satz "Subtil" damit anfängst in dem Du extra betonst dass sich dein Post nicht gegen mich richtet, kann ich nur davon ausgehen dass der Post genau das bezwecken soll, da man sowas nicht extra erwähnen braucht, wenn es nicht der Fall ist. = Wenn sich der Post nicht gegen mich richten würde dann bräuchtest du das auch nicht zu erwähnen, was du aber nun gemacht hast.

Du sagst der Post richtet sich nicht gegen mich. Ich sage, doch soll es und ich weiß auch warum ;) Es geht um die Differenz die wir beiden hatten, die vor dem öffnen diese Threads zwischen uns begann. Daher schlage ich dir vor, dass wir unsere Differenzen per PN oder ICQ aus der Welt schaffen und uns nicht auf so ein Niveau herablassen. Was hältst du davon? :) Es kann ja nicht angehen, das du jetzt anfängst in jeden Thread in dem ich poste, etwas zu schreiben das sich gegen mich richtet, nur weil du dich unberechtigterweise von mir angepisst gefühlt hast ;) Und ehrlich, ich habe auch keine Lust drauf da mitzumachen. Aus dem Alter bin ich lägst raus.

Ich könnte dich Ignorieren. Wahrlich, das wäre das einfachste. Da ich aber kein Mensch bin der andere Ignoriert (da ich sowas echt scheiße finde), würde ich lieber erst Probieren unsere Streitigkeiten wie Erwachsene Menschen zu klären.

Daher las uns unseren Disput, in einem privaten klärenden Gespräch, aus der Welt schaffen. Alles andere führt zu nichts.

lg
sape

P.S.: Danke fürs zuhören.

Verfasst: Sonntag 14. Januar 2007, 12:22
von cracki
cracki hat geschrieben:sape:
eine "29 MB" grosse .py datei zu erzeugen ist doch schon ein bisschen albern, nicht?
warum wolltest du die strings nicht zur laufzeit erzeugen? waer viel schneller gegangen.
wenn du mir das sagen koenntest, dann waer schon viel geschafft.

Verfasst: Montag 15. Januar 2007, 10:55
von cracki
nun denn, erklaer doch bitte mal, warum du eine 29MB grosse .py datei brauchst, um ein benchmark zwischen interpolation und + von strings zu testen.

Verfasst: Montag 15. Januar 2007, 11:18
von sape
Hab ich nicht gesagt das ich das brauche. Bei ``MAX_OBJ=1000`` ist ``output.py`` 2.79MB groß. Bei ``MAX_OBJ=100`` 285KB.

Je nach dem wie viele Elemente drin sind um so größer ist die Datei.

Aber mal was grundsätzliches:
Du willst das ja in einer iteratieven Variante testen. Als inerhalb einer Schleife und willst dafür kurze Strings verwenden bzw. nur einen String.

Code: Alles auswählen

str_ = "0" * 80
max_iter = 10000
# Additivevariante
def f1():
    for i in range(max_iter):
        tmp = str_ + str_

#Interpolierendevariante
def f2():
     for i in range(max_iter):
        tmp = "%s%s" % (str_, str_)
Sehe ich das so richtig?

Wenn ja, da ist das was völlig anderes als ich testen wollte. Was ich testen wollte habe ich ja nun zur genüge geschrieben.

Aber für dich nochmal:
Ich will testen wie lange ein return bei z.B. 10.000 Stings in der ``+`` variante dauert und wie lange ein return in der ``%`` Variante dauert. Da das ganze Flexible sein soll (also das ich die Anzahl der Objekte bestimmte kann) habe ich dafür ein script geschriben, das mir die Entsprechen Datei erzeugt.

Also simpel gesagt: Ich will nur wissen wie lange es dauert z.B. 10.000 Strings in der ``+`` Variante zu "verbinden" und in der ``%`` Variante. Mehr nicht.

So, noch was: Das die Scripts noch nicht Optimal sind weiß ich. Das kann man verbessern, aber nicht in dem du mir etwas vorschlägst das was ganz anderes testet.

Wie würdest du das finden?

Code: Alles auswählen

str_ = "blubb"
def f1():
    return str_ + str_ + ... + N

def f2():
    return "%s%s%s ... N %(str_, str_, ... N
So eine Datei müsste natürlich auch automatisch generiert werden, da ich ja die anzahl der Objekte die mit einander verbunde werden sollen bestimmen will. (Daher auch das große N das es andeuten soll).

Damit würde die Liste wegfallen und die ``output.py`` nicht mehr so groß werden. Am Messergebnis würde sich aber nur geringfügig was ändern.

lg

Verfasst: Montag 15. Januar 2007, 11:35
von cracki

Code: Alles auswählen

foostr = "a" * 1000 # oder sonst ein string

def test1(N):
    res = ""
    t = time.clock()
    for i in xrange(N):
        res += foostr
    return time.clock() - t

def test2(N):
    fmtstr = "%s" * N
    args = tuple([foostr] * N)
    t = time.clock()
    fmtstr % args
    return time.clock() - t

N = 5000
print "+ test:", test1(N)
print "% test:", test2(N)
das meine ich
das ding braucht fuer 10000 elemente keine ewigkeit beim laden, und es benchmarkt ebenfalls + und % auf strings.

Verfasst: Montag 15. Januar 2007, 11:40
von sape
cracki hat geschrieben:

Code: Alles auswählen

foostr = "a" * 1000 # oder sonst ein string

def test1(N):
    res = ""
    t = time.clock()
    for i in xrange(N):
        res += foostr
    return time.clock() - t

def test2(N):
    fmtstr = "%s" * N
    t = time.clock()
    fmtstr % tuple([foostr] * N)
    return time.clock() - t

print "+ test:", test1(1000)
print "% test:", test2(1000)
das meine ich
Sorry aber da sich ja echt :lol:. Sage ich doch die ganze Zeit das du nicht verstanden hast was ich machen wollte ;) Du testest da was völlig anderes als ich testen wollte.

BTW: ``test2`` geht noch einigermaßen. Aber ``test1`` ist völlig daneben und macht genau das was ich nicht testen wollte -> Test einer Additivevariante in einer iterativen-Methode.

Nichts für ungut.

lg

Verfasst: Montag 15. Januar 2007, 11:41
von Luzandro
cracki hat geschrieben:

Code: Alles auswählen

foostr = "a" * 1000 # oder sonst ein string

def test1(N):
    res = ""
    t = time.clock()
    for i in xrange(N):
        res += foostr
    return time.clock() - t

def test2(N):
    fmtstr = "%s" * N
    args = tuple([foostr] * N)
    t = time.clock()
    fmtstr % args
    return time.clock() - t

print "+ test:", test1(1000)
print "% test:", test2(1000)
das meine ich
Das ist allerdings tatsächlich etwas anderes, als er gemeint hat - im übrigen kann man für sowas das timeit-modul verwenden, was auch gleich die vielen iterationen erledigt und den GC deaktiviert:

Code: Alles auswählen

import timeit

a = "foo"
b = "bar"
c = "HURZ"

setup = "from __main__ import a,b,c"

print timeit.Timer('a+b+c', setup).timeit()
Edit (Leonidas): Code-Highlighting aktiviert.

Verfasst: Montag 15. Januar 2007, 11:47
von cracki
sape hat geschrieben:Du testest da was völlig anderes als ich testen wollte.

BTW: ``test2`` geht noch einigermaßen. Aber ``test1`` ist völlig daneben und macht genau das was ich nicht testen wollte -> Test einer Additivevariante in einer iterativen-Methode.
und was ist der unterschied, abgesehen davon dass deine variante ein grosses "foo+foo+foo+...+foo" ist und meine dazu eine schleife benutzt?
in beiden varianten wird immer wieder ein neuer string erzeugt, auf den dann wieder addiert wird usw... die laufzeit in beiden varianten wird von der stringerzeugung dominiert.
was soll da anders sein?

Verfasst: Montag 15. Januar 2007, 12:05
von sape
cracki hat geschrieben:
sape hat geschrieben:Du testest da was völlig anderes als ich testen wollte.

BTW: ``test2`` geht noch einigermaßen. Aber ``test1`` ist völlig daneben und macht genau das was ich nicht testen wollte -> Test einer Additivevariante in einer iterativen-Methode.
und was ist der unterschied, abgesehen davon dass deine variante ein grosses "foo+foo+foo+...+foo" ist und meine dazu eine schleife benutzt?
in beiden varianten wird immer wieder ein neuer string erzeugt, auf den dann wieder addiert wird usw... die laufzeit in beiden varianten wird von der stringerzeugung dominiert.
was soll da anders sein?
Naja, Z.B. setzt du das, was du in der ``+`` Variante nutzt, nicht auch in der ``%`` Variante fort, da du in der ``%`` Variante auf eine Schleife verzichtest. Das heißt, das dass Messergebnis immer zu ungenau sein wird, weil bei deiner ``+`` eine schleife genutzt wird, die unnötigen "Overhead" verursacht. Davon mal abgesehen, ist in beiden Funktionen zuviel Code drumherum (Unterschiedlicher Code) der das Messergebnissen nicht genauer macht, sondern im gegenteil sogar mehr verfälscht.

Meine Methode ist zwar nicht die "Speicherschonenste", aber dafür am "genausten". Den die machen genau nur das, was ich ja auch testen wollte und haben nicht unnötigen Code drumherum der das Ergebnis noch mehr verfälscht.

EDIT: Angenommen die ``+`` Variante und ``%`` Variante wären gleich schnell (Fluktuationen mit eingerechnet), dann würde bei deinem Testverfahren die ``+`` Variante immer den Kürzeren ziehen, weil dort mehr "overhead" erzeugt wird.

Verfasst: Montag 15. Januar 2007, 13:05
von BlackJack
cracki hat geschrieben:und was ist der unterschied, abgesehen davon dass deine variante ein grosses "foo+foo+foo+...+foo" ist und meine dazu eine schleife benutzt?
Genau das ist da anders. In Deinem Code ist eine Schleife und die Implementierung der Operatoren ``+`` und ``+=`` ist nicht zwingend die selbe, soll heissen ``a += b`` führt nicht in jedem Fall das gleiche aus wie ``a = a + b``. Wobei bei beiden Schreibweisen für jedes Zwischenergebnis auch eine Zuweisung an `a` durchgeführt wird, die auch Zeit kostet.

Verfasst: Montag 15. Januar 2007, 13:13
von cracki
sape:
du weisst, was laufzeitkomplexitaet bedeutet und wie man diese ermittelt?
also konstant, logarithmisch, linear, polynomial, exponential, faktorial...

blackjack:
__add__ von strings ist doch fuer 2 operanden (links und rechts) definiert. also kann schon mal eine optimierung fuer mehrfache addition nicht wahrscheinlich sein (ich werde jetzt nicht nachschauen, ob python da magic macht und sich ueber string.__add__ hinwegsetzt).
dann waere idealerweise ein `a += b` schneller als ein `a = a + b`, denn dann koennte evtl einfach an a angehaengt werden, wenn der speicher noch alloziiert ist.

wie solls also schneller gehen? a+a+a ist nichts anderes als x += a im loop.

iteration ist auch nix anderes als lineare rekursion, mit dem unterschied dass in primitiven sprachen jede lineare rekursion den stack zum wachsen bringt, was natuerlich genau wie iteration vernachlaessigbar wenig zeit verbraucht.

die laufzeit des loops ist linear und vernachlaessigbar. wenns sein muss kann man das sogar rausrechnen mit nem leeren loop...

Verfasst: Montag 15. Januar 2007, 13:28
von birkenfeld
cracki hat geschrieben: __add__ von strings ist doch fuer 2 operanden (links und rechts) definiert. also kann schon mal eine optimierung fuer mehrfache addition nicht wahrscheinlich sein (ich werde jetzt nicht nachschauen, ob python da magic macht und sich ueber string.__add__ hinwegsetzt).
Das tut es, in gewissem Sinne. Die Addition zweier Strings wird direkt im Evalloop ausgeführt, und nicht erst __add__ gesucht und dann aufgerufen.
Allerdings wird bei a+b+c immer erst a+b gebildet und dann c dazuaddiert (bei der stackbasierten VM wäre das anders auch garnicht möglich).
dann waere idealerweise ein `a += b` schneller als ein `a = a + b`, denn dann koennte evtl einfach an a angehaengt werden, wenn der speicher noch alloziiert ist.
Nein, nicht im allgemeinen, da Strings immutable sind. Das Objekt "a" darf also nicht verändert werden.

Eine Ausnahme gibt es allerdings, wenn das Objekt nur durch diesen einen Namen ("a") referenziert wird. Dann wird kein neues Objekt angelegt, sondern das alte "recyclet".

Verfasst: Montag 15. Januar 2007, 13:42
von cracki
birkenfeld hat geschrieben:Nein, nicht im allgemeinen, da Strings immutable sind.
mist! was man in der hektik so alles links liegen laesst...
jedoch gut zu wissen, dass python recyclen kann (oder koennte).

Verfasst: Montag 15. Januar 2007, 14:25
von sape
@cracki: Dir ging es doch, genauso wie mir, auch darum das die Ergebnisse so "genau" wie möglich sind. Und es ist doch logisch, um so mehr "drumherum" existiert (und dann kommt noch dazu das dass "drumherum" sehr unterschiedlich ist), das sich das auf die Ergebnisse negativ auswirkt.

Z.B.: Sind deine beiden Funktionen was den Speicherverbruach (Größe der Datei, und den Verbrauch im RAM), viel besser als meine Variante. Dagegen sage ich ja auch nichts. :) Da sind wir uns alle einig.

Aber, dennoch ist deine Variante für die Sache die ich messen wollte "unbrauchbar" da zuviel Code drumherum ist, der in beiden Funktionen unterschiedlich ist. Das alles verfälscht das Messergebnis, da die "Streuung" von dem "drumherum" nicht linear ausfällt, weil das "drumherum" in beiden Funktionen von dir unterschiedlich ist.
Z.B., wie schon gesagt, verwendest du eine Schleife für die ``+`` Variante die in deiner ``%`` Variante nicht vorkommt. Dann kommt hinzu das, wie schon von Jack gesagt, eine ``x+=y + z`` in einer Iterierenden Methode nicht das gleiche ist wie eine ``x = y + z``. Und wenn man es ganz genau nimmt, dann mist du eigentlich was ganz anderes, als ich Messen wollte.

Der beste Kompromiss (Wenn es den nun stört das die Datei bei 10.000 Objekten so groß ist) das man den Generator der ``output.py`` so verändert, dass nur noch sowas erzeugt wird:

Code: Alles auswählen

str_ = "blubb"
def f1():
    return str_ + str_ + ... + N

def f2():
    return "%s%s%s ... N %(str_, str_, ... N
Den hier wird dann tatsächlich das gemacht was ich auch messen wollte. Kein drumherum sonder Explizit das was ich wissen will.

Z.B: Sieht ``output.py`` bei ``MAX_OBJ=5`` so aus:

Code: Alles auswählen

str_ = '0' * 80 #Dan eben 80 statt 3000 Zeichen.
def f1():
    return str_ + str_ +  str_ +  str_ +  str_

def f2():
    return "%s%s%s%s%s" % (str_, str_, str_, str_, str_)
Das ist die sicherste Variante, weil es Explizit das macht was ich Messen wollte. Und mehr erwarte ich auch garnicht.

BTW: Ich finde wenn man was Messen will, sollte man dafür sorgen das so wenig "streuende" Elemente wie möglich daran beteiligt sind, damit das Ergebnis so "genau" wie möglich wird.

Und mal ehrlich: Ich weiß nicht warum du da was am Testverfahren, in Bezug auf Festplattenspeicher und RAM, verbessern willst. Ist doch egal. Der Benchmark macht das was er sollte. Ich habe jedenfalls meine Ergebnisse und kann nun ruhiger schlafen da mir diese Frage nicht mehr durch den Kopf geistert ;) :D Nebenbei habe ich auch noch was mitgekriegt, wie Python mit Redundanz umgeht wenn er Bytecode erzeugt.

...

Als nächstes teste ich dann mal die ``+=`` Variante gegen die ``"".join()``-Variante in schleifen. (Ergebnisse kenne wir zwar alle aber denoch will ich das mal selber sehen). Auf die Diskussion bin ich mal gespannt :D --> Soviel kann ich schon verraten. Es wird sich am eigentlichen Verfahren nicht viel ändern. Es wird wider ein Generator benutzt der zwei Funktionen erzeugt die Genau das machen was ich testen will :D Ich werde aber keine riesige Liste mehr generieren lassen, sondern nur einen String verwenden.

lg

Verfasst: Montag 15. Januar 2007, 14:29
von BlackJack
cracki hat geschrieben:wie solls also schneller gehen? a+a+a ist nichts anderes als x += a im loop.
Mit dem Unterschied das da eine Schleife verwaltet werden muss, mit einer Laufvariablen und das die Zwischenergebnisse immer an `x` gebunden werden müssen. Es geht hier nicht um Code-Äquivalenz oder Laufzeit in O-Notation, sondern um *reale* Laufzeit. Die kann man nur für einen bestimmten Quelltext ermitteln und das Ergebnis ist nicht einfach auf Quelltext übertragbar, der das gleiche Ergebnis liefert und die gleiche Laufzeitkomplexität besitzt, aber anders zum Ziel kommt. Bei Laufzeittests sind die Konstanten wichtig die bei O-Notation rausfallen. Genau darum geht es.
iteration ist auch nix anderes als lineare rekursion, mit dem unterschied dass in primitiven sprachen jede lineare rekursion den stack zum wachsen bringt, was natuerlich genau wie iteration vernachlaessigbar wenig zeit verbraucht.

die laufzeit des loops ist linear und vernachlaessigbar. wenns sein muss kann man das sogar rausrechnen mit nem leeren loop...
Bei Laufzeittests gibt es nichts was vernachlässigbar klein ist, bevor man wirklich gemessen hat das es vernachlässigbar klein ist. Wenn man das schon vorher wüsste, bräuchte man den Test nicht. Gerade mit solchen Annahmen kann man Laufzeittests total wertlos machen.

Verfasst: Donnerstag 18. Januar 2007, 17:57
von Luzandro
Nachdem ich deinen Testaufbau auch ein wenig eigenartig gefunden habe, hab ich mir jetzt etwas Zeit genommen, selbst ein paar Tests durchzuführen, insbesondre auch, weil ja schon ein einfaches

Code: Alles auswählen

timeit.Timer('"foo"+"bar"').timeit() #0.374
timeit.Timer('"%s%s" % ("foo","bar")').timeit() #0.925
zeigt, dass dein pauschales Ergebnis "formatstring ist schneller" nicht so ganz stimmen kann.

Meine Ergebnisse: concat ist schnell für wenige Strings, wird aber für mehr Strings ab dem 2stelligen Bereich immer langsamer - bei diesen ist join am schnellsten, natürlich erst recht, wenn die Daten schon in einer Liste stehen. Irgendwo dazwischen befindet sich noch die formatstring-Methode.
Bei weitem am langsamsten (ab einer gewissen Anzahl) ist, wenn man einzelne Listenelemte per Addition verknüpft, also zB foo[0] + foo[1] +...
Klar, warum sollte man sowas tun, aber darum gehts in diesem Thread ja sowieso schon lange nicht mehr und ich habe es hinzugefügt um besser mit sapes Ergebnissen vergleichbar zu bleiben, da er von einer vorhandenen Liste ausgegangen ist. Ein Unterschied ist noch, dass ich nicht lauter gleiche Strings (und damit auch mit gleicher ID) verwende, allerdings hat das keine Auswirkung.
Eigenartig ist irgendwie noch, dass ein join, bei dem beim Aufruf eine Liste generiert wird, bei steigender Anzahl an Strings schneller ist, als wenn dem join ein vorhandenes Tuple übergeben wird..
Number of Strings 2:
0.074 seconds (100.00 %): concat
0.117 seconds (158.16 %): concat list
0.156 seconds (211.14 %): join precalculated list
0.179 seconds (243.31 %): join precalculated tuple
0.191 seconds (258.64 %): formatstring
0.193 seconds (261.66 %): formatstring with precalculated tuple
0.244 seconds (330.71 %): join
0.266 seconds (361.29 %): formatstring with single list elements

Number of Strings 42:
0.754 seconds (100.00 %): join precalculated list
1.090 seconds (144.57 %): join
1.392 seconds (184.66 %): join precalculated tuple
1.669 seconds (221.41 %): formatstring with precalculated tuple
1.671 seconds (221.72 %): formatstring
2.415 seconds (320.40 %): concat
2.871 seconds (380.88 %): formatstring with single list elements
3.216 seconds (426.61 %): concat list
Was bei den verschiedenen Methoden passiert:
concat list/tuple: ['772959', '376396'] ('772959', '376396')

concat list
concat_list[0]+concat_list[1]
772959376396

join
''.join(['772959', '376396'])
772959376396

join precalculated tuple
''.join(concat_tuple)
772959376396

join precalculated list
''.join(concat_list)
772959376396

formatstring
'%s%s' % ('772959', '376396')
772959376396

concat
"772959"+"376396"
772959376396

formatstring with single list elements
'%s%s' % (concat_list[0],concat_list[1])
772959376396

formatstring with precalculated tuple
'%s%s' % concat_tuple
772959376396

Code: Alles auswählen

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import timeit, random

SETUP = "from __main__ import concat_list, concat_tuple"
NUMCALLS = 200000

def generateRandomStrings(numStrings):
    return [ str(random.randint(42, 1234567)) for i in xrange(numStrings) ]

code = {}
first_run = True

for i in xrange(2,40,2):
    print "Number of Strings %d:" % i
    concat_list = generateRandomStrings(i)
    concat_tuple = tuple(concat_list)

    code["concat"] = '+'.join([ '"%s"' % s for s in concat_list])
    code["concat list"] = '+'.join([ 'concat_list[%d]' % j for j in xrange(i) ])
    code["formatstring"] = "'%s' %% %s" % ("%s"*i, concat_tuple)
    code["formatstring with precalculated tuple"] = "'%s' %% concat_tuple" % ("%s"*i)
    code["formatstring with single list elements"] = "'%s' %% %s" % ("%s"*i, "(%s)" % ','.join([ 'concat_list[%d]' % j for j in xrange(i) ]))
    code["join"] = "''.join(%s)" % (concat_list)
    code["join precalculated list"] = "''.join(concat_list)"
    code["join precalculated tuple"] = "''.join(concat_tuple)"

    # on first run display what is going to be evaluated for each method 
    if first_run and i <= 10:
        print "concat list/tuple: ", concat_list, concat_tuple
        print
        for name in code:
            print name
            print code[name]
            print eval(code[name])
            print
        first_run = False
    
    results = sorted([ (timeit.Timer(code[name], SETUP).timeit(NUMCALLS), name) for name in code ])
    fastest = results[0][0]
    for r in results:
        print "%4.03f seconds (%3.02f %%): %s" % (r[0], (r[0]/fastest)*100, r[1]) #ignore ZeroDivisionException...
    print