PyPy 1.5 kann jetzt Python 2.7

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

So lächerlich sind diese Mikrobenchmarks gar nicht. Komplex-Benchmarks spiegeln ja oft nur das wieder was zur Zeit mit Python gemacht wird. Und das ist wiederum stark beeinflusst von der derzeitigen Geschwindigkeit der Standard-Implementierung. Niemand macht derzeit reines number-crunching in Python (in reinen Python,... ich rede nicht von C-Erweiterungen). Das ist aber durchaus eine reale Anwendung und es gäbe keinen Grund sowas nicht in Python zu machen wenn die Geschwindigkeit passen würde. Mikrobenchmarks können also durchaus eine reale Anwendung wiederspiegeln.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

DasIch hat geschrieben:Es stimmt, bis auf wenige Ausnahmen wie lächerliche Microbenchmarks die keine Aussagen für das Verhalten von echten Programmen zulassen. Ich bin davon ausgegangen dass wäre soweit offensichtlich.
Einfach mal zu sagen: "Meine Aussage war so nicht richtig" scheint ein schwerer Brocken zu sein ...
lunar

@numerix: Ist Dein Widerspruch nicht auch ein bisschen kleinkariert? Immerhin zeichnet das PyPy Speed Center doch ein relativ deutliches Bild, und zwar mit praktisch einigermaßen relevanten Messungen. Praktische Relevanz kann man Deinem Beispiel bei allem Respekt nicht zugestehen.

@Darii: Stimmt, an Templates hatte ich nicht gedacht. Problematisch sind allerdings nur nicht spezialisierte Templatesin der öffentlichen API, vollständig spezialisierte Templates haben wiederum eine Binärschnittstelle. Allerdings kommt damit nicht mal bei Qt weit. Qt hat zwar nur wenige nicht spezialisierte Template-Funktionen in der öffentlichen API, die aber sind ziemlich essentiell.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Praktische Relevanz kann man Deinem Beispiel bei allem Respekt nicht zugestehen.
OK, dann sollte man es vielleicht ein wenig umformulieren:

Code: Alles auswählen

>>> timeit("[k**2 for k in xrange(10**6)]",number=3)

CPython:4.9437051524498656
PyPy:12.076731293408159
Das ist eine typische Aufgabe im Bereich wissenschaftliches Rechnen. Und PyPy ist langsamer. Mit kleinkariert hat das nix zu tun. Numerix Aussage stimmt definitiv.
BlackJack

@HerrHagen: Das ist jetzt wirklich praxisrelavanter? Es gibt praxisrelevante wissenschaftliche Programme die nur aus dieser Zeile und nix anderem bestehen? Oder wo diese Zeile wirklich den Kern ausmacht?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier hat jemand vor ziemlich genau einem Jahr mal die Besonderheiten von PyPy in einem Blogeintrag beschrieben: http://alexgaynor.net/2010/may/15/pypy-future-python/. Vieles davon wurde hier ja schon erwähnt.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

BlackJack hat geschrieben:@HerrHagen: Das ist jetzt wirklich praxisrelavanter? Es gibt praxisrelevante wissenschaftliche Programme die nur aus dieser Zeile und nix anderem bestehen? Oder wo diese Zeile wirklich den Kern ausmacht?
Ja. Hier zum Beispiel die Anwendung einer Gammakorrektur auf ein ein zufällig erzeugtes Bild. Der entsprechende Teil besteht hier aus einer Zeile die der obigen zumindest ziemlich ähnlich aussieht.

Code: Alles auswählen

import random
from time import clock

GAMMA = 1.4
image = [random.randint(0, 255) for _ in xrange(1000*1000)]
LUT = [(x/255.)**GAMMA for x in xrange(256)]

t = clock()
image_lut = [LUT[x]*255 for x in image]
print clock() - t

Code: Alles auswählen

C:\Python26>python test.py
0.264631174997
D:\Download\pypy-1.5-win32\pypy-1.5.0a0-win32>pypy test.py
0.333214312653
Vielleicht lässt sich das Beispiel auch entsprechend umformulieren, dass es in PyPy schneller läuft. Aber die Aussage, das jedes Programm ohne Änderungen in PyPy per se schneller läuft ist trotzdem falsch.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Bei dem echten Beispiel was nur wenige Zeilen lang ist haben wir aber nur noch 0.1s Unterschied. Echte Anwendungen sind häufig noch weitaus komplexer wodurch einige Optimierungen die z.B. der JIT macht erst zum Zuge kommen.

