string interpolation VS. string addition
Verfasst: Donnerstag 11. Januar 2007, 16:03
EDIT: Fehler überarbeitet! Die Interpolationsvariant ist die schneller von beiden!
Vorsetzung vom Thread: http://www.python-forum.de/topic-8713.html
Falls der Thread im Falschen Sub-Forum ist, dann bitte verschieben.
``s = x[0] + x[1] + ...`` VS. ``s = '%s%s... % (x[0], x[1], ...`` - Geschwindigkeitsmessung
Folgendes Script erzeugt ``output.py``das bei `MAX_OBJ=3` so aussieht. Es hat einmal die ``list`` `obj_list` mit 3 Elementen vom Typ ``str``, bei dem jedes Element genau 2890 Chars lang ist. Weiterhin werden die Funktionen ``f1`` und ``f2`` generiert, dass die Elemente von ``obj_list`` unterschiedlich zurückgibt.
Diese Script...
...lädt ``output.py`` und macht einen Benchmark zwischen ``f1`` und ``f2`` um zu sehen welche Variante schneller ist. Das benötigte Modul ``benchmark`` gibt es hier. Den thread dazu ist findet ihr hier.
Dan nun der Ablauf geklärt ist, hier ein par Benchmarks:
Mit 1000 Elementen (`MAX_OBJ=1000`):
Wie man erkenne kann kaum ein Unterschied. Bei mehreren durchläufen ist mal f1 und mal f2 ein wenig schneller
Nebenbei sie angemerkt, dass bei ersten ausführen des Scriptes, das laden von ``output.py`` mit 1000 Elementen ungefähr 2.05 Sekunden dauerte. Nach dem daraus eine ``output.pyc`` erzeugt wurde, beim ersten start, beträgt die Ladedauer ~0.0 Sekunden.
Mit 10.000 Elementen (`MAX_OBJ=10000`):
Jetzt wollen wir mal Python zum schwitzen bringen. Achtet mal darauf wie lange es dauert ``output.py`` beim ersten Start zu laden.
Das laden des unkompilierten ``output.py`` dauerte ~3 Minuten. ``f1`` ~1,85 Minuten und ``f2`` ~3 Minuten. Somit hat sich meine anfängliche Vermutung dass die Interpolationsvariante ("" % ()) schneller ist doch nun als richtig erwiesen
~65% ist schon viel und mehr als ich vermutet habe 
Zweiter durchlaufe, nach dem die ``output.py`` in ``output.pyc`` kompiliert wurde:

...
Kleine Erklärung zu der Startdauer von ``output.py`` bei 10.000 Elementen in ``obj_list``, damit auch die letzten zweifler befridigt sind und nicht denken das meine Scripte "Kaputt sind":
Erstmal bedenkt das jeder string in ``obj_lis`` 2890 Zeichen hat (jeder stinrg ist dabei identisch!)! Dan kommt hinzu das 10.000 strings in ``obj_list`` sind
Das ergibt 2890000 Zeichen, was eine menge ist. Python kompiliert alle Module bei ersten Start immer in ``.pyc``. Dabei werden gleiche Strings (Also mit gleichen Inhalt) erfasst und alle Objekte die auf den gleiche string "Zeigen" werden auf eben diesen referenziert um Speicherplatz zu Sparen
Die ``output.py`` bei 10.000 Elementen ist 27.9MB groß. So eine große Datei zu parsern und dabei alle Mengen der Relation, siehe hier, in eine Menge zusammenzufassen dauert seine Zeit. Also ingrunde werden redundante Mengen zu einer Menge zusammengefast:

