IronPython und Mono

Probleme bei der Installation?
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Mittwoch 13. August 2008, 09:14

Den Sinn von einer Scriptsprache in einer Scriptsprache verstehe ich eigentlich nicht. Bei IronPython hatte ich mir eigentlich mehr Performance erhofft weil ich dachte Python wird in IML übersetzt und das wird letztendlich nativ compiliert. Aber das war wohl ein Trugschluss. Vielmehr ist wohl der Python Interpreter in C# implementiert
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 13. August 2008, 10:00

sma hat geschrieben:Und auch RPython fällt doch in diese Kategorie - ein um dynamische Features beraubtes Subset von Python, welches sich effizient in eine statische Programmiersprache übersetzen lässt.
Also meinst du RPython auf der JVM?

Was die Performancereihenfolge angeht hätte ich da ganz ähnlich geschätzt, lediglich Scala habe ich mir nicht ausreichend angesehen und da schätzen zu können. Was aber die Limitierungen der JVM angeht hat Rich Hickey (die treibende Kraft hinter Clojure) meiner Meinung nach noch Tail-Call-Optimization in Zukunft nachzuholen denn ``recur`` scheint drangekleistert zu sein, nicht so wie in Scheme. Zudem das Problem eigentlich lösbar ist, denn soweit ich weiß unterstützt Bigloo auf der JVM TCO ohne Probleme. Man muss es eben selbst Optimieren, da die JVM sowas nicht selbst kann, aber es ist nicht ausgeschlossen dass spätere Clojure-Versionen das nachrüsten wenn mehr Interesse da ist.
burli hat geschrieben:Den Sinn von einer Scriptsprache in einer Scriptsprache verstehe ich eigentlich nicht. Bei IronPython hatte ich mir eigentlich mehr Performance erhofft weil ich dachte Python wird in IML übersetzt und das wird letztendlich nativ compiliert. Aber das war wohl ein Trugschluss. Vielmehr ist wohl der Python Interpreter in C# implementiert
Da vermischst du etwas.

IronPython-Programme werden (zumindest als ich zuletzt reingeschaut habe) ja auch in IL kompiliert, nur ist dieser Code nicht so einfach wie der entsprechende C# Code und braucht länger in der Ausführung, weil eben bei Python zur Laufzeit mehr passiert.

Der IronPython-Interpreter ist aber weder in Python noch in IL geschrieben. Analog dazu ist der CPython Interpreter in C geschrieben und produziert ebenso Bytecode, nur eben Python-Bytecode.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Mittwoch 13. August 2008, 10:04

Leonidas hat geschrieben:Der IronPython-Interpreter ist aber weder in Python noch in IL geschrieben. Analog dazu ist der CPython Interpreter in C geschrieben und produziert ebenso Bytecode, nur eben Python-Bytecode.
Aber Python Bytecode wird doch direkt interpretiert. .NET wird zur Laufzeit in nativen Code compiliert, oder?
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 13. August 2008, 10:08

burli hat geschrieben:Aber Python Bytecode wird doch direkt interpretiert. .NET wird zur Laufzeit in nativen Code compiliert, oder?
Ja, durch den JIT-Compiler. Aber das ist keine .NET-Spezialität, das haben die JVM, PLT Scheme, PyPy und viele andere auch.

Fakt ist: auch wenn man den Code zu nativen Code kompiliert, ist es langsamer als C#, schlicht weil eben mehr Code ausgeführt werden muss.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Mittwoch 13. August 2008, 10:19

Leonidas hat geschrieben:Ja, durch den JIT-Compiler. Aber das ist keine .NET-Spezialität, das haben die JVM, PLT Scheme, PyPy und viele andere auch.
PyPy ist ein JIT? Das verwirrt mich jetzt weil bei mir pypy ein Python in Python Interpreter ist
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 13. August 2008, 10:46

burli hat geschrieben:
Leonidas hat geschrieben:Ja, durch den JIT-Compiler. Aber das ist keine .NET-Spezialität, das haben die JVM, PLT Scheme, PyPy und viele andere auch.
PyPy ist ein JIT? Das verwirrt mich jetzt weil bei mir pypy ein Python in Python Interpreter ist
PyPy hat einen JIT (AFAIR sogar mehr noch, einen JIT-Generator). PyPy ist weniger ein Interpreter (das auch), sondern mehr eine Compiler-Infrastruktur.