Die Programme bei denen Geschwindigkeit wirklich relevant ist laufen üblicherweise wesentlich länger und sind komplexer. Erst dadurch kommen einige Optimierungen zum Zuge. Gerade ein JIT führt schliesslich dazu das bei solchen kleinen Beispielen die Ausführung länger dauert.

Auf der anderen Seite gibt es Microbenchmarks in denen PyPy schneller als C ist, sowas ist ebenfalls nicht sonderlich aussagekräftig.

Betrachtet man aber den Übersetzungsprozess von PyPy selbst, wahrscheinlich eine der komplexesten (Open Source) Python Anwendungen überhaupt läuft der mit PyPy etwa 2x so schnell wie mit CPython.
BlackJack

Bei diesem Beispiel ist dann jetzt aber auch der Unterschied nicht mehr so hoch. Und da würde man in der Praxis wohl auch `numpy` für benutzen können. Wenn das in Zukunft in PyPy verwendet werden kann, ist der Unterschied wahrscheinlich noch kleiner.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

HerrHagen hat geschrieben:Vielleicht lässt sich das Beispiel auch entsprechend umformulieren, dass es in PyPy schneller läuft.
Zumindest lässt es sich schon allein dadurch, dass man den Code in eine Funktion verpackt zugunsten von CPython deutlich beschleunigen. :D
"Schneller" mit pypy wird es, wenn man auf die Vorberechnung und die LC verzichtet ...

Code: Alles auswählen

import random
from time import time

GAMMA = 1.4
image = [random.randint(0, 255) for _ in xrange(1000*1000)]

def f1(): # Original
    LUT = [(x/255.)**GAMMA for x in xrange(256)]
    t = time()
    image_lut = [LUT[x]*255 for x in image]
    return time() - t

def f2(): # pypy "schneller"
    t = time()
    image_lut = []
    for x in image:
        image_lut.append((x/255.)**GAMMA*255)
    return time() - t

print f1(), f2()

Code: Alles auswählen

python2.7 test.py
0.161140918732 0.612797021866

pypy1.5 test.py
0.365215063095 0.538238048553
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

... womit es allerdings um den Faktor 3 langsamer ist als die ursprüngliche CPython Lösung :wink:.
Betrachtet man aber den Übersetzungsprozess von PyPy selbst, wahrscheinlich eine der komplexesten (Open Source) Python Anwendungen überhaupt läuft der mit PyPy etwa 2x so schnell wie mit CPython.
Der Übersetzungsprozess ist sicher ein Musterbeispiel für einen sehr (sehr) komplexen Ablauf. Dort wird sicherlich nicht die komplette Rechenzeit in einer Zeile verbraten. Das heißt jedoch nicht das alle geschwindigkeitskritischen Programme derart komplex sind. Das von mir gezeigte Beispiel ist durchaus realistisch. Programme aus dem Bereich Bildverarbeitung sind tatsächlich meist so gestrickt, dass ein paar wenige Operationen fast die komplette Rechenzeit ausmachen. Im Hintergrund läuft natürlich meist auch eine komplexe Steuerungslogik. Die fällt aber zeitlich gerade nicht ins Gewicht, da hier die meißten Operationen dort nur einmal, statt für jeden Bildpunkt durchgeführt werden. Wenn man einen guten Benchmark will, sollte man also beides drin haben: eine sehr komplexe, verschachtelte Anwendung und eine vergleichsweise einfache Schleife, die dafür mit umso mehr Wiederholungen läuft. Für beide Applikationen existieren reale Anwendungen.

PS: Bitte versteht mich nich falsch. Ich setzte auch sehr große Hoffnungen in pypy. Das Projekt hat riesiges Potential und könnte die Zukunft von Python sein. Ich will bloß, dass bestimmte Anwendungen nicht als unrealistisch abgetan werden, nur weil sie momentan nicht zu den typischen Python-Applikationen gehören.

MFG HerrHagen
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

HerrHagen hat geschrieben:Ich will bloß, dass bestimmte Anwendungen nicht als unrealistisch abgetan werden, nur weil sie momentan nicht zu den typischen Python-Applikationen gehören.
Ja stimmt, ich erwarte bald Kernelmodule in Python schreiben zu können.

SCNR ;) Weil ich mir bei der ganzen Microbenchmark-Diskussion an den Kopf fassen muss.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Das Beispiel

Code: Alles auswählen

    [k * k for k in xrange(10 ** 6)]
entspricht dem folgenden Java-Code:

