Seite 1 von 1

Performance...

Verfasst: Mittwoch 3. September 2014, 14:15
von jens
Bin bei http://lucumr.pocoo.org/2014/8/16/the-p ... ke-to-see/ beim Thema "Slots" darauf gestoßen, das old style classes von der performance schneller sein sollen, als new style classes...

War mir neu. Dachte ich, teste ich mal in DragonPy... Und in der Tat, mit old style classes bin ich so bei ~1.070.000 cycles/sec und bei new style classes bei ~990.000 cycles/sec
Nicht so furchtbar viel, aber da ich bei class quasi nur das (object) löschen mußte und sonst nicht großartig was ändern muß, warum nicht?

Dann hab ich mich mal gefragt, was macht eigentlich CPython 2 vs. 3 so aus. Soll nicht 3 schneller sein?
Bei meinem Test nicht: Py2.7.8: ~1.080.000 cycles/sec und Py3.4.1: ~860.000 cycles/sec
Allerdings schießt da vielleicht Tkinter quer. Denn beim Fenster verschieben, mit Py3 schnellen die Zahlen kurzzeitig hoch, bei Py2 bleibt es relativ Konstanz...

Ich denke ich werde mal ein Benchmark implementieren, der ohne GUI und ohne Threads auskommt... Dann mal sehen...

Re: Performance...

Verfasst: Mittwoch 3. September 2014, 15:59
von DasIch
jens hat geschrieben:Nicht so furchtbar viel, aber da ich bei class quasi nur das (object) löschen mußte und sonst nicht großartig was ändern muß, warum nicht?
Weil unter anderem Deskriptoren nicht mehr funktionieren, niemand erwartet dass du ernsthaft old-style Klassen nutzt und du dementsprechend einiges an Verwirrung produzieren wirst, möglicherweise auch bei dir selbst. Außerdem ist die Performance nicht so viel höher als dass es sich ernsthaft lohnt. Du cachest ja hoffentlich auch nicht Zugriffe auf Attribute und globale Variablen in lokalen Variablen.

Wenn du willst das DragonPy schnell wird, implementier den Emulator in RPython und lass dir einen JIT und brauchbaren GC generieren. Eine solche Implementation wäre um mehrere Größenordnungen schneller als alles was du in Python implementieren kannst.

Re: Performance...

Verfasst: Mittwoch 3. September 2014, 17:34
von jens
Ich hab nun nicht überall auf Old-Style-Klasse gewechselt, sondern nur bei den paar wichtigen: https://github.com/jedie/DragonPy/compa ... yleClasses

Da nutzte ich auch nichts relevantes, somit ist es IMHO egal ob old oder new style...

Habe mit https://github.com/jedie/DragonPy/commi ... 2f6a333905 einen einfachen 6809 CPU benchmark implementiert und mal alle Tests gemacht:

mit new style Klassen:
CPython 2.7.1: ~870.000 CPU cycles/sec
CPython 3.4.1: ~800.000 CPU cycles/sec
PyPy2 v2.3.1: ~8.500.000 CPU cycles/sec
PyPy3 v2.3.1: ~8.380.000 CPU cycles/sec

mit old style Klassen:
CPython 2.7.1: ~1.000.000 CPU cycles/sec
CPython 3.4.1: ~800.000 CPU cycles/sec
PyPy2 v2.3.1: ~10.500.000 CPU cycles/sec
PyPy3 v2.3.1: ~8.700.000 CPU cycles/sec

Auffällig ist allerdings das CPython 3, egal ob old oder new style Klassen, scheint deutlich mehr Schwankt in den Ergebnissen als alle anderen Varianten.

Der Unterschied bei CPython 2 ist doch schon recht viel. Aber natürlich im Vergleich zu PyPy nix.

Re: Performance...

Verfasst: Mittwoch 3. September 2014, 17:46
von EyDu
Python 3 hat keine old-style-Klassen mehr, die sind alle automatisch new-style ;-)

Re: Performance...

Verfasst: Mittwoch 3. September 2014, 18:11
von jens
EyDu hat geschrieben:Python 3 hat keine old-style-Klassen mehr, die sind alle automatisch new-style ;-)
Das erklärt die Messwerte... D.h. in Python 3 erbt man i.d.R. noch mehr von 'object' ?

Hab mich bisher wenig mit Python 3 beschäftigt.

Re: Performance...

Verfasst: Freitag 5. September 2014, 09:13
von jens
Hab nun mal geschaut ob bytearray() statt einer Liste für das in DragonPy Emulierte RAM/ROM besser wäre.

Hab einfach nur die Initialisierung von RAM/ROM durch bytearray ersetzt. Sonst keine Anpassungen.
Jeweils drei Benchmark Durchgänge (mit --loops 10, PyPy mit 100) gemacht und die besten zwei Werte gemittelt.

list:
CPython 2.7: 895.000 cycles/sec
CPython 3.4: 840.000 cycles/sec
PyPy2: 32,8Mio cycles/sec

bytearray:
CPython 2.7: 893.000 cycles/sec
CPython 3.4: 825.000 cycles/sec
PyPy2: 33,8Mio cycles/sec

Hm. PyPy profitiert ein wenig und CPython ist es ein Tick langsamer?!?

Ich lass da mal die Finger davon... ;)



Hab was anderes gemacht, nach: https://wiki.python.org/moin/PythonSpee ... iding_dots...
z.B.:

Code: Alles auswählen

+        get_and_call_next_op = self.cpu.get_and_call_next_op
         for __ in range(burst_count):
-            self.cpu.get_and_call_next_op()
+            get_and_call_next_op()
Das bringt was (CPython): in der GUI: Vorher: ~840.000 Nachher: ~860.000
Benchmark:
CPython 2.7: vorher: 895.000 cycles/sec - nachher: 918.000 cycles/sec
Bei PyPy schient es nichts, bzw. etwas bremsend zu wirken ?!?

