Warum wird vor dem Entfernen nach einem Attribut mit der Punktnotation gesucht, anstatt das Attribut sofort zu entfernen?

8

Ich fragte diese Frage , und es stellte sich heraus Wenn beim Entfernen eines Attributs aus einem Element überprüft wird, ob das Element zuerst mit elem.xxx!==undefined existiert, wird die Laufzeit schneller. Proof .

Warum ist es schneller? Es gibt mehr Code, den du durchgehen und du musst der Methode removeAttribute() auf jeden Fall begegnen gehe darüber.

    
dayuloli 21.03.2014, 17:25
quelle

4 Antworten

5

Nun, zuerst müssen Sie wissen, dass elem.xxx nicht dasselbe ist wie elem.getAttribute() oder eine andere Methode relativ zum Attribut.

elem.xxx ist eine Eigenschaft eines DOM-Elements, während das Attribut und das Element im HTML-Code innerhalb des DOM ähnlich, aber unterschiedlich sind. Nehmen wir als Beispiel dieses DOM-Element: <a href="#"> und diesen Code:

%Vor%

Aber nehmen wir ein benutzerdefiniertes Attribut: <a custom="test">

%Vor%

Sie können also die Geschwindigkeit von beiden nicht wirklich vergleichen, da sie nicht das gleiche Ergebnis erzielen. Aber eines ist deutlich schneller, da Eigenschaften ein Schnellzugriffsdaten sind, während Attribute die get / hasAttribute DOM-Funktionen verwenden.

Nun, warum ohne die Bedingung ist schneller? Nur weil removeAttribute nicht interessiert, ist das Attribut fehlt, überprüfen Sie, ob es nicht ist.

Die Verwendung von hasAttribute vor removeAttribute ist also wie die doppelte Überprüfung, aber die Bedingung ist etwas langsamer, da überprüft werden muss, ob die Bedingung zum Ausführen des Codes erfüllt ist.

    
Karl-André Gagnon 21.03.2014 17:49
quelle
2

Ich habe den Verdacht, dass der Grund für die Geschwindigkeitssteigerung Trace-Bäume sind.

Trace Bäume wurden zuerst von Andreas Gal und Michael Franz von der Universität von Kalifornien, Irvine, in ihrer Arbeit Inkrementelle dynamische Codegenerierung mit Ablaufverfolgungsbäumen .

In seinem Blogbeitrag Tracing the Web Andreas Gal (der Co-Autor des Artikels) erklärt, wie das Verfolgen von Just-in-Time-Compilern funktioniert.

Um das Verfolgen von JIT-Compilern so sententiell wie möglich zu erklären (da mein Wissen über das Thema nicht tiefgehend ist), macht ein Tracing-JIT-Compiler Folgendes:

  1. Zunächst wird der gesamte Code interpretiert, der ausgeführt werden soll.
  2. Es wird ein Zählwert für die Anzahl von Malen gehalten, die jeder Code-Pfad ausgeführt wird (z. B. wie oft der true Zweig einer if Anweisung ausgeführt wird).
  3. Wenn die Anzahl der Code-Pfade größer als ein vordefinierter Schwellenwert ist, wird der Code-Pfad in Maschinencode kompiliert, um die Ausführung zu beschleunigen (z. B. führt SpiderMonkey Code-Pfade mehr als einmal aus)

Sehen wir uns nun Ihren Code an und verstehen Sie, was den Geschwindigkeitsschub verursacht:

Testfall 1: Überprüfen Sie

%Vor%

Dieser Code hat einen Code-Pfad (d. h. eine if -Anweisung). Denken Sie daran, dass Tracing-JITs nur Codepfade und nicht ganze Funktionen optimieren. Dies ist, was ich glaube, geschieht:

  1. Da der Code von JSPerf bewertet wird, wird er mehr als einmal ausgeführt (eine Untertreibung). Daher ist es in Maschinencode kompiliert.
  2. Allerdings verursacht es immer noch den zusätzlichen Funktionsaufruf an hasAttribute , der nicht JIT-kompiliert wird, da es nicht Teil des bedingten Codepfads ist (der Code zwischen den geschweiften Klammern).
  3. Obwohl der Code innerhalb der geschweiften Klammern schnell ist, ist die bedingte Überprüfung selbst langsam, da sie nicht kompiliert ist. Es wird interpretiert. Das Ergebnis ist, dass der Code langsam ist.

Testfall 2: Entfernen

%Vor%

In diesem Testfall haben wir keine bedingten Codepfade. Daher tritt der JIT-Compiler niemals ein. Der Code ist also langsam.

Testfall 3: Prüfung (Punktnotation)

%Vor%

Dies ist das gleiche wie im ersten Testfall mit einem signifikanten Unterschied:

  1. Die bedingte Überprüfung ist eine einfache Überprüfung der Nicht-Äquivalenz. Daher ist nicht der gesamte Aufwand eines Funktionsaufrufs erforderlich.
  2. Die meisten JavaScript-Interpreter optimieren einfache Äquivalenzprüfungen wie diese, indem sie einen festen Datentyp für beide Variablen annehmen. Da sich der Datentyp elem.xxx oder undefined nicht bei jeder Iteration ändert, macht diese Optimierung die bedingte Prüfung noch schneller.
  3. Das Ergebnis ist, dass die bedingte Überprüfung (obwohl interpretiert) den kompilierten Codepfad nicht signifikant verlangsamt. Daher ist dieser Code der schnellste.

