frankx: (c)LISP (interpretiert) ca. 30 mal langsamer als FORTRAN oder C

Hellihello,

in der Schul-AG haben wir mit Fortran, C (compiliert) und Clisp (intertreptiert via Kommandozeile) mit einer geschachtelten Schleife die ersten 50.000 Primzahlen errechnet. Fortran und C brauchen ca. eine Sekunde mit Ausgabe auf dem Bildschirm, Clisp ca. 30 Sekunden. Woher dieser Faktor? Liegt der Code nicht bei beiden Verfahren irgendwann im Stackframe des RAM, also als Assemblercode?

Dank und Gruß,

frankx

--
tryin to multitain  - Globus = Planet != Welt
  1. Hallo,

    in der Schul-AG haben wir mit Fortran, C (compiliert) und Clisp (intertreptiert via Kommandozeile) mit einer geschachtelten Schleife die ersten 50.000 Primzahlen errechnet. Fortran und C brauchen ca. eine Sekunde mit Ausgabe auf dem Bildschirm, Clisp ca. 30 Sekunden. Woher dieser Faktor?

    wie sieht es bei 10.000 Primzahlen aus, wie bei 1.000.000?
    Du benötigst viel mehr Meßwerte, um einen Faktor (einigermaßen) exakt herauszubekommen. Außerdem: was ist schon ein Faktor 30?

    Bitte beachte, dass man für eine Problemlösung das geeignete Werkzeug nimmt. Die Wahl des Werkzeuges kann die Wahl der Programmiersprache enthalten. Damit will ich sagen: bei konkreten Problemen kann dieser Faktor völlig irrelevant sein.

    Emacs wäre durchaus flüssig nutzbar, wenn er bedienbar wäre ;-)

    Freundliche Grüße

    Vinzenz

    1. Hellihello Vinzenz,

      wie sieht es bei 10.000 Primzahlen aus, wie bei 1.000.000?
      Du benötigst viel mehr Meßwerte, um einen Faktor (einigermaßen) exakt herauszubekommen. Außerdem: was ist schon ein Faktor 30?

      Naja, wir wollten erstmal einen Eindruck bekommen. Überraschend war dann doch, dass es eben merh als 10-Fach scheint. Beim letzten Mal hatten wir zwei Rechner parallel laufen lassen mit dem kompilierten Fortran-Programm, da war der eine halb so schnell wie der andere, mit dem selben Script. Aber das lässt sich mich CPU-Leistung ja schnell plausibel machen.

      Bitte beachte, dass man für eine Problemlösung das geeignete Werkzeug nimmt. Die Wahl des Werkzeuges kann die Wahl der Programmiersprache enthalten. Damit will ich sagen: bei konkreten Problemen kann dieser Faktor völlig irrelevant sein.

      Die Frage zielte jetzt eher darauf: warum ist die Scriptsprache überhaupt so krass langsamer.

      Emacs wäre durchaus flüssig nutzbar, wenn er bedienbar wäre ;-)

      Ja, das soll ein gutes Betriebssystem sein, nur welchen Editor benutzt Du dann?

      Dank und Gruß,

      frankx

      --
      tryin to multitain  - Globus = Planet != Welt
      1. Hallo,

        » Emacs wäre durchaus flüssig nutzbar, wenn er bedienbar wäre ;-)
        Ja, das soll ein gutes Betriebssystem sein, nur welchen Editor benutzt Du dann?

        vi, ist doch logisch. :D

        Freundliche Grüße

        Vinzenz

        1. Hellihello

          »» » Emacs wäre durchaus flüssig nutzbar, wenn er bedienbar wäre ;-)
          »» Ja, das soll ein gutes Betriebssystem sein, nur welchen Editor benutzt Du dann?

          vi, ist doch logisch. :D

          Ja, da lern ich auch immer wieder was. Das fortran-Programm und clisp-Script mit vi.

          Dank und Gruß,

          frankx

          --
          tryin to multitain  - Globus = Planet != Welt
  2. @@frankx:

    in der Schul-AG haben wir mit Fortran, C (compiliert) und Clisp (intertreptiert via Kommandozeile) […] Woher dieser Faktor?

    Den Unterschied zwischen Compiler und Interpreter kennst du?

    Live long and prosper,
    Gunnar

    --
    Das einzige Mittel, den Irrtum zu vermeiden, ist die Unwissenheit. (Jean-Jacques Rousseau)
    1. Hellihello Gunnar,

      »» in der Schul-AG haben wir mit Fortran, C (compiliert) und Clisp (intertreptiert via Kommandozeile) […] Woher dieser Faktor?

      Den Unterschied zwischen Compiler und Interpreter kennst du?

      Ja, irgendwie schon, deshalb der Hinweis darauf. Ich hatte jetzt gemeint, dass die Interpretation ja schlussendlich doch auch zu Aweisungen an die Register führen müsste und in einem gewissen Stadium dann auch was assemblerähnliches entstehen müsste. Wenn aber jede do-Schleife bei jedem Aufruf mit der Interpretierei erst wieder von vorn loslegt, kommt man ja rasch zu größeren  Faktoren.

      Irgendwie meinte ich halt, o.k., das Ding muss einmal erst das tun, was der Compiler eben schon einmal tat, als er compilierte. Was ja eher ein konstanter Faktor gewesen wäre. Hier ist das vermutlich dann eher was wie Fakultät. Da wären dann die Messergebnisse, wie von Vinzenz gefordert, aufschlussreich.

      Dank und Gruß,

      frankx

      --
      tryin to multitain  - Globus = Planet != Welt
      1. Moin.

        Ich hatte jetzt gemeint, dass die Interpretation ja schlussendlich doch auch zu Aweisungen an die Register führen müsste und in einem gewissen Stadium dann auch was assemblerähnliches entstehen müsste.

        Was du beschreibst nennt sich just-in-time (JIT) compilation. Die Java VM beispielsweise übersetzt den Java-Bytecode zur Laufzeit in Maschinencode. Die meisten Common Lisp Umgebungen stellen vergleichbare Möglichkeiten über die Funktion compile bereit.

        Christoph

  3. Moin.

    Ein paar Anmerkungen zur Begrifflichkeit:

    Liegt der Code nicht bei beiden Verfahren irgendwann im Stackframe des RAM, also als Assemblercode?

    Was  bitte ist der 'Stackframe des RAM'? Als Stackframe bezeichnet man normalerweise die für einen Funktionsaufruf nötigen, auf dem Stack abgelegten Werte. Wie genau ein solcher 'Rahmen' aussieht ist abhängig von der calling convention der entsprechenden Programmiersprache.

    Desweiteren meinst du vermutlich Maschinen- und nicht Assemblercode, da letzterer vor seiner Ausführung selbst noch in ersteren übersetzt werden muss.

    Christoph

    1. Hellihello

      Moin.

      Ein paar Anmerkungen zur Begrifflichkeit:

      »» Liegt der Code nicht bei beiden Verfahren irgendwann im Stackframe des RAM, also als Assemblercode?

      Was  bitte ist der 'Stackframe des RAM'?

      http://en.wikipedia.org/wiki/Stack_frame#Structure sowas vor dem inneren Auge, ich dachte, da würde schlussendlich auch eine Funktion drinne landen, also eine do-Schleife zum Beispie?

      Als Stackframe bezeichnet man normalerweise die für einen Funktionsaufruf nötigen, auf dem Stack abgelegten Werte.

      Also nicht die Funktion "gehe von startwert bis endwert durch".

      Wie genau ein solcher 'Rahmen' aussieht ist abhängig von der calling convention der entsprechenden Programmiersprache.

      Also bei Assembly anders als bei C anders als bei Fortran anders als bei Lisp.

      Desweiteren meinst du vermutlich Maschinen- und nicht Assemblercode, da letzterer vor seiner Ausführung selbst noch in ersteren übersetzt werden muss.

      http://en.wikibooks.org/wiki/Reverse_Engineering/Functions_and_Stack_Frames#Functions_and_Stack_Frames  - da bin ich mir selbst nicht so ganz sicher. C resultiert doch in Assembly, oder? Nicht aber Java, PHP, Lisp, Fortran.

      Ich vermutete nun sowas: irgendwie muss doch Maschinencode rauskommen. Und vielleicht wäre der dann irgendwie angeordnet wie Assembly-Code und läge im Ram. Und ich dachte, ab dem Moment wäre es eben ja wurscht, ob vorher kompiliert oder interpretiert wurde. Wenn aber immer wieder interpretiert wird, nur um die do-schleife weiterzuspulen, dann dauerts natürlich ewig. Das wäre ja, wie wenn der Fortran/C-Compiler für jeden Schleifendurchlauf neu komplieren würde (;-).

      Dank und Gruß,

      frankx

      --
      tryin to multitain  - Globus = Planet != Welt
      1. Moin.

        »» Wie genau ein solcher 'Rahmen' aussieht ist abhängig von der calling convention der entsprechenden Programmiersprache.

        Also bei Assembly anders als bei C anders als bei Fortran anders als bei Lisp.

        Der Begriff Stackframe im engeren Sinne ergibt eigentlich nur für kompilierte Sprachen Sinn, da diese ihre Argumente und lokalen Variablen auf dem Hardware-Stack ablegen. Bei interpretierten Sprachen kann man den Begriff in Analogie verwenden, da es auch dort in der Regel einen call stack gibt. Die Funktions-Argumente liegen meist aber nicht auf dem Hardware-Stack (das Argument-Objekt in JavaScript sollte auf dem Heap zu finden sein).

        Die calling convention beschreibt, wie ein Funktionsaufruf auf Assembler-Ebene durchgeführt wird (wo liegen die Argumente, wo die Rücksprungadresse, wer ist verantworlich für die Freigabe des Speichers für Argumente und lokale Variable, ...). Compiler können auch mehrere Konventionen für eine Sprache unterstützen. In C beispielsweise sind für x86-Architekturen die Konventionen cdecl (der Standard), stdcall (Windows API, nur für Funktionen mit fester Argumentzahl möglich) und fastcall (die ersten beiden Argumente liegen in Registern und nicht auf dem Stack) verbreitet.

        [...] da bin ich mir selbst nicht so ganz sicher. C resultiert doch in Assembly, oder? Nicht aber Java, PHP, Lisp, Fortran.

        C und Fortran werden vom Compiler direkt in Maschinencode übersetzt, der Zwischenschritt über Assembly wird in der Regel nicht genommen. Soweit ich weiß disassembliert beispielsweise der Aufruf gcc -S den generierten Maschinencode.

        PHP und Lisp werden in der Regel interpretiert, wobei es wie bereits beschrieben bei letzterer Sprache auch die Möglichkeit zur Kompilierung gibt.

        Bei Java ist das komplizierter: Der Java-Compiler erzeugt Bytecode, d.h. Maschinencode für eine 'virtuelle' Maschine und nicht den x86 Prozessor. Erst beim Aufruf des Java-Programms wird der Bytecode in x86 Maschinencode übersetzt, der dann sofort ausgeführt wird. Der Vorteil gegenüber rein interpretierten Sprachen ist hier, dass die Übersetzung des Bytecodes in Maschinencode viel schneller durchgeführt werden kann. Der Vorteil gegenüber einer direkten Kompilierung in Maschinencode ist, dass der Bytecode unabhängig von der Rechnerarchitektur ist, d.h. die selben .class-Dateien können auf jeder Rechnerarchitektur verwendet verden, für die eine JVM verfügbar ist. Das Kompilieren zur Laufzeit erlaubt der VM Optimierungen, die ein klassischer Compiler nicht durchführen kann, da er weniger 'Wissen' über den Programmablauf besitzt.

        Wenn aber immer wieder interpretiert wird, nur um die do-schleife weiterzuspulen, dann dauerts natürlich ewig. Das wäre ja, wie wenn der Fortran/C-Compiler für jeden Schleifendurchlauf neu komplieren würde (;-).

        Genau das macht ein klassischer Interpreter aber, wobei allerdings in der Regel der Syntaxbaum nur einmalig generiert wird und dieser dann durchlaufen wird. Wird hingegen maschinencode erzeugt, ist der Interpreter zum Compiler geworden.

        Christoph

        1. Hellihello

          »» Wenn aber immer wieder interpretiert wird, nur um die do-schleife weiterzuspulen, dann dauerts natürlich ewig. Das wäre ja, wie wenn der Fortran/C-Compiler für jeden Schleifendurchlauf neu komplieren würde (;-).

          Genau das macht ein klassischer Interpreter aber, wobei allerdings in der Regel der Syntaxbaum nur einmalig generiert wird und dieser dann durchlaufen wird. Wird hingegen maschinencode erzeugt, ist der Interpreter zum Compiler geworden.

          Naja, aber sag mal das dauert eine Sekunde, dann müsste danach doch alles so schnell gehen wir beim compilierten, würde ich jetzt wieder sagen, back to the Anfang (;-).

          Dank und Gruß,

          frankx

          --
          tryin to multitain  - Globus = Planet != Welt