Re: Performance...

Verfasst: Freitag 5. September 2014, 11:00
von BlackJack
@jens: Bei einem Bytearray muss bei jedem Zugriff in CPython ein `int`-Objekt in einen Bytewert bzw. umgekehrt gewandelt werden. Bei PyPy ist das dagegen eher ein kleiner Typhinweis für den JIT-Compiler der an der Stelle vorher wahrscheinlich sowieso schon zumindest C-``int``\s verwendet nach dem er herausgefunden hat das in der Liste nur ganze Zahlen gespeichert werden. So würde ich die Ergebnisse jedenfalls erklären ohne konkret geschaut zu haben was passiert.

Mit anderen Worten: Der Typ `bytearray` ist nicht dazu da um Geschwindigkeit zu erzielen, sondern um Speicherplatz zu sparen und weil er die Buffer-API implementiert zum Austausch mit ”externem” Code, also zum Beispiel schreiben in eine Datei oder Datenaustausch von Binärdaten mit C oder anderem ”nativen” Code.

Und jetzt machst Du genau das was DasIch ja schon in seinem letzten Beitrag befürchtet hat — Attributzugriffe mit explizitem Code cachen. Wenn man bei solchen Mikrooptimierungen angekommen ist, die relativ wenig bringen, aber konsequent angewendet nerviger Boilerplate-Code sind, ist die Zeit gekommen das (Teil)Problem in einer anderen Programmiersprache umzusetzen.

Re: Performance...

Verfasst: Freitag 5. September 2014, 11:35
von jens
BlackJack hat geschrieben:Und jetzt machst Du genau das was DasIch ja schon in seinem letzten Beitrag befürchtet hat — Attributzugriffe mit explizitem Code cachen. Wenn man bei solchen Mikrooptimierungen angekommen ist, die relativ wenig bringen, aber konsequent angewendet nerviger Boilerplate-Code sind, ist die Zeit gekommen das (Teil)Problem in einer anderen Programmiersprache umzusetzen.
Ich hab das jetzt an zwei Stellen gemacht: https://github.com/jedie/DragonPy/commi ... a6e2b87952
Das finde ich noch ok.

Auf andere Programmiersprachen will ich aber nicht setzten ;)

Zumindest auf meinem Rechner bin ich ja schon bei "Echtzeit" auch mit CPython angekommen. Das reicht mir vollkommen. Zumal man halt dann einfach PyPy nehmen kann, wenn man es schneller will ;)

Re: Performance...

Verfasst: Donnerstag 11. September 2014, 09:48
von jens
Statt bytearray käme noch array.array() in Frage.

Hab mal Tests gemacht:

mit einfacher Liste (Dir bisherige Lösung):
CPython 2.7: 910.000 CPU cycles/sec
CPython 3.4: 843.000 CPU cycles/sec
PyPy2 2.3.1: 31.800.000 CPU cycles/sec

mit array.array("B", ...): # unsigned char
CPython 2.7: 903.000 CPU cycles/sec
CPython 3.4: 851.000 CPU cycles/sec
PyPy2 2.3.1: 32.700.000 CPU cycles/sec

mit array.array("H", ...): # unsigned short
CPython 2.7: 902.000 CPU cycles/sec
CPython 3.4: 838.000 CPU cycles/sec
PyPy2 2.3.1: 32.400.000 CPU cycles/sec

mit array.array("I", ...) # unsigned int
CPython 2.7: 778.000 CPU cycles/sec
CPython 3.4: 839.000 CPU cycles/sec
PyPy2 2.3.1: 9.600.000 CPU cycles/sec


(Jeweils der Best-Wert aus mehreren Durchgängen)

Letztlich bringt array auch nur ein wenig Einsparungen im Speicherverbrauch.


EDIT: Hab nun mal auf array.array umgestellt: https://github.com/jedie/DragonPy/commi ... 48d409c26a :D
Wobei es gibt noch wesentlich mehr Potential als das. Denn ich sollte generell nochmal den RAM/ROM Lösung überarbeiten: https://github.com/jedie/DragonPy/issues/1

Re: Performance...

Verfasst: Donnerstag 11. September 2014, 17:00
von jens
jens hat geschrieben:mit array.array("B", ...): # unsigned char
CPython 2.7: 903.000 CPU cycles/sec
CPython 3.4: 851.000 CPU cycles/sec
PyPy2 2.3.1: 32.700.000 CPU cycles/sec
Hab nun doch https://github.com/jedie/DragonPy/issues/1 angefangen: https://github.com/jedie/DragonPy/commi ... lit#diff-7

erste Messungen:
CPython 2.7: 962.000 CPU cycles/sec
CPython 3.4: 877.000 CPU cycles/sec
PyPy2 2.3.1: 25.200.000 CPU cycles/sec

Mir ist schleierhaft, warum die PyPy Werte schlechter werden :K Zumal die GUI Werte was anderes sagen... Wer misst misst Mist?!? :twisted:

Re: Performance...

Verfasst: Montag 15. September 2014, 17:05
von DasIch
Mach einfach mal Bugs in PyPys Bugtracker auf. Möglicherweise hat PyPy einen Bug oder da ist noch Optimierungspotential. Sollte keines davon der Fall sein wirst du dann wahrscheinlich zumindest eine Antwort darauf bekommen, wieso PyPy da langsamer ist.

Re: Performance...

Verfasst: Montag 15. September 2014, 17:19
von jens
Wenn, dann sollte ich vorher einen kleinen timeit test basteln. Ansonsten kann es ja an irgendwas liegen...