Gibt es Leistungsvorteile beim "Sperren" von JavaScript-Objekten?


74

JavaScript 1.8.5 (ECMAScript 5) fügt einige interessante Methoden hinzu, die zukünftige Änderungen eines übergebenen Objekts mit unterschiedlichem Grad an Gründlichkeit verhindern:

Vermutlich geht es hauptsächlich darum, Fehler zu erkennen: Wenn Sie wissen, dass Sie ein Objekt nach einem bestimmten Punkt nicht mehr ändern möchten, können Sie es sperren, sodass ein Fehler ausgegeben wird, wenn Sie versehentlich versuchen, es später zu ändern. (Vorausgesetzt, Sie haben das getan "use strict";.)

Meine Frage: Gibt es in modernen JS-Engines wie V8 einen Leistungsvorteil (z. B. schnellere Nachschlagen von Eigenschaften, reduzierter Speicherbedarf) beim Sperren von Objekten mit den oben genannten Methoden?

(Siehe auch John Resigs nette Erklärung - erwähnt jedoch nicht die Leistung.)


Antworten:


82

Es gibt keinen Leistungsunterschied seit mindestens Chrome 47.0.2526.80 (64-Bit).

Testing in Chrome 6.0.3359 on Mac OS 10.13.4
-----------------------------------------------
Test               Ops/sec
non-frozen object  106,825,468  ±1.08%  fastest
frozen object      106,176,323  ±1.04%  fastest