Code: Alles auswählen

    static Obj example() {
      Obj result = new List();
      Obj iter = new Range(new Int(1000000)).iter();
      Obj k = iter.next();
      while (k != null) {
        result.append(k.mul(k));
        k = iter.next();
      }
      return result;
    }
Natürlich müssen wir noch schnell eine Python-artige Laufzeitumgebung hinzufügen und `Obj`, `Int`, `Range` und `List` implementieren. `Obj` ist die abstrakte Oberklasse für alle Datentypen und stellt, damit ich nicht laufend casten muss, alle benutzten Methoden als Stub zur Verfügung:

Code: Alles auswählen

    static abstract class Obj {
      public Obj next() { throw new UnsupportedOperationException(); }
      public void append(Obj obj) { throw new UnsupportedOperationException(); }
      public Obj mul(Obj other) { throw new UnsupportedOperationException(); }
    }
`List` ist ein Wrapper um `java.util.ArrayList<E>`:

Code: Alles auswählen

    static class List extends Obj {
      private final ArrayList<Obj> values = new ArrayList<Obj>();

      public void append(Obj obj) {
        values.add(obj);
      }
    }
Meine IDE warnt mich an dieser Stelle, dass ich nie auf `values` zugreife. Das ist ein gutes Indiz dafür, dass auch die Java-VM das erkennen könnte und gnadenlos meinen Benchmark wegoptimieren könnte. Wir werden sehen...

Weiter mit `Int`, die im Gegensatz zu Python nur 32-Bit breit sind. Das führt zu falschen Ergebnissen, aber das korrigieren wir später:

Code: Alles auswählen

    static class Int extends Obj {
      private final int value;

      public Int(int value) {
        this.value = value;
      }

      public Obj mul(Obj other) {
        return new Int(value * ((Int) other).value);
      }
    }

Hier ist `Range`, wo ich für den Iterator eine anonyme innere Klasse benutze:

Code: Alles auswählen

    static class Range extends Obj {
      private final int stop;

      public Range(Obj stop) {
        this.stop = ((Int) stop).value;
      }

      public Obj iter() {
        return new Obj() {
          private int index;

          public Obj next() {
            return index < stop ? new Int(index++) : null; 
          }
        };
      }
    }
Lasse ich das Beispiel jetzt wie folgt unter Java 6 laufen, sehe ich eine interessante Zeitverteilung: Es beginnt bei ca. 200ms, geht dann zweimal auf 400ms hoch um letztlich bei 150ms zu landen. Ich tippe darauf, das die längeren Laufzeiten daran liegen, dass hier die JVM den Code kompiliert. Mit Java 7 gehe ich letztlich auf etwa 30ms runter, braucht zwischendurch aber auch mal 300ms. Möglicherweise sind die längeren Durchläufe auch dem GC geschuldet.

Code: Alles auswählen

    public static void main(String[] args) {
      for (int i = 0; i < 10; i++) {
        long t = System.currentTimeMillis();
        example();
        System.out.println(System.currentTimeMillis() - t);
      }
    }
Diese Zeitverteilung ist typisch für JIT-Systeme und macht das Benchmarking so schwer. Ein Durchlauf ist viel zu wenig. Die Laufzeiten sind außerdem viel zu gering. Speziell Java ist darauf getrimmt, langlaufende Prozesse zu optimieren, nicht Code, der wenige Millisekunden lang läuft. Dort macht der JIT in der Regel nicht die Mühe. Wahrscheinlich misst dieser Benchmark die Leistung des GC, denn ich erzeuge immerhin 20.000.001 `Int`-Objekte und diverse `Obj[]` während die `ArrayList`-Objekte wachsen.

Da ich eh eine 64-bit-VM habe, kann ich ohne Performance-Nachteil von `int` auf `long` wechseln, damit mein Programm auch korrekt rechnet.

Was mich allerdings misstrauisch macht ist, dass es keinen Unterschied macht, ob ich die `ArrayList` wachsen lasse oder gleich auf 1.000.000 Elemente initialisiere. Ich hätte gedacht, man würde hier einen Unterschied wahrnehmen können. Wird das Array doch wegoptimiert? Lasse ich mir mal das letzte Element des Array zurückgeben, steigt die typische Laufzeit unter Java 7 auf 40ms. Initialisiere ich jetzt das Array auf 1.000.000 vor, habe ich wieder die alte Laufzeit. Erwischt! Da hat sich also die JVM das speichern meiner `Int` in dem Array und das Array selbst gesparrt, weil ich's nie gebraucht habe.

Ein weiterer Punkt, warum in der Regel gilt, dass Micro-Benchmarks lügen.