Natürlich sind das nur Spekulationen von meiner Seite. Ich kenne die Interna einer JavaScript-Engine nicht und meine Antwort ist daher nicht kanonisch. Ich bin jedoch der Meinung, dass es sich um eine gute Vermutung handelt.

    
Aadit M Shah 21.03.2014 18:07
quelle
0

Ihr Beweis ist falsch ...

elem.class !== undefined wird immer als false ausgewertet und somit wird elem.removeAttribute("class") niemals aufgerufen, daher wird dieser Test immer schneller sein.

Die richtige Eigenschaft für elem ist className , z. B.:

%Vor%     
Graham 21.03.2014 19:05
quelle
0

Wie Karl-André Gagnon darauf hingewiesen hat, sind der Zugriff auf eine [native] JavaScript-Eigenschaft und das Aufrufen einer DOM-Funktion / -Eigenschaft zwei verschiedene -Operationen .

Einige DOM-Eigenschaften werden als JavaScript-Eigenschaften über die DOM IDL ; Diese sind nicht gleich wie adhoc JS-Eigenschaften und erfordern DOM-Zugriff. Auch wenn die DOM-Eigenschaften offengelegt werden, gibt es keine strikte Beziehung mit DOM-Attributen !

Zum Beispiel aktualisiert inputElm.value = "x" nicht das DOM-Attribut , obwohl das Element einen aktualisierten Wert anzeigt und meldet. Wenn das Ziel mit DOM-Attributen umgehen soll, ist die einzige korrekte Methode die Verwendung von hasAttribute/setAttribute , etc.

Ich habe daran gearbeitet, einen "fairen" Mikro-Benchmark für die verschiedenen Funktionsaufrufe abzuleiten, aber es ist ziemlich schwierig und es gibt viele von verschiedenen Optimierungen, die auftreten. Hier mein bestes Ergebnis , mit dem ich meinen Fall argumentieren werde.

Beachten Sie, dass no if oder removeAttribute vorliegt, um die Ergebnisse zu verwirren, und ich fokussiere nur auf den DOM / JS-Eigenschaftenzugriff. Außerdem versuche ich, die Behauptung auszuschließen, dass die Geschwindigkeitsdifferenz nur auf einen Funktionsaufruf zurückzuführen ist, und ich ordne die Ergebnisse zu, um eklatante Browseroptimierungen zu vermeiden. YMMV.

Beobachtungen:

  1. Der Zugriff auf eine JS-Eigenschaft ist fast . Dies ist zu erwarten 1,2

  2. Das Aufrufen einer Funktion kann höhere Kosten verursachen als der Zugriff auf direkte Eigenschaften 1 , ist aber bei weitem nicht so langsam wie DOM-Eigenschaften oder DOM-Funktionen . Das heißt, es ist nicht nur ein "Funktionsaufruf", der hasAttribute so viel langsamer macht.

  3. Der Zugriff auf DOM-Eigenschaften ist langsamer als der Zugriff auf native JS-Eigenschaft ; Die Leistung unterscheidet sich jedoch stark zwischen den DOM-Eigenschaften und den Browsern. Mein aktualisierter Micro-Benchmark zeigt einen Trend, dass der DOM-Zugriff - sei es via DOM-Eigenschaft oder DOM-Funktion - möglicherweise langsamer ist als der native JS-Eigenschaftenzugriff 2 .

Und zurück zum Anfang: Der Zugriff auf eine Nicht-DOM [JS] -Eigenschaft für ein Element ist grundlegend anders als der Zugriff auf eine DOM-Eigenschaft weniger ein DOM-Attribut , auf demselben Element. Es ist dieser grundlegende Unterschied, und Optimierungen (oder das Fehlen davon) zwischen den Ansätzen zwischen den Browsern, die für die beobachteten Leistungsunterschiede verantwortlich sind.

1 IE 10 macht einen cleveren Trick, bei dem der gefälschte Funktionsaufruf sehr schnell ist (und ich vermute, dass der Aufruf gelöscht wurde), obwohl er einen abgründigen JS-Eigenschaftenzugriff hat. Betrachtet IE jedoch einen Ausreißer oder lediglich eine Verstärkung, dass der Funktionsaufruf nicht ist, was das von Natur aus langsamere Verhalten einführt, so beeinträchtigt dies nicht mein Hauptargument: Es ist der Zugriff auf DOM das ist grundsätzlich langsamer.

2 Ich würde gerne sagen, DOM-Eigenschaft Zugriff ist langsamer, aber FireFox macht einige erstaunliche Optimierung von input.value (aber nicht img.src ). Es gibt eine besondere Magie, die hier passiert. Firefox optimiert das DOM-Attribut nicht.

Und verschiedene Browser können ganz andere Ergebnisse zeigen. Ich denke jedoch nicht, dass man irgendeinen "Zauber" mit dem if oder removeAttribute in Betracht ziehen muss, um zumindest das zu isolieren, was ich glaube. Leistungsproblem ": tatsächlich das DOM verwenden .

    
user2864740 21.03.2014 23:43
quelle