Zählschleifen for oder while?

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.
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Zählschleifen for oder while?

Beitragvon Dookie » Freitag 20. Dezember 2002, 18:12

Hallo zusammen,

ich habe mir gerade mal gedanken gemacht, was wohl schneller ist, eine Zählschleife mit while oder mit for?
Um eine Antwort zu bekommen, habe ich folgendes kleines Script geschrieben.

Code: Alles auswählen

#!/usr/binenv python

from time import clock

max = 1000000

print "Teste Whileschleife"
print "i = 0"
print "while i < %d:" % max
print "    pass"
print "    i = i+1"
a = clock()
i = 0
while i < max:
    pass
    i = i+1
   
b = clock()
lzw = b-a
print "Laufzeit = %.3f\n" % lzw

print "Teste Forschleife"
print "for i in range (%d):" % max
print "    pass"
a = clock()
for i in range(max):
    pass
   
b = clock()
lzf = b-a
print "Laufzeit = %.3f\n" % lzf

if lzw > lzf:
    print "Eine Forschleife ist %.2f mal schneller als eine Whileschleife" % (lzw/lzf)
else:
    print "Eine Whileschleife ist %.2f mal schneller als eine Forschleife" % (lzf/lzw)
print


Ich denke mal das Ergebnis spricht für sich, besonders interessant sind auch die Ergebnisse verschiedener Pythonversionen, so ist Python2.2 bei mir (Linux) 10-20% schneller als Python2.1, auch der Unterschied zwischen for und while ist deutlich ausgeprägter.


Gruß


Dookie
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Freitag 20. Dezember 2002, 20:41

Bei mir ergibt sich:
"Eine Forschleife ist 2.38 mal schneller als eine Whileschleife"

Das ist interessant. Aber vmtl. liegt, das daran, daß das Python-range-Objekt in C programmiert ist. Und for-Schleifen auch optimiert für derartige Objekte sind.
Benutzeravatar
hans
User
Beiträge: 728
Registriert: Sonntag 22. September 2002, 08:32
Wohnort: Sauerland
Kontaktdaten:

Beitragvon hans » Freitag 20. Dezember 2002, 22:36

Also auf einem Linuxservechen ergibt sich ein Geschwindigkeitsvorteil von 1,78 bis 1,85 für die for Schleife.

Interessanterweise wird die Anweisung i = i+1 scheinbar geringfügig langsamer ausgeführt als i += 1

Hans
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Samstag 21. Dezember 2002, 00:28

Hallo nochmal,

bei mir ergibt sich mit Python2.1 auch ein Geschwindigkeitsvorteil von 1.7 - 1.8, mit Python 2.2 ist die For-Schleife 2x so schnell und insgesammt ist Python 2.2 nochmal um etwa 10% schneller als 2.1. ich habe i = i+1 gewählt, damit das Script auch mit Pythonversionen <2.x läuft.
Von Vorteil ist bei der While-Schleife der geringere Speicherverbrauch, der aber erst bei sehr großen Werten zum Tragen kommt (bei mir steigt Python bei 10000000 bei der For-Schleife aus).


Gruß

Dookie
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Beitragvon Milan » Samstag 21. Dezember 2002, 11:46

lool... ich hab gerade mal xrange getestet. xrange ist sehr speichersparend, weil es immer erst das nächste Schleifenglied ausrechnet, was gebraucht wird. Also eigentlich wie x+1 bei jedem Aufruf, was ja langsamer sein müsste, dafür aber Zahlen bis zum ende des Arbeitsspeichers in For-Schleifen passieren lassen kann. Und siehe da, komischerweise ist es um nocheinmal 30% schneller. Deswegen sind bei mir for-schleifen auf jeden Fall erste Wahl. Interessant ist auch, das es auch für Dateien ein xreadlines gibt, was man an for-Schleifen übergeben kann. Damit kann man auch große Dateien genauso schnell bearbeitenund muss nich immer eine Zeile mit readline abarbeiten und dann beim nächsten Durchlauf wieder readline nehmen. :lol:
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Samstag 21. Dezember 2002, 13:38

