Are frozen Objects faster ?

# Andrea Giammarchi (13 years ago)

If not, any particular reason these are not, being immutable and representable similar to a C struct ?

If they could, any idea which engine is planning to optimize and when?

Thanks and Best

# Kevin Gadd (13 years ago)

Frozen and sealed objects are both dramatically slower in most JS engines I've tested. In the ones where they're not dramatically slower they are never faster.

The last time I asked on the mozilla and v8 bug trackers I was informed that there is no plan to optimize for these features and that the design of the respective JS engines would make such optimizations difficult anyway.

(I find this extremely unfortunate.)

# Mark S. Miller (13 years ago)

Likewise. And unlikely.

Based on history, I suggest that the best way to get this situation fixed is benchmarks. Either create a new benchmark or a variation of an existing benchmark. For example, if someone created a variant of SunSpider in which all objects that don't need to not be frozen were frozen, and posted the measurements, that would help get everyone's attention. The situation might then improve rapidly.

# Herby Vojčík (13 years ago)

(I find this extremely unfortunate.)

I, on the other hand, find it fortunate. Otherwise, some people would prematurely freeze objects to gain speed. It would be same <del>disaster</del> akwardness as v8 did with removing delete in favour of = null.

# Mark S. Miller (13 years ago)

What are you referring to? What 'delete' was removed?

# Andreas Rossberg (13 years ago)

On 14 February 2013 19:01, Kevin Gadd <kevin.gadd at gmail.com> wrote:

The last time I asked on the mozilla and v8 bug trackers I was informed that there is no plan to optimize for these features

Well, regarding V8, there is now. :)

# Andreas Rossberg (13 years ago)

If someone created a variant of SunSpider in which all objects that don't need to not be frozen were frozen, and posted the measurements, that would help get everyone's attention.

Gulp. Mark, you didn't just suggest using SunSpider as the basis for a future benchmark, did you? My eyes must have tricked me.

# Mark S. Miller (13 years ago)

It was just an example. Feel free to make an alternative and more informed suggestion ;).

# David Bruant (13 years ago)

I suggest that the best way to get this situation fixed is benchmarks.

Agreed 100%

Either create a new benchmark or a variation of an existing benchmark.

Choice of the specific benchmark aside, this is a very good idea. This could also be applied to strict mode.

# Herby Vojčík (13 years ago)

What are you referring to? What 'delete' was removed?

I meant "de facto". People wanting to remove property bar from foo do not write delete foo.bar anymore; they (at least some significant subset) have learned to write foo.bar = null; or foo.bar = undefined;. The reason is perf - delete deoptimized hidden classes.

# Andreas Rossberg (13 years ago)

And with ES6, those people will hopefully realise that for those cases, using a Map is a cleaner alternative anyway.

# Herby Vojčík (13 years ago)

No, it is another scenario. If an object is used as a Map, it should degrade to HashMap, it's ok.

The problem is proliferation of foo.bar = null in "normal" code, where sometimes you want to remove some property (maybe it was an expando, or it is realy not needed any more in the actual phases of the lifecycle). In such cases, doing delete would degrade your optimized instance into a Hash. Thus, people do foo.bar = null even if what they want to do is delete foo.bar.

# Kevin Gadd (13 years ago)

This is definitely the case. Recent performance guidance from V8 developers strongly discouraged the use of 'delete' so I've gone to some lengths to avoid using it in my own code, mostly through design (trying to avoid scenarios that require the deletion of attributes). If this guidance isn't accurate anymore, I certainly never heard about it. Then again engine developers have said that they intentionally avoid publishing performance guidance since people continue to follow it after it becomes outdated - so I guess this is just support for that policy. :/

Anyway! I spent an hour or so building a simplified real-world benchmark for Object.freeze, by modifying a HTML5 game port to use Object.freeze at construction time for its immutable structured objects. These objects are basically simple tuples of integers/floats that are frequently constructed and passed around - think a Vector2 or Point data structure, etc.

