[linux-l] Ruby: sehr cool, aber auch sehr laaaahm... wie geht's schneller?!

Volker Grabsch vog at notjusthosting.com
Di Aug 22 01:16:54 CEST 2006


On Mon, Aug 21, 2006 at 01:48:18PM +0200, Oliver Bandel wrote:
> seit kurzem schaue ich mir Ruby an.
> 
> Echt coole Sprache.
> Da kann man sich das mit dem OO ja doch wieder angewöhnen ;-)

Ja, die Syntax sieht schön aus.
(im Vergleich zu z.B. Ocaml oder Python, und erst recht im
Vergleich zu C/C++/Java ;-))

Aber ich persönlich finde die Sprache nicht gut designt.
Unter den dynamisch typisierten Sprachen finde ich nach
wie vor Python super designt, da könnte höchstens Scheme
mithalten (nicht kosmetisch, aber konzeptionell).

Ich habe viele buggige Ruby-Bibliotheken gefunden, und ich
glaube nicht, dass sie alle auf die kleinere Community
zurück zu führen sind. Diese übermäßig komplexe Basis-Klasse
"object", die von anderen Modulen nachträglich sogar weiter
aufgebläht wird, ist z.B. genau das, was in Python viel, viel
besser gemacht wurde. Ditto für I/O-Streams.

Ich hatte damals Ruby vor Python gelernt. Pythons Syntax
erschien mir anfangs zu obskur, aber ich habe mich schnell
daran gewöhnt. Rubys Syntax finde ich immer noch schöner,
dennoch sind meine Ruby-Programme niemals so schön wie die
Python-Programme geworden. Zwar gibt es einige Konzepte,
die sich Python von Ruby abschauen sollte, aber die sind
IMHO weniger wichtig.

Dir als OCaml-Fan, der mir in Sachen Perfektionismus wahr-
scheinlich sehr ähnlich ist, würde ich daher dringend eher
zu Python raten.

> Leider ist die Sprache wohl etwas langsam, aber
> OK wäre es, wenn die für die meisten Scripting-Sachen
> schnell genug ist. Sollte meist reichen.

Es kommt immer auf den Anwendungszweck an. Normalerweise
werden Ruby/Python genutzt, um die "großen" Dinge zu
programmieren. Details, die Performance brauchen (z.B.
"multiple-precision arithmetics" oder XML-Parser), werden
in anderen Sprachen geschrieben.

Für diesen Zweck gibt es Profiler, die heutzutage immer
noch viel zu wenig genutzt werden (auch nicht von mir,
muss ich zu meiner Schande gestehen). Da sucht man sich
die *echten* Flaschenhälse raus (da verschätzt man sich
gerade in großen Programmen sehr schnell), und optimiert
sie, zur Not durch Reimplementierung in C.
(Ocaml wäre natürlich cooler, weil etwa genauso schnell,
aber robust)