Das ist ja interessant.

Ich hatte gedacht, daß in den neueren Python-Versionen xrange und range das selbe ist. Das alte range macht ja auch nicht viel Sinn -- Oder?

Ihr müßt aber den Test mehrfach ausführen. Oder das Programm so abändern, daß die beiden Zählschleifenversionen abwechselnd mehrfach ausgeführt werden. Die Einzelmessung schienen bei mir einer großen Statistischen Schwankung zu unterliegen.

Ansich könnte man da auch eine schöne Fehlerrechnung machen ...

cu beyond
Benutzeravatar
hans
User
Beiträge: 728
Registriert: Sonntag 22. September 2002, 08:32
Wohnort: Sauerland
Kontaktdaten:

Beitragvon hans » Samstag 21. Dezember 2002, 15:02

Die Einzelmessung schienen bei mir einer großen Statistischen Schwankung zu unterliegen.


Ich denke, da gibt's mehr Unterschiede zu beachten, als auf den ersten Blick so zu vermuten ist.

  • Wie ist die Prozessorauslastung
  • welches OS
  • welche Hintergrundprozesse
  • Prioritäten der Prozesse
  • Speicher
  • RAM-Disk
  • etc., etc, etc.


Während ich getestet habe, lief gerade Mozilla 1.2 und XMMS versorgte mich mit Sound aus MP3. ICQ war glaube ich auch am werkeln. Eigentlich nix besonderes für einen 433 Celeron mit 192 MB RAM, SuSE 8.1 und KDE 3.

Linux hat ja bekanntlich nie genug Speicher. Was "übrig" ist, wird halt zum Cachen verwendet.

Hans
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Beitragvon Milan » Samstag 21. Dezember 2002, 16:33

Beyond hat geschrieben:Das alte range macht ja auch nicht viel Sinn -- Oder?


das range macht vielleicht im Vergleich zu xrange in schleifen weniger sinn, aber range erzeugt im Gegensatz zu xrange eine komplette liste. man kann das also auch zum initialisieren von Variablen nehmen. Überflüssig ist es auf jeden Fall nicht.

xrange erzeugt ein iterierbares Objekt, also speziell für for-schleifen ausgelegt. Da aber wie gesagt für das iterieren nur 1 Element pro Durchlauf benötigt wird, kann da ja am Anfang eines neues Durchlaufes wieder neu erzeugt werden. Ich nehm mal an, dass x-Objekte in C geschrieben sind, weil sonst müssten sie wirklich langsamer als range-schleifen sein, wo ja immer das nächste schon vorliegt.
für kleine Sachen tuts ja auch ein range, man braucht ja nicht gleich mit Kanonen auf Spatzen zu schießen, wenn man es schnell machen will.
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Samstag 21. Dezember 2002, 17:34

:) Wieder mal was neues kennengelernt.

range macht also eine echte -- sprich beschreibbare -- Liste.

>>> r=range(10)
>>> r[2]='Hello'
>>> r
[0, 1, 'Hello', 3, 4, 5, 6, 7, 8, 9]
>>>

OK. Dann müssen beide Versionen (range und xrange) auch erhalten bleiben.

cu beyond
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Samstag 21. Dezember 2002, 17:36

Aufgrund obiger Unsicherheiten wäre ein längerer Prozess beim Messen der Ausführungsgeschwindigkeit sinnvoll ...

cu beyond
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Samstag 21. Dezember 2002, 18:33

Ich hab gerade mal die Suchfunktion von Python.org mit xrange gefüttert, gibt da ein paar sehr interessante Sachen.
Jetzt hab ich mal eine kleine Modifikation bei meinem Script angebracht:

Code: Alles auswählen

#!/usr/binenv python