BTW: Nochmals Sorry das mir dieser peinliche Fehler mit den vertauschten Labels im Benchmarkscript passiert ist.
BTW2: Ich habe alle Testst noch mit deaktivierten GC gemacht und es war kein unterschied in der Geschwindigkeit auszumachen. Schön
P.S.: Als PC kam ein AMD A64 X2 3800 (DualCore) @ 2GHz pro Core zum Einsatz, wobei nur ein Core genutzt wurde, da Python noch nicht SMP Unterstützung bietet.
Vorsetzung vom Thread: http://www.python-forum.de/topic-8713.html
Falls der Thread im Falschen Sub-Forum ist, dann bitte verschieben.
``s = x[0] + x[1] + ...`` VS. ``s = '%s%s... % (x[0], x[1], ...`` - Geschwindigkeitsmessung
Folgendes Script erzeugt ``output.py``das bei `MAX_OBJ=3` so aussieht. Es hat einmal die ``list`` `obj_list` mit 3 Elementen vom Typ ``str``, bei dem jedes Element genau 2890 Chars lang ist. Weiterhin werden die Funktionen ``f1`` und ``f2`` generiert, dass die Elemente von ``obj_list`` unterschiedlich zurückgibt.
Diese Script...
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from mylib.benchmark import Benchmark
t1 = time.clock()
print "Load ``output.py``",
from output import f1, f2
t2 = time.clock() - t1
print "OK (%.2fsec.)" % t2
print "Start Benchmark\n"
bm = Benchmark()
bm.add_function(f1, "return '%s%s... % (x[0], x[1], ...")
bm.add_function(f2, "return x[0] + x[1] + ... ")
bm.run()
bm.print_ranking()
...lädt ``output.py`` und macht einen Benchmark zwischen ``f1`` und ``f2`` um zu sehen welche Variante schneller ist. Das benötigte Modul ``benchmark`` gibt es hier. Den thread dazu ist findet ihr hier.
Dan nun der Ablauf geklärt ist, hier ein par Benchmarks:
Mit 1000 Elementen (`MAX_OBJ=1000`):
Code: Alles auswählen
Load ``output.py`` OK (0.00sec.)
Start Benchmark
[Die Laufzeit wird ermittelt]
run: f1 - return '%s%s... % (x[0], x[1], ... OK (1.81sec.)
run: f2 - return x[0] + x[1] + ... OK (1.82sec.)
[Ranking wird erzeugt]
Ranking wurde erzeugt.
-------------------------------------------------------------------------------
Funktion: f2 - return x[0] + x[1] + ... ist am langsamsten; Zeit: 1.819344 sec
Funktion: f1 - return '%s%s... % (x[0], x[1], ... ist um 0.70% schneller; Zeit: 1.806714 sec
-------------------------------------------------------------------------------

Mit 10.000 Elementen (`MAX_OBJ=10000`):
Jetzt wollen wir mal Python zum schwitzen bringen. Achtet mal darauf wie lange es dauert ``output.py`` beim ersten Start zu laden.
Code: Alles auswählen
Load ``output.py`` OK (186.24sec.)
Start Benchmark
[Die Laufzeit wird ermittelt]
run: f1 - return '%s%s... % (x[0], x[1], ... OK (111.80sec.)
run: f2 - return x[0] + x[1] + ... OK (183.74sec.)
[Ranking wird erzeugt]
Ranking wurde erzeugt.
-------------------------------------------------------------------------------
Funktion: f2 - return x[0] + x[1] + ... ist am langsamsten; Zeit: 183.744273 sec
Funktion: f1 - return '%s%s... % (x[0], x[1], ... ist um 64.35% schneller; Zeit: 111.797196 sec
-------------------------------------------------------------------------------


Zweiter durchlaufe, nach dem die ``output.py`` in ``output.pyc`` kompiliert wurde:
Code: Alles auswählen
Load ``output.py`` OK (0.01sec.)
Start Benchmark
[Die Laufzeit wird ermittelt]
run: f1 - return '%s%s... % (x[0], x[1], ... OK (111.45sec.)
run: f2 - return x[0] + x[1] + ... OK (184.55sec.)
[Ranking wird erzeugt]
Ranking wurde erzeugt.
-------------------------------------------------------------------------------
Funktion: f2 - return x[0] + x[1] + ... ist am langsamsten; Zeit: 184.550015 sec
Funktion: f1 - return '%s%s... % (x[0], x[1], ... ist um 65.58% schneller; Zeit: 111.454085 sec
-------------------------------------------------------------------------------

...
Kleine Erklärung zu der Startdauer von ``output.py`` bei 10.000 Elementen in ``obj_list``, damit auch die letzten zweifler befridigt sind und nicht denken das meine Scripte "Kaputt sind":
Erstmal bedenkt das jeder string in ``obj_lis`` 2890 Zeichen hat (jeder stinrg ist dabei identisch!)! Dan kommt hinzu das 10.000 strings in ``obj_list`` sind


. Ich hoffe ich interpretiere das richtig als nicht Informatiker. Nach dem das fertig ist, ist die output.pyc nur noch 296KB groß, weil alle redundanten strings beseitigt sind und alle 10.000 Elemente von ``obj_list``nur noch auf den einen String zeigen! Damit sollte geklärt sein warum der zweite start nach dem kompilieren nur noch 0.01sec statt 3 Minuten dauertLALR(k)-Parser hat geschrieben:Im einfachen Worten bedeutet das, dass im zuvor berechneten LR(1)-Automaten Zustände zusammengeführt werden, deren Kern identisch ist. Der Kern zweier Zustände ist identisch, falls die Items der beiden Zustände bis auf die Follow-Mengen (Lookaheads) identisch sind.

BTW: Nochmals Sorry das mir dieser peinliche Fehler mit den vertauschten Labels im Benchmarkscript passiert ist.
BTW2: Ich habe alle Testst noch mit deaktivierten GC gemacht und es war kein unterschied in der Geschwindigkeit auszumachen. Schön

P.S.: Als PC kam ein AMD A64 X2 3800 (DualCore) @ 2GHz pro Core zum Einsatz, wobei nur ein Core genutzt wurde, da Python noch nicht SMP Unterstützung bietet.