Nein, eval ist effizient in dem Sinne dass der Code genauso schnell ausgeführt wird wie bei jeder anderen Art der Codeausführung. Und eval ist ineffizient in dem Sinne dass der Code zur Laufzeit geparst werden muss.
Hier mal ein Beispiel:
Code:
eval("eval('5 + 5')")
Zuerst parst der Rubyinterpreter den gesamten Code in einen Methodenaufruf und ein Stringliteral. Der Methodenaufruf von eval startet den Parser erneut und lässt ihn den String wieder in einen Methodenaufruf und ein weiteres String-Literal zerlegen. Die Ausführung des Codes bewirkt nun, dass der Parser erneut gestartet wird und den String in einen Methodenaufruf mit zwei Fixnum-Literalen parst. Dieser wird dann wieder ausgeführt.
Die Ausführung des letzten Stück Codess geht genauso schnell als hätte ich direkt 5+5 hingeschrieben. Was länger dauert ist das Parsen des Codes. Denn hier muss der Code drei Mal geparst werden, statt nur einmal.
Das ist aber auch der einzige Zeitverlust durch eval. Aber ein nicht unerheblicher, wenn man jede Millisekunde mehrere Programmteile mit eval ausführt. Darum sollte man eval möglichst selten einsetzen. Eine Idee zur Optimierung der Methodenaufrufe im Parser wäre z.B., wenn man die Script-Befehle nur einmal mit eval ausführt und den Code in eine neu angelegte Methode packt, die man daraufhin bei jeder Ausführung von "Script" Aufruft. Dann wäre nur noch ein Parse-Vorgang pro Script-Befehl notwendig. Aber wozu diese Mühe? Welcher Spieler hat mehrere parallele Prozesse, die ohne Wait jeden Frame zig Codefragmente per eval ausführen? Solange man sowas nicht macht sind die wenigen eval Aufrufe, die pro Frame stattfinden, problemlos verkraftbar. Es ist wie mit dem Show Picture Befehl im Rm2k. Wer so
blöd unerfahren ist und ein Picture jeden Frame neu anzeigt, der bringt das Spiel eben zum Ruckeln. Im XP ist das Problem mit den Pictures gelöst wurden. Aber natürlich gibt es auch da aufwendigerere Befehle die man möglichst nicht jeden Frame ausführen sollte.
Code:
Was es auch sein "könnte": Der "Ruby-Interpreter" selbst, der (in gewisser Weise) eine Art "Kompilierung erst während der Laufzeit" durchführt, statt - wie beispielsweise bei c++ bekannt - eine Vorabkompilierung durchzuführen.
Ruby hat keinen Just-in-Time Compiler sondern wird (leider) zur Laufzeit interpretiert. Ausnahme JRuby, aber auch dort optimiert der JIT eher die vielen "Brücken" die das JRuby-Programm mit der JVM verbinden, weniger den Rubycode selbst.
Ruby 1.8 (also die Version die der Maker nutzt) verwendet nicht einmal Bytecode, sondern arbeitet direkt auf dem Abstract Syntax Tree. Optimierungen werden eigentlich so gut wie gar keine ausgeführt (sieht man mal von so kleineren Sachen wie das Cachen von Instanzvariablen o.ä. ab).
Vor allem aber, um das nochmal deutlich zu machen: Der Rubyinterpreter selbst führt einfach nur eval auf den Programmcode aus, wenn du ein Rubyprogramm startest. Darum verursacht eval wirklich keinen ineffizienteren Bytecode bzw. Abstract Syntax Tree, als der Rubyinterpreter selbst liefern würde.