sma, wie ich inzwischen rausgefunden habe, gibt es RPython auf der JVM.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Mittwoch 13. August 2008, 12:07

burli hat geschrieben:Den Sinn von einer Scriptsprache in einer Scriptsprache verstehe ich eigentlich nicht. Bei IronPython hatte ich mir eigentlich mehr Performance erhofft weil ich dachte Python wird in IML übersetzt und das wird letztendlich nativ compiliert. Aber das war wohl ein Trugschluss. Vielmehr ist wohl der Python Interpreter in C# implementiert
IronPython übersetzt in IL - genau wie Jython, JRuby, Pnuts, Rhino oder Groovy in Java-Bytecode übersetzen. Über den Stand des einfachen Interpreters sind die alle hinaus. Allerdings bringt das weniger als einem lieb ist, da der JIT jeweils gerade mal eben den Nachteil durch die nicht auf die Sprache zugeschnittene VM-Architektur auszugleichen vermag. Ich erwarte eigentlich, dass MagLev, einem auf einer Smalltalk-VM basierendem Ruby JRuby (und IronRuby) zeigt, wo der Hammer hängt... oder Gemstone-Smalltalk ist richtig schlecht. JVM und VES haben natürlich den Vorteil, dass da 10x mehr Ressourcen in die Entwicklung geflossen sind als bei jeder anderen VM.

JRuby ist ein Spezialfall, da hier der ursprüngliche Ruby-Interpreter so langsam ist, dass fast alles, was man versucht, schneller ist :) Daher gelang es hier, Ruby hinter sich zu lassen. Die Jython-Entwickler werden sich mehr anstrengen müssen, da CPython schon mal grundsätzlich schneller ist. JRuby erkauft sich die schnellere Performance übrigens (genau wie Jython) durch deutlich höheren Speicherverbrauch und eine längere Startphase.

Ansonsten: Eine Scriptsprache ist meist ausdrucksstärker als eine klassische Compiler-Sprache wie C oder Java und das das will man sich bei der Entwicklung von neuen Interpretern zu nutze machen. Hier ist ein Beispiel:

Code: Alles auswählen

for i, r in enumerate(map(lambda n: n * n, range(10))):
    print i, r
Übersetze ich das in Java - mal ignorierend, dass ich eigentlich kein "int" benutzen darf, weil Python ja beliebig große Ganzzahlen benutzt und auch ignorierend, dass "int" eigentlich nicht als Spezialisierung erlaubt ist, erhalte ich sowas:

Code: Alles auswählen

public class Test {
    public static void main(String[] args) {
        Function<int, int> f = new Function<int, int>() {
            public int apply(int n) {
                return n * n;
            }
        };
        for (Tuple2<int, int> t : Python.enumerate(Python.map(f, Python.range(10)))) {
            int i = t.get(0);
            int r = t.get(1);
            Python.print(i, r);
        }
    }
}
Die Laufzeit wäre beides mal ähnlich, das Java-Programm macht ja nicht mehr oder weniger als die Python-Version. Tatsächlich würde ich erwarten, dass die Java-Version die Chance hat, schneller zu sein, weil das primitive "*" weniger machen muss, als "operator.mul" bei Python.

Unten stehendes Java-Programm könnte man dabei automatisch aus dem Python-Programm erzeugen - jedenfalls wenn man annimmt, das "int" ausreichend ist und eine ausgefeilte Typanalyse hat, die "Tuple2<int, int>" ermitteln kann. Hier sind die statischen Funktionen, die ich benutzt habe.

Eine einfachere, aber nicht mehr so effiziente Übersetzung wäre, das statisches Typsystem von Java auszuhebeln und überall mit "PyObject"-Exemplaren zu arbeiten, von denen dann "PyInt" eine Unterklasse ist, die Ganzzahlen repräsentiert. das Beispiel könnte dann so aussehen:

Code: Alles auswählen

public static void main(String[] args) {
        Function f = new Function() {
            public Obj apply(Obj n) {
                return n.multiply(n);
            }
        };
        for (Tuple t : Python.enumerate(Python.map(f, Python.range(10)))) {
            Obj i = t.get(0);
            Obj r = t.get(1);
            Python.print(i, r);
        }
    }