Stefan
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Ich kapiers nicht: Wenn ich vor den Funktionsaufrufen oben eine Schleife setzte und die Funktion hunderte male aufrufen lasse ändert sich an der Geschwindigkeit auch nicht wirklich was:

Code: Alles auswählen

...
for _ in range(10000): 
    print f1(), f2()

Code: Alles auswählen

0.374000072479 0.611999988556
0.389999866486 0.611999988556
0.384999990463 0.586999893188
0.388999938965 0.601999998093
0.352999925613 0.611000061035
0.385999917984 0.621999979019
0.381999969482 0.586000204086
0.453000068665 0.598999977112
0.352999925613 0.614000082016
0.384000062943 0.604000091553
0.361000061035 0.674000024796
0.37700009346 0.582000017166
...
0.379999876022 0.584000110626
Bitte erklärt mir jemand (ernsthaft) was nun an dem Beispiel unrealistisch ist und warum man sich dabei an den Kopf fassen muss.

EDIT: Zumindest smas Ausführungen hab ich nun entnehmen können, das in diesem Beispiel zumindest etwas mit den gefilterten Bilddaten gemacht werden sollte, da sonst der ganze oder Teile der Funktion einfach weg optimiert werden könnten.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich verstehe das ehrlich gesagt auch nicht. Es wurde doch nur gezeigt, dass es noch Bereiche gibt, wo CPython schneller als PyPy ist. Wie praxisrelevant das Ganze ist, spielt doch für die Aussage "PyPy ist zumindest in Fall X noch langsamer als CPython" gar keine Rolle. Niemand hat PyPy damit vorgeworfen, dass es nicht ausgereift sei, nicht benutzt werden sollte oder ähnliches. Die Tauglichkeit von Mikrobenchmarks, Hintergründe über mögliche Optimierungen, usw ändern in dem Moment nichts an der Tatsache, dass die gezeigte Codezeile nun mal belegbar langsamer ausgeführt wird. Wie gesagt, inwiefern so etwas wirklich erhellend sein kann, ist eine andere Sache. Wenn die Geschwindigkeit zur Laufzeit tatsächlich eine Rolle spielt, würde ich ohnehin eher zu Cython raten. Die Zielsetzung von PyPy ist ja primär eine andere. Mal abgesehen davon, dass es in Sachen Multimedia bekanntermaßen üblich ist, dass man eher in C implementierte Module zur Unterstützung benutzt als dass man so etwas in purem Python implementiert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

snafu hat geschrieben:Wenn die Geschwindigkeit zur Laufzeit tatsächlich eine Rolle spielt, würde ich ohnehin eher zu Cython raten. Die Zielsetzung von PyPy ist ja primär eine andere.
Nein?! Welche andere Zielsetzung sollte PyPy den haben? Sicherlich hat es auch andere Ziele als CPython aber bessere Performance (und geringerer Speicherverbrauch) sind definitiv Ziele auf die sich die PyPy Leute konzentrieren.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Erstmal sprach ich nicht von geringerem Speicherverbrauch, sondern vom Speed und außerdem bitte ich, das Wort "primär" zu beachten. PyPy hat, wie bereits gezeigt/verlinkt, nicht ausschließlich zum Ziel, die schnellste Python-Implementierung on earth zu sein. Wenn man bei seinem Programm den Fokus auf Geschwindigkeit setzt und mit Drittbibliotheken keine befriedigenden Ergebnisse erreicht, dann sollte man IMHO weiterhin Cython nutzen. Vielleicht ändert sich das ja irgendwann, aber derzeit ist Cython schneller als PyPy, oder irre ich mich da?
BlackJack

@snafu: Ui, wir sollten doch gelernt haben, dass alle verallgemeinernde Aussagen Böse™ sind. ;-) Im PyPy-Blog war mal ein Beispiel für JIT-Code der schneller war als eine C-Umsetzung, demnach gibt es auch mindestens ein Beispiel bei dem von Cython generierter Code wohl auch langsamer ist. :-)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

chr(); chr(); chr()
Nebelhom
User
Beiträge: 155
Registriert: Mittwoch 19. Mai 2010, 01:31

Ich finde es immer sehr erfrischend, wie eine harmlose Frage so viel Zuendstoff fuer hitzige Diskussionen liefern kann in diesem Foum (metadiscussions rule) :roll:

Von meiner Seite aber ein grosses Dankeschoen fuer die Ausfuehrung und den Blogeintrag (@sma), obwohl ich weiss, dass die Diskussion schon weit an der Beantwortung meiner Frage vorbei geschritten ist :P
Antworten