from time import clock

max = 10000000

def whileloop(n):
    i = 0
    while i < n:
        pass
        i = i+1

def forloop(n):
    for i in xrange(n):
        pass

print "Teste Whileschleife"
print "i = 0"
print "while i < %d:" % max
print "    pass"
print "    i = i+1"
a = clock()
whileloop(max)
   
b = clock()
lzw = b-a
print "Laufzeit = %.3f\n" % lzw

print "Teste Forschleife"
print "for i in xrange (%d):" % max
print "    pass"
a = clock()
forloop(max)
   
b = clock()
lzf = b-a
print "Laufzeit = %.3f\n" % lzf

if lzw > lzf:
    print "Eine Forschleife ist %.2f mal schneller als eine Whileschleife" % (lzw/lzf)
else:
    print "Eine Whileschleife ist %.2f mal schneller als eine Forschleife" % (lzf/lzw)
print


Die Schleifen innerhalb von Funktionen aufzurufen bringt nochmal einen schönen Geschwindigkeitsschub.


Gruß

Dookie
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Samstag 21. Dezember 2002, 20:41

Klar wg. des Lookups. Zuerst wird im lokalen Namespace gesucht. Das ist bei so kurzen Funktionen sehr schnell, da der Namespace fast leer ist.

cu beyond
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Samstag 21. Dezember 2002, 21:33

Ich habe mal das Programm erweitert. Jetzt wird eine Messreihe durchgeführt, um ein besseres Ergebnis zu erhalten. Ich gehe bei der Fehlerrechnung von einer Gaussverteilung der Messwerte bzw des Fehler aus.

Code: Alles auswählen

from time import clock
from math import sqrt

max = 1000000
measurements= 20
student= {   # Student-Funktion/sqrt(n) fuer eine Sicherheit von 68,26 %
       4:    0.6,
       10:   0.34,
       20:   0.23
   }
rtWhileArr= range(measurements)
rtForArr= range(measurements)


for iR in range(measurements):
    print "MESSUNG " + str(iR+1)
   
    print "Teste Whileschleife"
    print "i = 0"
    print "while i < %d:" % max
    print "    pass"
    print "    i = i+1"
    a = clock()
    i = 0
    while i < max:
   pass
   i = i + 1
   
    b = clock()
    lzw = b-a
    print "Laufzeit = %.3f\n" % lzw
    rtWhileArr[iR]= lzw
   
    print "Teste Forschleife"
    print "for i in range (%d):" % max
    print "    pass"
    a = clock()
    for i in range(max):
   pass
   
    b = clock()
    lzf = b-a
    print "Laufzeit = %.3f\n" % lzf
    rtForArr[iR]= lzf


lzw= 0
for lzwR in rtWhileArr:
    lzw= lzw + lzwR
lzw= float(lzw)/float(measurements)
stAbw= 0
for lzwR in rtWhileArr:
    stAbw= stAbw + ((lzwR - lzw)**2)
stAbw= sqrt(float(stAbw)/float(measurements-1))
delta_lzw= stAbw * student[measurements]

lzf= 0
for lzfR in rtForArr:
    lzf= lzf + lzfR
lzf= float(lzf)/float(measurements)
stAbw= 0
for lzfR in rtForArr:
    stAbw= stAbw + ((lzfR - lzf)**2)
stAbw= sqrt(float(stAbw)/float(measurements-1))
delta_lzf= stAbw * student[measurements]


print "Durschnittliche Dauer einer Whileschleife: " + str(lzw) + " pm " + str(delta_lzw) + " Sekunden"
print "Durschnittliche Dauer einer Forschleife: " + str(lzf) + " pm " + str(delta_lzf) + " Sekunden"

if lzw > lzf:
    print "Eine Forschleife ist %.2f mal schneller als eine Whileschleife" % (lzw/lzf)
else:
    print "Eine Whileschleife ist %.2f mal schneller als eine Forschleife" % (lzf/lzw)