Bei diesem Ansatz punktet sogar die JVM mit ihrem JIT gegen einen statischen Compiler, da sie durch dynamisches Inlinning und Typanalyse sowie das Umschreiben der Programme die eigentlich statischen Pfade im Programm finden und optimalen Maschinencode dafür erzeugen kann - das gelingt einem C-Compiler durch statische Analyse des Quelltextes nicht.

Stefan
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Mittwoch 13. August 2008, 13:49

Ich habe eben gerade noch ein bisschen gespielt. Ich glaube, das wird jetzt ziemlich OT.

Dieses Java-Programm (notwendiges Laufzeitsystem auf Anfrage) braucht auf meinem Rechner etwa 7ms für fib(28). CPython 2.5 braucht dafür 340ms.

Code: Alles auswählen

  static Obj fib(Obj n) {
    if (n.lessThan(_2)) {
      return _1;
    } else {
      return fib(n.subtract(_1)).add(fib(n.subtract(_2)));
    }
  }
Das Java-Programm ist offensichtlich nicht so flexibel wie Python, denn ich kann z.B. nicht einfach die globale Variable "fib" überschreiben - es gibt sie nicht. Ich kann auch nicht mittels "locals()" auf das "n" zugreifen. Der Kontext in Form von Frame-Objekten existiert ebenfalls nicht.

Sind derartige Funktionen verzichtbar, kann man in einem Subset von Python programmieren, welches sich recht effizient nach Java übersetzen lässt.

Definiere ich fib als Exemplar eine Klasse "Function", beträgt die Laufzeit etwa 8ms. Das ist noch erträglich. Jetzt kann ich "fib" ändern, habe nach wie vor gehen weder locals() noch globals().

Code: Alles auswählen

  static Function fib = new Function() {
    @Override
    Obj call(Obj n) {
      if (n.lessThan(_2)) {
        return _1;
      } else {
        return fib.call(n.subtract(_1)).add(fib.call(n.subtract(_2)));
      }
    }
  };
Benutze ich ein Dict, um globale Werte zu speichern, bricht mir die Performance auf 33ms (50ms wenn ich die Strings nicht cache und 19ms wenn ich Strings interniere - also einzigartig mache) ein. Ich nutze eine java.util.HashMap als Implementierung.

Code: Alles auswählen

  static Dict globals = Dict();
  static {
    globals.setItem(s_fib, new Function() {
      @Override
      Obj call(Obj n) {
        if (n.lessThan(_2)) {
          return _1;
        } else {
          return globals.getItem(s_fib).call(n.subtract(_1)).add(globals.getItem(s_fib).call(n.subtract(_2)));
        }
      }
    });
  }
Baue ich mir ein für diesen Benchmark optimales Dict, welches sich eine Anfrage merkt und bei selben Key den gemerkten Wert liefert und nicht in die java.util.HashMap schaut, komme ich auf 11ms runter.

Benutze ich auch für das lokale "n" ein dict, bin ich bei 340ms. Ahme ich also möglichst genau die Python-Semantik nach, muss ich auch mit Pythons Ausführungszeit leben.

Mit einer speziellen Dict-Implementierung für lokale Variablen kann ich die Zeit nochmal auf 109ms drücken, ich denke aber, das ist nur deswegen so extrem, weil ich in fib nur eine Variable habe. Simuliere ich 4 Variablen, bin ich bei 250ms (200ms bei 3 Variablen).

Führe ich ein Frame-Objekt ein, kann ich dort lokale Variablen verwalten. Das erlaubt, locals(), doch Frame-Objekte müssen jetzt parallel zu Javas normalem direkt in der VM verwalteten Stack von Activations verwaltet werden und ich komme auf 98ms (ThreadLocal-Variable) bzw. 64ms (einfache globale Variable). Eine Alternative, die ich in Smython benutze ist, den Frame überall durchzureichen. Das ist mit 62ms in diesem Beispiel geringfühig schneller. Locals lässt sich dann so implementieren:

Code: Alles auswählen

new BuiltinFunction() {
  @Override
  Obj call(Frame frame) {
    return frame.back.locals();
  }
}
Alle Zeiten mit JDK 1.6 64-bit im Sever-Modus gemessen.
Stefan
Antworten