This file has detailed timings from the latest versions of Chrome Canary and Firefox Nightly with the Object.freeze call disabled and enabled, respectively, along with a URL you can hit to run it yourself: dl.dropbox.com/u/1643240/freeze timings.txt

I'm pleased to report that Object.freeze does not seem to cause an enormous performance hit in v8 anymore. Hooray! Unfortunately, the call to Object.freeze itself shows up as a bottleneck in V8 profiles, and the 'freeze enabled' version is slower in both versions (though at least only slightly slower). So, I guess you could start using freeze in applications now if your users are all on latest FF/Chrome, and hope that in the future it will actually make you faster.

Feel free to let me know if you have any questions (though if they're questions about the benchmark, maybe don't spam es-discuss with them :) )

# Andreas Rossberg (13 years ago)

On 14 February 2013 20:56, Herby Vojčík <herby at mailbox.sk> wrote:

The problem is proliferation of foo.bar = null in "normal" code, where sometimes you want to remove some property (maybe it was an expando, or it is realy not needed any more in the actual phases of the lifecycle). In such cases, doing delete would degrade your optimized instance into a Hash. Thus, people do foo.bar = null even if what they want to do is delete foo.bar.

By definition, if you want to delete it, it's not a "normal" property. In most languages, you couldn't even do that. The reason that you can in JavaScript is mostly historic. (And fortunately, I don't see a "proliferation" of this in practice.)

# Andreas Rossberg (13 years ago)

On 14 February 2013 21:12, Kevin Gadd <kevin.gadd at gmail.com> wrote:

I'm pleased to report that Object.freeze does not seem to cause an enormous performance hit in v8 anymore. Hooray! Unfortunately, the call to Object.freeze itself shows up as a bottleneck in V8 profiles, and the 'freeze enabled' version is slower in both versions (though at least only slightly slower).

Thanks for doing the benchmark, very interesting!

That freeze bottleneck will hopefully vanish in the foreseeable future.

# Andrea Giammarchi (13 years ago)

the delete obj.property changes the shape of the object, right? so I think is more a problem of devs using objects as trash bin but this is the opposite scenario of a frozen object.

I understand ES5 descriptors are an overhead during an object lifecycle but I think a mechanism to make frozen object that fast would be a win while we wait for better options on statically defined types.

"Binary Arrays" are indeed frozen objects, at least in Firefox, and ultra fast: Object.isFrozen(new Float32Array()) // true in Firefox

Since these are ultra fast in Chrome too but not frozen, I believe there is already a way to speed up typed stuff (didn't check how it's done though) so I wonder how come Object.freeze() is not taking similar approach "typizing" behind the scene the object improving all static properties getters (probably dropping those getters where possible unless defined as such)

Will this cost more than now at freeze() time? I understand this might be undesired but think about libraries or modules that do not want to be extended, would like to be as fast as possible, and are loaded once and never again.

Thanks for all thoughts and info already provided, also agreed on some bench. I usually do that on jsPerf 'cause I don't know how to reach those bigger, widely tested, one.

Any hint here appreciated.

# Andreas Rossberg (13 years ago)

On 14 February 2013 21:36, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

"Binary Arrays" are indeed frozen objects, at least in Firefox, and ultra fast: Object.isFrozen(new Float32Array()) // true in Firefox

Since these are ultra fast in Chrome too but not frozen, I believe there is already a way to speed up typed stuff (didn't check how it's done though) so I wonder how come Object.freeze() is not taking similar approach "typizing" behind the scene the object improving all static properties getters (probably dropping those getters where possible unless defined as such)

Frozenness is largely irrelevant for typed arrays, since all array accesses are defined by a magic nameless getter/setter pair per the WebIDL spec.

# Andrea Giammarchi (13 years ago)

One more thought ... the best scenario ever would be the ability to define a frozen prototype and create already fixed shaped instance at runtime.

I think Object.freeze() semantic is not the right one to do this, but I am dreaming about something like this:

var MyStaticShapeConstructor = Object.createStaticConstructor(
  inheritFrom, // either null or actually not extremely important
  descriptors // descriptors we know
);

var instance = new MyStaticShapeConstructor;

We might discuss if the "constructor" property in descriptors should have a handy, exceptional, treatment (ie invoked with arguments)

{
  constructor: {
    value: function (a, b, c) {
      // invoked when new MyStaticShapeConstructor(1,2,3)
    }
  }
}

or simply encourage the usage of o.init(arg1, ..., argN);

I know this is way too much magic behind the scene but it would be straight forward from different points of view, at least for JS users, IMHO, and really easy to polyfill.

Thoughts on this would be much appreciated, thanks.

Apologies if already discussed and I have missed that thread.

# Andrea Giammarchi (13 years ago)

I wodner how come Firefox behaves like that then but I don't have tests to compare any difference between these two. I will write some,

# Kevin Gadd (13 years ago)

Ideally the JIT would, at runtime, just identify the pattern where your constructor ends with an Object.freeze(this) call, and turn it into the equivalent of an immutable, pass-by-value packed struct. IIRC v8 and SpiderMonkey are both able to do some of this already based on looking at your constructor and using shape information...

# Andrea Giammarchi (13 years ago)

that is assuming nobody ever call the constructor without new keyword and no inheritance is involved, since the sub constructor would be trapped behind the parent.

That would be even more magic ... I kinda like it, not sure is explicit enough though ... but yeah, if freeze(this) would do that, it would be awesome!

# Alex Russell (13 years ago)

On Thursday, February 14, 2013, Andreas Rossberg wrote:

On 14 February 2013 19:26, Herby Vojčík <herby at mailbox.sk <javascript:;>> wrote:

I meant "de facto". People wanting to remove property bar from foo do not write delete foo.bar anymore; they (at least some significant subset) have learned to write foo.bar = null; or foo.bar = undefined;. The reason is perf - delete deoptimized hidden classes.

And with ES6, those people will hopefully realise that for those cases, using a Map is a cleaner alternative anyway.

I think it's worth noting here that of course older features have seen heavier optimization. I honestly expect that the Map type will start much slower than it will eventually end up being, perhaps not in V8, but elsewhere. But slow and available often beats unavailable and/or non-standard. It's a complicated story to tell end-users, but anything else is misleading.

One hopes that any new feature we that gets wide implementation and is not explicitly performance oriented pays for itself on a semantic basis. Such features find their natural users prior to the optimization foot race kicking off, and there's nothing bad about any of that. The ideal world (that freeze() is now a poster child for) looks roughly like:

// Standard written, implementations arrive (not in that order) // ...time passes... Hooray! New features!

// ...time passes... // Users realize optimization is uneven Boo! They're slow! // Said without any sense of JS perf history

// ...time passes... // Features optimized Yay! They're fast!

# Aymeric Vitte (13 years ago)

Typed Arrays are not frozen on FF, they are not extensible, only new typed_array(nothing) is frozen (just bad luck for your example :-) ).

Reading this thread, it seems that I am not using good practices, because I am using quite often the object literal indexed with numbers (var a={};a[1]=something), and I am using delete to remove the values (the object litteral becomes a kind of array with holes), and I really expect delete to remove the property, not to assign it to null. I find it convenient (despite of the fact that properties enumeration order in that case is let to the appreciation of the js engine and can change depending on what you are doing), is this not correct/impacting a lot performances?

One day maybe there could be an annex in ES specs about good practices and performances, or does it exist somewhere?

# Andrea Giammarchi (13 years ago)

not sure I follow here ... I did ask if Object.freeze was faster and why, if not, 'cause faster is what I would expect after a probably slower operation as freeze could be.

Aymeric yes, that was unfortunate, also I don't understand all these different behaviors but point is, I think you can extend in Chrome, you cannot in Firefox and no idea why this choice (bu tI understand the implementation of static, fixed, type/shape)

So, it looks like is planned, but nobody knows when? Well, that's better than nothing :-)