print


relError= sqrt( ((delta_lzw/lzw)**2) + ((delta_lzf/lzf)**2) )*100
print "Dieses Ergebnis weicht mit einer Sicherheit von 68,26% nicht mehr als " + str(relError) +"% vom wahren Wert ab"


Auf Optimierungen habe ich bewußt verzichtet, da in "normalen" Programmen for-Schleifen auch nicht in Extra-Funktionen verpackt werden.

Viel Spaß,
beyond
Benutzeravatar
hans
User
Beiträge: 728
Registriert: Sonntag 22. September 2002, 08:32
Wohnort: Sauerland
Kontaktdaten:

Beitragvon hans » Sonntag 22. Dezember 2002, 00:03

Beyond, ich habe mir mal erlaubt in deinem Sourcecode die print Befehle umzubauen. Jetzt kommt die Ausgabe tabellarisch, was die Vergleichbarkeit der Werte leichter macht.

Code: Alles auswählen

from time import clock
from math import sqrt

max = 1000000
measurements= 20
student= {   # Student-Funktion/sqrt(n) fuer eine Sicherheit von 68,26 %
       4:    0.6,
       10:   0.34,
       20:   0.23
   }
rtWhileArr= range(measurements)
rtForArr= range(measurements)

print "\n\n\n\n\n\n\n\nTeste Whileschleife"
print "i = 0"
print "while i < %d:" % max
print "    pass"
print "    i = i+1\n"

print "Teste Forschleife"
print "for i in range (%d):" % max
print "    pass\n\n\n"

print "MESSUNG %3s   %9s   %9s" % ("Nr", "WHILE", "FOR")
for iR in range(measurements):
   a = clock()
   i = 0
   while i < max:
     pass
     i = i + 1
   
   b = clock()
   lzw = b-a
   rtWhileArr[iR]= lzw
   
   a = clock()
   for i in range(max):
     pass
 
   b = clock()
   lzf = b-a
   print "MESSUNG %3d   %9.3f   %9.3f" % (iR+1, lzw, lzf)
   rtForArr[iR]= lzf


lzw= 0
for lzwR in rtWhileArr:
    lzw= lzw + lzwR
lzw= float(lzw)/float(measurements)
stAbw= 0
for lzwR in rtWhileArr:
    stAbw= stAbw + ((lzwR - lzw)**2)
stAbw= sqrt(float(stAbw)/float(measurements-1))
delta_lzw= stAbw * student[measurements]

lzf= 0
for lzfR in rtForArr:
    lzf= lzf + lzfR
lzf= float(lzf)/float(measurements)
stAbw= 0
for lzfR in rtForArr:
    stAbw= stAbw + ((lzfR - lzf)**2)
stAbw= sqrt(float(stAbw)/float(measurements-1))
delta_lzf= stAbw * student[measurements]


print "Durschnittliche Dauer einer Whileschleife: %.3f +/- %.3f sek " % (lzw, delta_lzw)
print "Durschnittliche Dauer einer Forschleife:   %.3f +/- %.3f sek " % (lzf, delta_lzf)

if lzw > lzf:
    print "Eine Forschleife ist %.2f mal schneller als eine Whileschleife" % (lzw/lzf)
else:
    print "Eine Whileschleife ist %.2f mal schneller als eine Forschleife" % (lzf/lzw)
print


relError= sqrt( ((delta_lzw/lzw)**2) + ((delta_lzf/lzf)**2) )*100
print "Dieses Ergebnis weicht mit einer Sicherheit von 68,26%% nicht mehr als %.2f%% vom wahren Wert ab" % relError


Das Ergebnis sieht bei mir dann so aus:

Code: Alles auswählen

Teste Whileschleife
i = 0
while i < 1000000:
    pass
    i = i+1

Teste Forschleife
for i in range (1000000):
    pass



