_Robert: Geschwindigkeit von Javascript-Befehlen?

Hallo,

es geht um die dynamische Erzeugung von HTML-Seiten mittels einer JavaScript-Datei. (Für alle denen sich jetzt die Nackenhaare aufgestellt haben: Es ist nur eine Offline-Spielerei auf meinem Heim-Rechner. Ich persönlich verwende zum Surfen weder JavaScript noch Cookies.)

Was ist schneller: Die Verwendung  von switch oder der Aufruf als Funktion?

Einfaches Beispiel: entweder:

var y = "hallo_welt"

switch(y){
case "hallo_welt": tue dieses und jenes
}

oder:

windowy

function hallo_welt(){
tue dieses und jenes
}

Ich schreibe mehrmals hintereinander den Inhalt mittels document.write. Ist es schneller, wenn ich den Inhalt in eine Variable schreibe und zum Schluss einmal mit document.write ausgeben lassen? Falls ja, gibt es eine Begrenzung der Länge des Variableninhalts? Es gibt vielleicht Probleme wenn ich da 20 kb an Text reinschreiben will.

Gibt es Geschwindigkeitseinbußen, wenn ich Variablen in einem Array speichere?

Robert

  1. Warum testest du es einfach nicht?
    Die Geschwindigkeit hängt von ein paar Faktoren ab, z.b. dein Browser (IE4 oder IE8 oder gar Safari 4?), dein Rechner (P2 400 MHz oder Q4 4x3 GHz?) RAM usw.

    Starte einen Timer wenn und Beende ihn, ermittle die Differenz und du hast die Laufzeit.

    1. Warum testest du es einfach nicht?

      Ich bin eigentlich mehr an der Arbeitsweise von Javasvript interessiert. Beispiel: Wie viel Schritte müssen bei switch im Speicher ausgeführt werden, wie viele beim Aufruf als Funktion? Geht switch vielleicht schneller, weil die Funktionen im Speicher erst "lange" gesucht werden müssen?

      So was in dieser Richtung. Oder ist das auch alles vom Browser abhängig, wie die Befehle umgesetzt werden? Ich dachte, das es da vielleicht so was wie einen Standard gibt, den alle Browser nutzen. Wohlgemerkt, es geht nur um den Unterschied zwischen den einzelnen Befehlen. Das einige Browser schneller Javascript verarbeiten als anderen ist klar.

      1. Ich bin eigentlich mehr an der Arbeitsweise von Javasvript interessiert.

        Dann musst du in die Interpreter und Virtual Machines schauen, auf denen JavaScript ausgeführt wird. Das ist nicht so banal, wie du dir das vorstellst und hängt einfach stark vom genauen Aufbau des Scriptes und von der verwendeten JS-Engine ab.

        Wie viel Schritte müssen bei switch im Speicher ausgeführt werden

        Der Bezeichner y wird über die Scope Chain aufgelöst, sodass zunächst beim internen Variablenobjekt der gegenwärtig ausgeführten Funktion gesucht wird oder direkt, sofern der Code global ist, beim globalen Objekt window.

        Dann wird der Wert der Variable mit dem ===-Operator mit dem Wert des gegebenen String-Literals verglichen. Falls die übereinstimmen, wird der nachfolgende Code ausgeführt (wenn du dann nicht break; sagst der folgende auch noch).

        Switch wird problematisch, sobald du viele Cases hast. Dann müssen u.U. sehr viele ===-Vergleiche gemacht werden - das könnte der Compiler aber im Prinzip mit einem Hash-Table wegoptimieren, zumindest wenn nach allen case ein fester String-Literal folgt. Da aber eine Expression folgen darf, die bei jedem neuen Abarbeiten ausgeführt wird, würde ich nicht mit einer solchen Optimierung rechnen.

        wie viele beim Aufruf als Funktion?

        Der Bezeichner y wird wie oben aufgelöst, dasselbe geschieht mit window. Dann wird beim window-Objekt nach einem entsprechenden Member gesucht und dieser als Funktion aufgerufen.

        Funktionsaufrufe können teuer sein, allerdings ist es keine Lösung, auf Codestrukturierung mittels Funktionen bloß der Performance halber zu verzichten. Vorher sollte man den Algorithmus optimieren, sodass die Notwendigkeit von Funktionsaufrufen minimiert wird. Aktuelle JS-Engines wie Chrome und JavaScriptCore haben da unglaubliche Fortschritte gemacht - da ist der Funktionsaufruf an sich kein nennenswertes Problem mehr.

        Geht switch vielleicht schneller, weil die Funktionen im Speicher erst "lange" gesucht werden müssen?

        Wenn du genau angibst, an welchem Objekt gesucht werden muss, kostet der Eigenschaftszugriff sehr wenig, weil das Nachschlagen z.B. in einem Hash-Table äußerst performant ist.

        Oder ist das auch alles vom Browser abhängig, wie die Befehle umgesetzt werden?

        Ja!

        Ich dachte, das es da vielleicht so was wie einen Standard gibt, den alle Browser nutzen.

        Der Standard regelt, wie die Sprache nach »außen« hin funktionieren soll und wie das »interne« Vorgehen gelöst ist. Das ist aber äußerst abstrakt. Wie der einzelne JavaScript-Interpreter das dann in Maschinencode umsetzt, ist eine ganz andere Geschichte.

        Mathias

  2. Was ist schneller: Die Verwendung  von switch oder der Aufruf als Funktion?

    Vermutlich letzteres, weil der »Property Accessor Operator« bestimmt unglaublich stark optimiert wurde. Ein Unterschied käme vermutlich durch das Aufrufen der Funktion - beim switch hättest du ja alles an einem Ort. Insofern könnte auch letzteres langsamer sein, je nachdem, was die JavaScript-Engine besser optimiert hat.

    Wenn du mehr als Vermutungen willst, musst du es halt benchmarken auf den Systemen, auf den es später laufen soll.

    Allerdings glaube ich nicht, dass es auf diesen Performance-Unterschied ankommt. switch ist hier vor allem schlechter strukturierter Code. Ich würde ein Objekt mit Funktionsobjekten daran verwenden und dann mit objname die gewünschte Funktion aufrufen. Optimiere nicht dort, wo nicht offensichtlich etwas herauszuholen ist. Wie heißt es so schön: »Premature optimization is the root of all evil.« Wenn dein Script langsam läuft, dann vermutlich nicht wegen diesem einen switch versus Property Accessor und Funktionsaufruf. Sondern wegen DOM-Operationen, schlechten Algorithmen, ineffizientem Event-Handling und so weiter.

    Ich schreibe mehrmals hintereinander den Inhalt mittels document.write. Ist es schneller, wenn ich den Inhalt in eine Variable schreibe und zum Schluss einmal mit document.write ausgeben lassen?

    Ja. Allerdings können mehrere document.write-Aufrufe dafür sorgen, dass der Anwender schneller die Inhalte sieht. document.write(rechenintensivesScript1()); rechenintensivesScript(rechenintensivesScript2()); ist natürlich »subjektiv« für den Anwender schneller als document.write(rechenintensivesScript1() + rechenintensivesScript2());.

    Falls ja, gibt es eine Begrenzung der Länge des Variableninhalts?

    Nein, da würde ich mir da keine Sorgen machen.

    Es gibt vielleicht Probleme wenn ich da 20 kb an Text reinschreiben will.

    Das dürfte kein Problem sein.

    Gibt es Geschwindigkeitseinbußen, wenn ich Variablen in einem Array speichere?

    Du meinst y usw.?
    Im Vergleich zu was, globalen Variablen? Wie würdest du sie denn ohne Array speichern und darauf zugreifen? Wenn du eine Liste hast, kannst du sie nur in einem Array oder Object (das nicht identisch mit dem globalen Objekt ist) abspeichern.

    Mathias

    1. Wenn dein Script langsam läuft, dann vermutlich nicht wegen diesem einen switch versus Property Accessor und Funktionsaufruf. Sondern wegen DOM-Operationen, schlechten Algorithmen, ineffizientem Event-Handling und so weiter.

      Als Event-Handling gibt es nur onmouseover und -out. Damit gibt's auch keine Probleme. Und als DOM-Operation habe ich nur getElementByID im Zusammenhang mit den beiden Events. Ich muss allerdings gestehen, dass die JavScript-Datei momentan eine Größe von 400 kb hat. Aber meiner Meinung nach speilt das nur eine Rolle beim allerersten Aufruf (weil man da warten muss bis es geladen wurde). Was die schlechten Algorithmen betrifft, vielleicht liegt es daran:

      Eine Funktion zum Erzeugen von HTML-Code ist bei mir so gestaffelt aufgebaut (einfaches Beispiel):

      function erzeuge(i1,i2,i3,i4,i5){
      var a=new Array(i1,i2,i3,i4,i5)
      for(var i=0;i<a.length;i++){
      if(!a[i]){a[i]="&nbsp;"}x.writleln('<p>'+a[i]+'</p>')
      }}

      function inhalt(){
      erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')
      erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')
      erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')
      erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')
      erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')
      erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')
      ...
      }

      So in etwa sieht's aus. In Wirklichkeit enthält die Funktion erzeuge ein paar mehr if-Abfragen. Man könnte der Funktion "erzeuge" natürlich gleich ein Array übergeben, aber in diesem Fall müsste ich a=new Array in jeder Zeile ergänzen. Und das würde die Datei aufpusten. Wieder mal der alte Spagat zwischen Dateigröße und Geschwindigkeit.

      1. function erzeuge(i1,i2,i3,i4,i5){
        var a=new Array(i1,i2,i3,i4,i5)
        for(var i=0;i<a.length;i++){
        if(!a[i]){a[i]="&nbsp;"}x.writleln('<p>'+a[i]+'</p>')
        }}

        function inhalt(){
        erzeuge('inhalt1','inhalt2','inhalt3','inhalt4','inhalt5')

        Man könnte der Funktion "erzeuge" natürlich gleich ein Array übergeben,

        Ja.

        aber in diesem Fall müsste ich a=new Array in jeder Zeile ergänzen.

        Nein, wieso?

        1. Anscheinend kennst du die arguments-Liste nicht, mit der du auf die Parameter einer Funktion zugreifen kannst.

        function funktionMitBeliebigerParameterzahl () {
           for (var i = 0, length = arguments.lenth; i < length; i++) {
              alert(arguments[i]);
           }
        }

        funktionMitBeliebigerParameterzahl("eins", "zwei");
        funktionMitBeliebigerParameterzahl("eins", "zwei", "drei", "vier");

        2. Anscheinend kennst du den Array-Literal nicht.

        ['inhalt1', 'inhalt2', 'inhalt3', 'inhalt4', 'inhalt5']

        ist ein Ausdruck, der einen Array mit eben jenen Elementen erzeugt.

        erzeuge(['inhalt1', 'inhalt2', 'inhalt3', 'inhalt4', 'inhalt5']);

        übergibt den so erzeugten Array dann an eine Funktion.

        Wieder mal der alte Spagat zwischen Dateigröße und Geschwindigkeit.

        Dateigröße ist vernachlässigbar. JavaScript-Code lässt sich wunderbar in der Größe optimieren (z.B. durch die genannten Array-Literal) und schließlich komprimieren.

        Mathias

          1. Anscheinend kennst du die arguments-Liste nicht, mit der du auf die Parameter einer Funktion zugreifen kannst.
          2. Anscheinend kennst du den Array-Literal nicht.

          Kannte ich beides bis jetzt noch nicht. Muss irgendwie an mir vorüber gegangen sein. Vielen Dank für die Tipps. Damit ich sicherlich noch 'ne ganze Menge an umständlichen Code vereinfachen.

          Dateigröße ist vernachlässigbar. JavaScript-Code lässt sich wunderbar in der Größe optimieren (z.B. durch die genannten Array-Literal) und schließlich komprimieren.

          Das mit dem Komprimieren kenne ich. Vor allem bei den sprechenden Funktions- und Variablennamen kann man 'ne Menge rausholen. Und gibt's da wohl noch was mit gZip unter der Verwendung von php und .httaccess (oder so ähnlich).

        1. Hallo molily,

          Dateigröße ist vernachlässigbar. JavaScript-Code lässt sich wunderbar in der Größe optimieren (z.B. durch die genannten Array-Literal) und schließlich komprimieren.

          Man sollte an dieser Stelle auch auf mod_deflate des Apache Servers hinweisen, mit dem eine Kompression vornehmen kann ohne die JavaScript-Datei selbst zu verändern. Im Normalfall dürfte die zu übertragende Datenmenge hier deutlich geringer sein.

          Grüße

          Marc Reichelt || http://www.marcreichelt.de/

          --
          DPRINTK("Last time you were disconnected, how about now?");
                  linux-2.6.6/drivers/net/tokenring/ibmtr.c
          Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
          1. Dateigröße ist vernachlässigbar. JavaScript-Code lässt sich wunderbar in der Größe optimieren

            Man sollte an dieser Stelle

            An dieser Stelle hatte ich das nicht erwähnt, weil es um eine »Offline-Spielerei auf meinem Heim-Rechner« ging, ich also annahm, dass kein Webserver mitspielt.

            Wenn es sich dabei um eine HTTP-Anwendung handelt, dann ist natürlich viel mehr möglich. Dann besteht keine Notwendigkeit für ein 400-KB-Script mit den haufenweise Arbeitsdaten, auf dessen Laden man erst einmal warten muss. Die Daten kann man im Nachhinein laden, sobald sie benötigt werden.

            Mathias

  3. Hallo!

    Ich schreibe mehrmals hintereinander den Inhalt mittels document.write. Ist es schneller, wenn ich den Inhalt in eine Variable schreibe und zum Schluss einmal mit document.write ausgeben lassen? Falls ja, gibt es eine Begrenzung der Länge des Variableninhalts? Es gibt vielleicht Probleme wenn ich da 20 kb an Text reinschreiben will.

    Du wirst auf mehrere Bremsen stoßen. Die eine besteht darin, dass bei einer Manipulation des DOM die Page neu gerendert werden muss - derartige Manipulationen sind also so selten wie möglich zu verwenden. Eine Variable kann bei JavaScript nahezu beliebig großen Inhalt enthalten, da gibt es nur die Speicherbegrenzung der JavaScript-Engine.

    Das nächste Problem besteht beim IE darin, dass jeder selbst statisch definierte String als neues Object im Speicher aufgemacht wird, was unnötig Zeit kostet. Idealerweise schmeißt Du die Strings erst einmal in ein Array, welches Du schließlich mit .join('') zusammenfügst.

    Gibt es Geschwindigkeitseinbußen, wenn ich Variablen in einem Array speichere?

    Nein, im Gegenteil - die Array-Adressierung ist auch im IE schnell genug.

    Gruß, LX

    --
    RFC 1925, Satz 3: Mit ausreichendem Schub fliegen Schweine wunderbar. (...)
  4. Was ist schneller: Die Verwendung  von switch oder der Aufruf als Funktion?

    Meine Benchmark Skript hat dir Mathias ja schon gezeigt. Ist aber für solche Sachen eher Spielerei, ob eine Funktion theoretisch 1 Millionen Mal oder 1,2 Mill. pro Sekunde durchlaufen wird, dürfte bei den meisten Anwendungen kaum eine Rolle spielen, zumal eher die Darstellung bremst. Also wenn du Objekte verschiebst, deren Inhalt oder das Aussehen änderst.

    Einfaches Beispiel: entweder:

    var y = "hallo_welt"

    switch(y){
    case "hallo_welt": tue dieses und jenes
    }

    oder:

    windowy

    function hallo_welt(){
    tue dieses und jenes
    }

    Das Prinzip ist elegant und du kannst damit schönen Code produzieren. Ich kenne und benutze sowas häufig in Perl (mit Hash's), aber ich habe scho festgestellt, dass die JS Umsetzung hier nicht unbedingt schneller ist, als der direkte Weg über switch oder if.

    Wenn du so eine Funktion häufiger benutzt, könnte es sich sogar anbieten das mit eval (ist evil) umzusetzen, da du dem Interpreter damit die suche nach dem String (y) sparen kannst.

    Aber wie gesagt, oft bremst die Darstellung und JS ist nicht unbedingt auf Geschwindigkeitsoptmierung ausgelegt. Aber vielleicht bieten die Artikel ja noch Erkenntnise (ich benutz das mal als Bookmark, ich hab die Artikel noch nicht gelesen ;-) )
    http://home.earthlink.net/~kendrasg/info/js_opt/(etwas arg alt)
    http://www.mellowmorning.com/2008/05/18/javascript-optimization-high-performance-js-apps/
    und noch mehr
    http://www.google.de/search?hl=de&ei=alQdSpL-JJCZ_Qbvyd2FDQ&sa=X&oi=spell&resnum=1&ct=result&cd=1&q=javascript+optimization&spell=1

    Gibt es Geschwindigkeitseinbußen, wenn ich Variablen in einem Array speichere?

    Arrays sind i.d.R. recht schnell, kommt aber im Einzelfall drauf an was du vor hast.

    Struppi.

    1. Danke für die Erklärungen und die Links. Letztere werde ich mir mal zu Gemüte führen. Und natürlich: Google-Suche nach den englischen Begriffen. Bisher hab ich immer nur nach den deutschen Begriffen gesucht.