Leistungstest (verfügbar unter http://jsperf.com/performance-frozen-object ):

  const o1 = {a: 1};
  const o2 = {a: 1};

  Object.freeze(o2);

  // Non-frozen object:
  for(var key in o1);

  // Frozen object:
  for(var key in o2);

Update 30.10.2019 : Bei Chrome 78.0.3904 (64-Bit) gibt es keinen Leistungsunterschied.

Update 17.09.2019 : Bei Chrome 76.0.3809 (64-Bit) gibt es keinen Leistungsunterschied.

Update 03.05.2018 : Bei Chrome 66.0.3359 (64-Bit) gibt es keinen Leistungsunterschied.

Update 06.03.2017 : Bei Chrome 56.0.2924 (64-Bit) gibt es keinen Leistungsunterschied.

Update 13.12.2015 : Bei Chrome 47.0.2526.80 (64-Bit) gibt es keinen Leistungsunterschied.


Mit Chrome 34 schneidet ein eingefrorenes Objekt im Testfall von @ pimvdb etwas besser ab als ein nicht eingefrorenes (Ergebnisse unten). Der Unterschied scheint jedoch nicht groß genug zu sein, um die Verwendung dieser Technik für Leistungsvorteile zu rechtfertigen.

http://jsperf.com/performance-frozen-object

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  105,250,353  ±0.41%  3% slower
frozen object      108,188,527  ±0.55%  fastest

Das Ausführen der Testfälle von @ kangax zeigt, dass beide Versionen des Objekts ziemlich gleich funktionieren:

http://jsperf.com/performance-frozen-object-prop-access

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  832,133,923  ±0.26%  fastest
frozen object      832,501,726  ±0.28%  fastest

http://jsperf.com/http-jsperf-com-performance-frozen-object-instanceof

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  378,464,917  ±0.42%  fastest
frozen object      378,705,082  ±0.24%  fastest

Ihre Antwort ist gut, ich habe sie + 1 bearbeitet, aber Sie sollten die jetzt veraltete Antwort bearbeitet haben, um die Dinge richtig zu machen.
Nicocube

Vielen Dank für Ihr Feedback, @Nicocube. Ich war mir nicht sicher, ob es besser ist, eine veraltete Antwort zu bearbeiten oder eine neue zu schreiben. Ich habe gesehen, dass beide Ansätze für den Stapelüberlauf verwendet werden, aber Ihr Vorschlag ist sinnvoll.
Jan Molak

Ihre Links zu jsperf sind fehlerhaft. Haben something went wrongSie eine Kopie des Codes, den Sie für Ihren Anspruch getestet haben?
Ferrybig

Sieht nach einem Problem mit jsperf aus, da auch keine anderen Links zu ihnen funktionieren ... Ich werde versuchen, das Beispiel hier zu veröffentlichen, wenn sie wieder online sind.
Jan Molak

2
Ich hatte einen Fall, in dem der Aufruf von Object.freeze () an sich negativ für die Leistung war, weil ich viele verschiedene kleine Objekte hatte (denken Sie an Knoten für einen großen Baum). Die Konstruktion erwies sich als zu schwer, daher ließ ich Object.freeze () fallen.
Nalply

14

Update: Da diese Antwort ursprünglich geschrieben wurde, wurde der Fehler in V8 behoben , der dieses Problem verursacht hat. Siehe die Antwort von Jan Molak für weitere Informationen.


In Google Chrome (also V8) iteriert ein eingefrorenes Objekt 98% langsamer als ein normales Objekt.

http://jsperf.com/performance-frozen-object

Test name*              ops/sec

non-frozen object    32,193,471
frozen object           592,726

Wahrscheinlich liegt das daran, dass diese Funktionen relativ neu und wahrscheinlich noch nicht optimiert sind (aber das ist nur meine Vermutung, ich kenne den Grund ehrlich gesagt nicht).

Jedenfalls empfehle ich wirklich nicht, es für Leistungsvorteile zu verwenden, da dies anscheinend keinen Sinn ergibt.


* Der Code für den Test lautet:

var o1 = {a: 1};
var o2 = {a: 1};

Object.freeze(o2);

Test 1 (nicht gefrorenes Objekt):

for(var key in o1);

Test 2 (gefrorenes Objekt):

for(var key in o2);

7
Das klingt eher nach einem V8-Fehler als nach etwas, das nicht optimiert ist. Hinweis Object.keys ist nur 72% langsamer
Raynos

3
@ Raynos: Guter Punkt; auch Object.keyssollte nicht langsamer . Ich bin damit einverstanden, dass es eher wie ein Fehler ist, da das Einfrieren kein Leistungseinbruch sein sollte. eher das Gegenteil.
Pimvdb

3
Interessanter Fund. Ich habe andere Browser überprüft - nächtliche FF, WebKit, Opera und keiner von ihnen hat eine so verrückte Verlangsamung (siehe jsperf ). Sieht definitiv aus wie ein Käfer. Ich habe ein Problem im V8-Tracker eingereicht - code.google.com/p/v8/issues/detail?id=1858
kangax

20
Jetzt (in Chrome 34 und im Jahr 2014) scheint die Iteration eines eingefrorenen Objekts um 24 Prozent schneller zu iterieren.
Msung

7
Dies ist jetzt veraltet, Downvoting.
Emil Eriksson

13

Theoretisch können Sie durch das Einfrieren eines Objekts stärkere Garantien für die Form eines Objekts abgeben.

Dies bedeutet, dass die VM die Speichergröße komprimieren kann.

Dies bedeutet, dass die VM die Suche nach Eigenschaften in der Prototypenkette optimieren kann.

Dies bedeutet, dass Live-Referenzen einfach nicht mehr live sind, da sich das Objekt nicht mehr ändern kann.

In der Praxis führen JavaScript-Engines diese aggressive Optimierung noch nicht durch.


1
In der Praxis gibt es in den meisten Motoren aus Speichersicht für ein bestimmtes Objekt wenig zu gewinnen. Ebenso werden Eigenschaftensuchen von Prototypen bereits zwischengespeichert (die Leistung der meisten integrierten Funktionen wäre schrecklich, wenn dies nicht der Fall wäre).
Gsnedders

Richtig. Sie sollten in der Lage sein, mehr als nur einen Inline-Cache zu haben, da Sie den gesamten Lesevorgang inline machen können, wenn Sie einen bekannten Wert haben.
gsnedders

(Beachten Sie, dass Inlining nur einen bestimmten Betrag erhält: Sie möchten beispielsweise keine Inline-Zeichenfolgen
einbinden

@ Raynos Leider können wir den Prototyp eines Objekts auch nach dem Einfrieren noch ändern. Eine Möglichkeit, eine wirklich stabile Form zu erhalten, besteht darin, den Prototyp auf einzustellen null. Eine andere Möglichkeit besteht darin, die gesamte Prototypenkette zusammen mit Object.prototype(klingt beängstigend) einzufrieren .
Tomékwi

"Es bedeutet, dass Live-Referenzen einfach nicht mehr live sind, weil sich das Objekt nicht mehr ändern kann" - Ich denke, dies ist ein Missverständnis dessen, was es bedeutet, wenn eine Referenz live ist . Eine Referenz, die nicht geändert werden kann, kann weiterhin aktiv sein.
Davmac


2

Wenn Sie bei der Durchführung von Objekt interessiert sind Schöpfung (wörtliche vs gefrieren vs versiegelt vs Immutable.Map), habe ich einen erstellten Test auf jsPerf , dass aus zu überprüfen.

Bisher hatte ich nur die Möglichkeit, es in Chrome 41 und Firefox 37 zu testen. In beiden Browsern dauert die Erstellung eines eingefrorenen oder versiegelten Objekts etwa dreimal länger als die Erstellung eines Literals - während die Leistung Immutable.Mapetwa 50-mal schlechter ist als das wörtliche.


FWIW, die 3 × Strafe ist besser als ich erwartet hatte. Ich verwende gerne eingefrorene Objekte, außer in leistungskritischen Situationen.
Tomékwi

Es ist ziemlich schlimm, wenn es dreimal so lange dauert ... laut dem verknüpften jsperf ist dieser Leistungsfehler immer noch vorhanden (Chrome 49). Object.freeze mit einem Literal sollte vom Compiler als Absicht erkannt werden, dass das Objekt eingefroren wird. Das heißt, es müssen nicht zwei separate Schritte sein, einer der Objekterstellung und einer des Einfrierens. Das Erstellen (und Zugreifen auf) eines eingefrorenen Objekts sollte gleich oder schneller sein. Sieht nach einem Problembericht aus.
Robert Monfera

Ich kann bestätigen. Durch die Implementierung unveränderlicher Datenstrukturen in JavaScript wurde das Einfrieren interner Knoten erheblich verlangsamt. Es ist eine schlechte Wahl, wenn Sie überall viele neue Objekte erstellen lassen. Alle Pfadkopien, die Sie mit unveränderlichen Datenstrukturen durchführen, summieren sich am Ende zu viel. Es geht um 2x-3x, aber meiner Meinung nach eher um einen 2x-Faktor als um einen 3x-Faktor.
John Leidegren

-1

Der einzige Grund, den ich für diese Methoden im Produktionscode sehe, ist, dass Sie aus Gründen der Integrität versiegelte oder eingefrorene Objekte haben können.

Zum Beispiel schreibe ich eine kleine Bibliothek, die einfach großartig funktioniert und Ihnen eine Reihe von Methoden in einem Objekt bietet, aber ich möchte nicht, dass Sie meine Eigenschaften oder Methoden ändern oder überschreiben. Ich sage nicht, dass ich Sie daran hindern kann, aber ich kann versuchen, Sie daran zu hindern, es aus Versehen zu tun, was vielleicht wichtiger ist.

Außerdem können diese Methoden in Umgebungen, die nichts über sie wissen, leicht "shim" werden, indem nur das ursprüngliche Objekt zurückgegeben wird. Natürlich hätte es dann keine Wirkung.

Ich sehe keine leistungsbezogenen Gründe dafür.


1
Integritätszwecke sind für die Entwicklung
Raynos

@ Raynos: Ich sehe sowieso den Zweck für Bibliotheken. Wie gesagt, eher die Integrität von Objekten vor unerwünschten Änderungen schützen.
Andy
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.