MESSUNG  Nr       WHILE         FOR
MESSUNG   1       4.150       2.250
MESSUNG   2       4.220       2.220
MESSUNG   3       4.000       2.240
MESSUNG   4       4.140       2.350
MESSUNG   5       3.960       2.260
MESSUNG   6       4.060       2.250
MESSUNG   7       4.070       2.240
MESSUNG   8       4.040       2.110
MESSUNG   9       4.100       2.240
MESSUNG  10       4.030       2.190
MESSUNG  11       4.120       2.210
MESSUNG  12       4.060       2.260
MESSUNG  13       4.070       2.260
MESSUNG  14       4.110       2.230
MESSUNG  15       4.080       2.490
MESSUNG  16       3.960       2.200
MESSUNG  17       3.990       2.270
MESSUNG  18       4.200       2.260
MESSUNG  19       4.200       2.390
MESSUNG  20       4.250       2.550
Durschnittliche Dauer einer Whileschleife: 4.090 +/- 0.019 sek
Durschnittliche Dauer einer Forschleife:   2.273 +/- 0.023 sek
Eine Forschleife ist 1.80 mal schneller als eine Whileschleife

Dieses Ergebnis weicht mit einer Sicherheit von 68,26% nicht mehr als 1.13% vom wahren Wert ab
hans@poorbill:~/tmp>


Ich trage meine versionen mal nach:

Code: Alles auswählen

Zur Messung wurde Python "2.2.1 (#1, Sep 10 2002, 17:49:17)
[GCC 3.2]" auf "linux2" benutzt


Hans
Zuletzt geändert von hans am Sonntag 22. Dezember 2002, 13:02, insgesamt 1-mal geändert.
Benutzeravatar
Beyond
User
Beiträge: 227
Registriert: Freitag 6. September 2002, 19:06
Kontaktdaten:

Beitragvon Beyond » Sonntag 22. Dezember 2002, 11:59

Cool.

Ich habe oben noch

Code: Alles auswählen

from sys import version, platform

und unten ein

Code: Alles auswählen

print "Zur Messung wurde Python \"%s\" auf \"%s\" benutzt" % (version, platform)

eingefügt, damit auch gleich die Python-Version und das OS sichtbar sind.

Ich erhalte folgendes Ergebnis:

Code: Alles auswählen

Teste Whileschleife
i = 0
while i < 1000000:
    pass
    i = i+1

Teste Forschleife
for i in range (1000000):
    pass



MESSUNG  Nr       WHILE         FOR
MESSUNG   1       3.720       2.310
MESSUNG   2       3.710       2.140
MESSUNG   3       3.710       2.160
MESSUNG   4       3.710       2.140
MESSUNG   5       3.720       2.150
MESSUNG   6       3.740       2.150
MESSUNG   7       3.710       2.150
MESSUNG   8       3.720       2.130
MESSUNG   9       3.720       2.150
MESSUNG  10       3.710       2.150
MESSUNG  11       3.720       2.150
MESSUNG  12       3.740       2.140
MESSUNG  13       3.730       2.150
MESSUNG  14       3.720       2.140
MESSUNG  15       3.720       2.150
MESSUNG  16       3.710       2.140
MESSUNG  17       3.710       2.150
MESSUNG  18       3.710       2.140
MESSUNG  19       3.720       2.150
MESSUNG  20       3.710       2.140
Durschnittliche Dauer einer Whileschleife: 3.718 +/- 0.002 sek
Durschnittliche Dauer einer Forschleife:   2.154 +/- 0.009 sek
Eine Forschleife ist 1.73 mal schneller als eine Whileschleife

Dieses Ergebnis weicht mit einer Sicherheit von 68,26% nicht mehr als 0.40% vom wahren Wert ab
Zur Messung wurde Python "2.0 (#1, Jan 19 2001, 17:54:27)
[GCC 2.95.2 19991024 (release)]" auf "linux2" benutzt



cu beyond

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder