Heiner Lamprecht: Verarbeiten von XML Daten langsam

Hallo,

ich arbeite gerade an einer JavaScript-Applikation, die unter anderem eine XML-Liste mit relativ vielen Elementen runterlädt und verarbeitet.  Dabei habe ich festgestellt, dass der Firefox deutlich schneller arbeitet als der IE.  In Zahlen:  FF braucht pro Eintrag ca. 2 msec., während IE bei 10 bis 12 msec. liegt, gemessen nur für's verarbeiten, Runterladen ist irrelevant, da es den Browser nicht blockiert.

Die Liste hat mehrere 100 Einträge.  Jeder Eintrag besteht nur aus einem XML-Tag mit mehreren Attributen.

Mein Haupt-Problem ist eigentlich nicht, dass das Bearbeiten der Daten so lange dauert, sondern, dass der Browser in der Zeit nichts anderes tut.  Er zieht 100% CPU und man kann nichts anderes nebenher tun.

Gibt es irgendwelche Möglichkeiten, die Verarbeitung zu beschleunigen?  Oder kennt jemand gute Beschreibungen, was man bei der Verarbeitung von XML-Daten beim IE beachten sollte oder wo Fallstricke sind?  Oder generell im Zusammenhang mit Performance von JavaScript beim IE?

Eine Alternative, die ich schon mal angedacht hatte, war, die Daten nicht in einem großen Block, sondern in diversen kleineren Portionen runter zu laden.  Dann hat der Browser häufiger etwas zu tun, aber nicht mehr für so lange Zeit am Stück.  Oder mein Ihr, das wäre ineffektiv?

Bin für Ideen jederzeit offen.

Heiner

  1. Hallo Heiner,

    Bin für Ideen jederzeit offen.

    Waere es vielleicht eine Option, die XML-Dateien auf dem Server nach JSON zu konvertieren und dieses im Client auszuwerten?

    Gruß,

    Dieter

    1. Hallo Dieter,

      Hallo Heiner,

      Bin für Ideen jederzeit offen.
      Waere es vielleicht eine Option, die XML-Dateien auf dem Server nach JSON zu konvertieren und dieses im Client auszuwerten?

      Ist sicherlich mal einen Test wert.

      Heiner

  2. Hallo !

    :-)

    Ja, bei allen Vorteilen die XML hat, letrztlich ist's halt (String-)Parsing...

    Die Liste hat mehrere 100 Einträge.  Jeder Eintrag besteht nur aus einem XML-Tag mit mehreren Attributen.

    Mein Haupt-Problem ist eigentlich nicht, dass das Bearbeiten der Daten so lange dauert, sondern, dass der Browser in der Zeit nichts anderes tut.  Er zieht 100% CPU und man kann nichts anderes nebenher tun.

    Was passiert denn da genau ? XSLT ?

    Kannst Du zwischen Server und Client skalieren, also einen Teil der Verarbeitung serverseitig vornehmen ?

    Caching mal angedacht ?

    Beschreib bitte mal die Architektur. ( IIS / Apache, (serverseitiger) Parser, Sprachen, evtl COM-Komponenten, DB-Anbindung, Layering )

    Gruesse

    Holger

    --
    Aus dem Perl Styleguide:
    "Choose mnemonic identifiers. If you can't remember what mnemonic means, you've got a problem."
    1. Hallo,

      Die Liste hat mehrere 100 Einträge.  Jeder Eintrag besteht nur aus einem XML-Tag mit mehreren Attributen.

      Mein Haupt-Problem ist eigentlich nicht, dass das Bearbeiten der Daten so lange dauert, sondern, dass der Browser in der Zeit nichts anderes tut.  Er zieht 100% CPU und man kann nichts anderes nebenher tun.

      Was passiert denn da genau ? XSLT ?

      Nein.  Ich gehe die Liste durch und positioniere pro Eintrag je ein Icon abhängig von den Werten aus dem Eintrag.

      Kannst Du zwischen Server und Client skalieren, also einen Teil der Verarbeitung serverseitig vornehmen ?

      Ich fürchte, das wäre allenfalls minimal möglich.

      Caching mal angedacht ?

      Geht leider nicht.  Die Daten in der Datenbank ändern sich alle paar Sekunden ;-(

      Beschreib bitte mal die Architektur. ( IIS / Apache, (serverseitiger) Parser, Sprachen, evtl COM-Komponenten, DB-Anbindung, Layering )

      Als Server läuft da Apache, die ausgelieferten Daten werden per PHP aus einer Datenbank geholt und als XML an den Client geschickt.  Das Selektieren und Schicken der Daten ist aber unkritisch.  Zum einen passiert es im Hintergrund und blockiert den Browser nicht, zum anderen dauert es auch nicht lange.

      Heiner

      1. Hallo Heiner !

        Nein.  Ich gehe die Liste durch und positioniere pro Eintrag je ein Icon abhängig von den Werten aus dem Eintrag.

        Warum geht das nicht serverseitig mit XSLT ?

        Wenn Du schon mit PHP ein XML zusammenbaust, hast den Baum ja schon parsed im  Speicher. Dann koennte das XSLT auf einen schon parsed Baum zugreifen, wodurch einen Teil der moeglichen Serverlast bei XSLT - Verarbeitung ( XML laden und parsen ) schon mal wegfaellt.

        Leider kannst Du das XSL nicht zwischen Sitzungen im Speicher halten, aber Du koenntest das XSL Dokument vielleicht serialisieren und als Blob in die DB legen.
        Bin nicht der PHP-Guru, aber dann sollte auch das XSL nicht mehr parsed werden muessen.

        Wieviele Sessions hast Du denn zu Spitzenzeiten gleichzeitig ?
        Oder ist das noch nicht bestimmbar ?

        Gruss

        Holger

        P.S. "ge"-parsed kommt mir heute irgendwie zu germlish vor... ;-)

        --
        Aus dem Perl Styleguide:
        "Choose mnemonic identifiers. If you can't remember what mnemonic means, you've got a problem."
        1. Hallo Heiner !

          Nein.  Ich gehe die Liste durch und positioniere pro Eintrag je ein Icon abhängig von den Werten aus dem Eintrag.

          Warum geht das nicht serverseitig mit XSLT ?

          Weil die Position erst im Client fest steht.  Ich habe eine Karte, die der Benutzer im Client beliebig verschieben und zoomen kann.  Die angezeigten Elemente kommen mit ihren Positionen an und der Client legt dann fest, wo genau sie angezeigt werden sollen.  Wenn jetzt der Benutzer zoomt oder panned, werden die Icons entsprechend neu positioniert.  In dem Moment will ich natürlich nicht die Daten neu abrufen.

          Wenn Du schon mit PHP ein XML zusammenbaust, hast den Baum ja schon parsed im  Speicher.

          Naja, nicht wirklich.  Ich erzeuge einfach einen String, der dann an den Browser geschickt wird, kein XML-Document.

          Wieviele Sessions hast Du denn zu Spitzenzeiten gleichzeitig ?
          Oder ist das noch nicht bestimmbar ?

          Genau bestimmbar ist es nicht, aber es wird so im Bereich zwischen 10 und 20 gleichzeitig eingeloggten Usern liegen.  Die Serverlast ist vernachlässigbar.

          Heiner

          1. Hallo Heiner !

            Weil die Position erst im Client fest steht.  Ich habe eine Karte, die der Benutzer im Client beliebig verschieben und zoomen kann.  Die angezeigten Elemente kommen mit ihren Positionen an und der Client legt dann fest, wo genau sie angezeigt werden sollen.  Wenn jetzt der Benutzer zoomt oder panned, werden die Icons entsprechend neu positioniert.  In dem Moment will ich natürlich nicht die Daten neu abrufen.

            Zwar weiss ich nicht, ob mein Ansatz das wirklich loesen wuerde, aber grade bei Deiner Aufgabestellung wuerde ich das soweit wie irgend moeglich vom Server aufbereiten lassen.

            Du lieferst die Seite einmal aus, aber skalierst sie ggf. oft. Da wurde ich so wenig wie moeglich Rohdaten senden.

            Das XSLT schriebt die die Objekte die Du bewegen moechtest in JS-Variablen und beim Skalieren braucht der Browser nicht mehr den Tree zu parsen.

            Naja, nicht wirklich. Ich erzeuge einfach einen String,

            Ups ! ;-) Aber Du hast den String schon mal validiert, oder ?

            der dann an den Browser geschickt wird, kein XML-Document.

            Einzeln ??? Oder als XML-Data-Island ?

            Genau bestimmbar ist es nicht, aber es wird so im Bereich zwischen 10 und 20 gleichzeitig eingeloggten Usern liegen.

            Das ja moderat; noch ein Argument fuer serverseitige Vorverarbeitung.

            Gruesse
            Holger

            --
            Aus dem Perl Styleguide:
            "Choose mnemonic identifiers. If you can't remember what mnemonic means, you've got a problem."
            1. Hallo Holger,

              Weil die Position erst im Client fest steht.  Ich habe eine Karte, die der Benutzer im Client beliebig verschieben und zoomen kann.  Die angezeigten Elemente kommen mit ihren Positionen an und der Client legt dann fest, wo genau sie angezeigt werden sollen.  Wenn jetzt der Benutzer zoomt oder panned, werden die Icons entsprechend neu positioniert.  In dem Moment will ich natürlich nicht die Daten neu abrufen.

              Zwar weiss ich nicht, ob mein Ansatz das wirklich loesen wuerde, aber grade bei Deiner Aufgabestellung wuerde ich das soweit wie irgend moeglich vom Server aufbereiten lassen.

              Du lieferst die Seite einmal aus, aber skalierst sie ggf. oft. Da wurde ich so wenig wie moeglich Rohdaten senden.

              Die Daten ändern sich aber alle 30 Sekunden.  Ich muss also regelmäßig ein Update machen, egal, ob in der Zwischenzeit etwas an der Karte verändert worden ist.

              Das XSLT schriebt die die Objekte die Du bewegen moechtest in JS-Variablen und beim Skalieren braucht der Browser nicht mehr den Tree zu parsen.

              Mag sein, dass ich gerade auf dem Schlauch stehe.  Wenn ich Dich richtig verstehe, erfolgt die XSLT Transformation auf dem Server, richtig?  Wie werden jetzt die Daten an den Client übertragen?  Kann ich einfach JS-Code übertragen, der mir z.B. einen großen Array füllt?  Wäre das schneller?

              Naja, nicht wirklich. Ich erzeuge einfach einen String,

              Ups ! ;-) Aber Du hast den String schon mal validiert, oder ?

              Ja.  Da die Erzeugung relativ simpel ist, kann man davon ausgehen, dass die Daten auch immer valide sind.  Ich habe eine Zeitlang parallal jede Ausgabe zusätzlich in eine Datei umgelenkt und das dann kontrolliert.

              der dann an den Browser geschickt wird, kein XML-Document.

              Einzeln ??? Oder als XML-Data-Island ?

              Die XML Daten werden getrennt von der eigentlichen HTML-Seite übertragen und regelmäßig im Hintergrund neu runtergeladen.

              Grüße,

              Heiner

              1. Hallo !

                Das XSLT schriebt die die Objekte die Du bewegen moechtest in JS-Variablen und beim Skalieren braucht der Browser nicht mehr den Tree zu parsen.

                Mag sein, dass ich gerade auf dem Schlauch stehe.  Wenn ich Dich richtig verstehe, erfolgt die XSLT Transformation auf dem Server, richtig?  Wie werden jetzt die Daten an den Client übertragen?  Kann ich einfach JS-Code übertragen, der mir z.B. einen großen Array füllt?  Wäre das schneller?

                Genau so hab ich das gemeint:
                XSLT gibt Dir einen JS Variblen-Block aus. Das ist aber smeantisch nicht ganz trivial; da war mal was hier im Forum im letzten Monat .

                Ob's schneller ist, weiss ich natuerlich nicht, aber Du entkoppeltst die Neuskalierung der Karte, physisch vom Parsing des XML. Sollte also.

                Die Idee mit dem  serialisierten XSL-Doc koennte Sinn machen; spart fopen,  freads und komplettes Durchhashen.

                Statt des (XML-Strings) konntest Du auch pro Update-Zyklus ein XML-Doc serialisieren.

                Oder Du serialisierst gleich den Ergebnis JS-String - das ist natuerlich am effizientesten. Nur noch einmal XSLT pro Zyklus !
                => Ja, das ist gut ! Weil am billigsten <=

                Eigentlich braechtest Du dann gar kein XML mehr; du koenntest ja auch
                DB => JS direkt transformieren; aber vielleicht willst Du das Layering haben.

                Poste bitte mal wie's weitergeht !

                Gruesse

                Holger

                --
                Aus dem Perl Styleguide:
                "Choose mnemonic identifiers. If you can't remember what mnemonic means, you've got a problem."
  3. Hallo Heiner,

    ich arbeite gerade an einer JavaScript-Applikation, die unter anderem eine XML-Liste mit relativ vielen Elementen runterlädt und verarbeitet.  Dabei habe ich festgestellt, dass der Firefox deutlich schneller arbeitet als der IE.

    Mal dahingeraten: Hat Deine XML-Liste womöglich eine DTD? Eine interne oder gar eine externe? Meines Wissens unterscheiden sich die XML-Parser von Mozilla und Microsoft nur darin, dass letzterer Parser ein validierender Parser ist, d.h. die XML-Datei nicht nur auf Wohlgeformtheit sondern auch auf Validität gegenüber der DTD überprüft – was natürlich extra Cyclen verbrät, auch wenn das nicht so großartig sein sollte. Allerdings glaube ich auch nicht, dass bei Microsoft Idioten arbeiten. Wenn Validität nach DTD für Dich nicht so wichtig ist, wäre dann mein Ansatz, die DTD einfach wegzulassen.

    Warnung: Ich hab keine Ahnung der Interna von MSXML, das ist jetzt einfach mal so in den Wind geraten.

    Mein Haupt-Problem ist eigentlich nicht, dass das Bearbeiten der Daten so lange dauert, sondern, dass der Browser in der Zeit nichts anderes tut.  Er zieht 100% CPU und man kann nichts anderes nebenher tun.

    Ansonsten wäre natürlich ein Beispiel Deines jetzigen JS-Quellcodes und eventuell der zu verarbeitenden XML-Daten sinnvoll.

    Bin für Ideen jederzeit offen.

    Ich würde mich dem JSON-Vorschlag übrigens anschliessen.

    Tim

    1. Hallo Heiner,

      ich arbeite gerade an einer JavaScript-Applikation, die unter anderem eine XML-Liste mit relativ vielen Elementen runterlädt und verarbeitet.  Dabei habe ich festgestellt, dass der Firefox deutlich schneller arbeitet als der IE.

      Mal dahingeraten: Hat Deine XML-Liste womöglich eine DTD? Eine interne oder gar eine externe?

      Nein.  DTD wird nicht verwendet.

      Mein Haupt-Problem ist eigentlich nicht, dass das Bearbeiten der Daten so lange dauert, sondern, dass der Browser in der Zeit nichts anderes tut.  Er zieht 100% CPU und man kann nichts anderes nebenher tun.

      Ansonsten wäre natürlich ein Beispiel Deines jetzigen JS-Quellcodes und eventuell der zu verarbeitenden XML-Daten sinnvoll.

      Die XML Datei sieht grob so aus:

      ---------------------------------------------------------------
      <?xml version="1.0"?>
      <List>
          <CreatedAt>123456789</CreatedAt>
          <Entry id="abc" t="0" n="bla fasel" s="ABCD" lt="50,123"
             ln="10,543" c="17" h="25" m="293645" />
          <Entry id="def" t="1" n="foo" s="POIU" lt="51,323"
             ln="9,1422" c="51" h="47" m="412145" />
          <Entry id="asw" t="0" n="bar" s="ASWE" lt="49,164"
             ln="10,142" c="82" h="103" m="203477" />
      </List>
      ---------------------------------------------------------------

      Von den Entry-Tags gibt es ziemlich viele.  Die Werte lt und ln werden benutzt, um Icons in der Seite zu positionieren (Positionen in einer verschiebbaren Karte).  Der Rest dient dazu, die Icons auszuwählen und eine Art Tooltipp zu erzeugen.

      Die Verarbeitung sieht dann grob so aus:

      ---------------------------------------------------------------
      var content = '';
      var tagList = xmlDocument.getElementsByTagName("Entry");
      var listCount = tagList.length;

      for(var i = 0; i < listCount; i++)
      {
        var pos = tagList.item(i);
        var posData = new Array();

      posData[pos.attributes[0].nodeName] = pos.attributes[0].nodeValue;
        posData[pos.attributes[1].nodeName] = pos.attributes[1].nodeValue;
        posData[pos.attributes[2].nodeName] = pos.attributes[2].nodeValue;
        posData[pos.attributes[3].nodeName] = pos.attributes[3].nodeValue;
        posData[pos.attributes[4].nodeName] = pos.attributes[4].nodeValue;
        posData[pos.attributes[5].nodeName] = pos.attributes[5].nodeValue;
        posData[pos.attributes[6].nodeName] = pos.attributes[6].nodeValue;
        posData[pos.attributes[7].nodeName] = pos.attributes[7].nodeValue;
        posData[pos.attributes[8].nodeName] = pos.attributes[8].nodeValue;

      ...
      Hier kommen jetzt ein paar Berechnungen.
      Auskommentieren hat keine nennenwerten Auswirkungen auf
      die Performance
      ...

      content += "\n<img src="" + __ICON_PATH__ + imgName + ""
          onclick="getDetails('" + posData['id'] + "');" id="" + id +
          "" " + "style="position: absolute; top: " +
          (parseInt(pos['y']) - iconOffset) + "px; " +
          "left: " + (parseInt(pos['x']) - iconOffset) +
          "px; z-index: " + layer + ";" " +
          "onmouseover="showLabel('" + posData['n'] + "','" +
          posData['s'] + "','" + posData['c'] + "','" +
          posData['h'] + "','" + posData['m'] + "', this)" " +
          "onmouseout="hideLabel();"/>";

      layer++;

      }

      document.getElementById(p_Layer).innerHTML = content;
      ---------------------------------------------------------------

      Ich habe ein paar Dinge weggelassen.  Aber wie gesagt, diese Sachen wirken sich nicht wirklich auf die Performance aus.  Wenn ich es auskommentiere, geht es insgesamt ca 5% schneller.

      Bin für Ideen jederzeit offen.

      Ich würde mich dem JSON-Vorschlag übrigens anschliessen.

      Dann sollte ich mir das wohl wirklich mal anschauen :-)

      Heiner

      1. hi,

        document.getElementById(p_Layer).innerHTML = content;

        Hast du mal geprüft, ob das vielleicht die Bremse (oder zumindest Teil des Flaschenhalses) sein könnte?
        (Also vor dieser Aktion mal messen, wenn das XML geparst und verarbeitet, aber das Ganze noch nicht ins Dokument reingeklebt wurde.)

        innerHTML muss nicht unbedingt performanter sein, als Erzeugen von Nodes und Einhängen über's DOM.

        Zu diesem Schluss kommt Danny Goodman in seinem Artikel Dynamic HTML Tables: Improving Performance:
        "Assembling DOM objects in memory is commonly more efficient than extensive string concatenation and assignment to innerHTML properties."

        Das bezieht sich dort zwar auf das dynamische Erstellen von Tabellen, aber ggf. könntest du ja auch bei deinem Vorhaben damit etwas gewinnen.

        Auch das dort beschriebene Arbeiten mit DocumentFragment verdient eine Betrachtung - soll wesentlich fixer sein als das wiederholte direkte Einhängen von Nodes ins eigentliche Dokument:
        "In timing tests, it consistently outpaced all approaches shown earlier, in all tested browsers except Safari."

        Kleiner Nachteil: Im IE erst ab Version 6 verfügbar.

        gruß,
        wahsaga

        --
        /voodoo.css:
        #GeorgeWBush { position:absolute; bottom:-6ft; }