> Aber wenn es langsamer (oder jedenfalls nicht schneller) wird,
> obwohl es schneller werden sollte, wundert man sich doch. :(

Das gibt es auch in Python. Der Interpreter wird auf die
gebräuchlichen Programm-Muster optimiert, und dennoch
tauchen immer wieder total abwegige Herangehensweisen auf,
die aus obskuren Gründen trotzdem schneller sind. Auf der
Python-Mailingliste haben die "Timing"-Freaks, die irgendwelche
Pauschal-Aussagen (Programm-Konstrukt X ist schneller als Y)
tatsächlich mal nachgemessen haben. Für Ruby gilt vermutlich
dasselbe.

Auf sowas sollte man aber niemals optimieren, weil das
bei anderen Interpreter-Implementierungen oder auf anderen
Betriebssystemen total anders aussehen kann. Auch zukünftige
Implementierungen werden immer auf die gebräuchlichen Muster
optimiert werden, sodass gutes, einfaches Design auch in
Sachen Performance die besten Zukunfts-Aussichten hat.

> Naja gut, am Wochenende habe ich erstmals geRuby't. Werden
> sicherlich noch ein paar Kniffe da sein, die ich zu lernen habe
> (was aber sicherlich recht flott geht).

Ich habe am WE mit Ocaml herumgespielt. Nun gut, mein Programm
war wirklich ein Paradebeispiel für etwas, wo mir ein gutes
Typsystem sehr entgegen kommt, und die uralte Implementierung
war in C++, aber dennoch hat es mich positiv überrascht, wie
einfach und "straight forward" ich voran gekommen bin. Im
Moment sind nur die wichtigsten Funktionen implementiert, aber
sobald das Programm eine "sichtbare" Ausgabe liefert, werd
ich es dir auf jeden Fall zeigen und würde gerne deine
Meinung zum Programm-Design haben.

Es gab sehr wenig Tippfehler, die Sprache ist in dieser
Richtung also wirklich wie Ruby/Python, nicht wie C/C++/Java.
Die sonstigen Fehler, die der Compiler fand, basierten in
der Tat auf Schlampigkeiten und/oder Denkfehler. Der Ocaml-
Compiler versteht mich in der Tat besser als Ruby/Pythons
Exception-System. Ich habe das "böse auf die Finger hauen"
des Ocaml-Compilers noch nicht erlebt, im Gegenteil: Ich
verwende den Compiler, um meinen Programmier-Stil zu
verbessern. Wie? Indem ich "-w A" mache, also alle Warnungen
an. Dann kann selbst ein Pedant noch was vom Compiler lernen.
Eine zusätzliche Warnung, sie klang total belanglos, war
aber bei genauerem Nachdenken wirklich berechtigt, und hat
mich auf etwas aufmerksam gemacht, dass ich vorher zu wenig
beachtet hatte.

> Als erste Experimente wollte ich große Files checken, ob sie
> /^begin/ enthalten, um zu checken, ob sie uuencodete Daten
> beinhalten.
> 
> Folgende Lösung hatte ich mir einfallen lassen:
> 
> ================================================
> def look_for_begin
>   while line = gets
>     if line =~ /^begin/
>       puts line
>     end
>   end
> end
>
> ARGF.each { look_for_begin }
> ================================================
> 
> Dauert mit den Files, die ich habe ziemlich lange,
> ca. 3,6 Sekunden.
> 
> Nun dachte ich, mache ich das schneller, indem ich abbreche
> nachdem der String gefunden wurde:
> 
> 
> 
> ================================================
> def look_for_begin
>   catch :ready do
>     while line = gets
>       if line =~ /^begin/
>         puts line
>         throw :ready
>       end
>     end
>   end
> end
> 
> ARGF.each { look_for_begin }
> 
> ================================================
> 
> Das wird aber auch nicht schneller.... sollte es aber,
> denn die Datei dürfte ja nach dem throw nicht mehr zuende gelesen
> werden müssen. Allerdings sagen mir die Ausführungszeiten, daß
> anscheinend doch alle Files bis zum bitteren Ende gelesen werden...?!

Wie viele Dateien sind es denn? Vielleicht wird die Mammut-Zeit
vom Öffnen der Dateien und nicht vom Auslesen gefressen?
(zumal das Auslesen ja gecached wird)

Um sicher zu gehen, kannst du ja mal sowas wie "break" benutzten,
anstelle der Exception.
(weiß jetzt nicht, wie das "break" in Ruby heißt)


> ========================================
>   foreach (@ARGV)
>   {
>     open FILE, "$_"  or next;
>     while(<FILE>)
>     {
>       if( /^begin/ )
>       {
>         print;
>         #close FILE;
>         #next;
>       }
>     }
>     close FILE;
>   }
> ========================================
> 
> ca. 1,7 Sekunden mit den Kommentaren,
> ca. 0,8 Sekunden wenn man die Kommentarzeichen entfernt
> 
> So etwas (speedup) hätte ich bei meinen Ruby-Lösungen auch erwartet.

Kannst du das mal mit Python testen? Mit und ohne "break".
Der Vergleich auf deiner Kiste mit dem Ruby-Programm und dem
Perl-Programm würde mich interessieren:

    import sys

    def look_for_begin(stream):
        for line in stream:
            if line.startswith("begin"):
                print line,
                break

    for filename in sys.argv:
        look_for_begin(open(filename))


Das Komma hinter dem "print" ist übrigens korrekt.
Soll er sich selbst nicht mit durchscannen (d.h. argv[0] ignorieren),
lauten die letzten beiden Zeilen:

    for filename in sys.argv[1:]:
        look_for_begin(open(filename))

> Wie geht das nun in Ruby?

Außer einem "break" oder "return" fällt mir nur noch eine
Möglichkeit ein: Vielleicht sorgt irgendeines einer Ruby-Kürzel
dafür, dass die ganze Datei in ein Array gelesen wird. Glaube
ich zwar nicht, aber ansonsten ...

Außerdem solltest du das selbe Programm mehrfach hintereinander
laufen lassen und die Laufzeit beobachten, um die Auswirkungen
von Caching-Effekten zu sehen. Und es könnte noch sein, dass
die Lade-Zeit des jeweiligen Interpreters (Ruby, Perl, Python)
einen Einfluss hat. (z.B. ist das Perl-Programm vielleicht nur
deshalb so viel schneller? Nur ne Idee, keine Ahnung)

Könntest du deine zu scannenden Dateien mal zehnmal durchlaufen
lassen? Ein paar Sekunden sind aus oben genannten Gründen doch
noch ein bisschen wenig zum Messen.
(d.h. nicht das Programm zehnmal starten, sondern z.B. die
Dateien zehnmal kopieren, oder jeweils zehnmal als Kommando-
Zeilen-Argument mit angeben)


Viele Grüße,

    Volker

-- 
Volker Grabsch
---<<(())>>---
Administrator
NotJustHosting GbR



Mehr Informationen über die Mailingliste linux-l