Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When overwriting native Map, use one internally for O(1) lookups #326

Open
ljharb opened this issue Apr 7, 2015 · 13 comments
Open

When overwriting native Map, use one internally for O(1) lookups #326

ljharb opened this issue Apr 7, 2015 · 13 comments

Comments

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

Even when overwriting native Map that doesn't comply with the spec, we should use the native Map internally rather than our O(n) slow path for object keys.

@cscott
Copy link
Collaborator

cscott commented Apr 7, 2015

Quite possibly. Right now we test a variety of things, and if any of them are not spec-compliant, we fall back to our own from-scratch implementation.

Maps (and Sets) are common enough that we perhaps should add a middle-ground type of fallback, where we substantially use the native implementation but only override a few things.

But profiling seems to indicate that's not actually the problem/common case with our current Map implementation. Instead, the slow thing seems to be the defineProperties calls on each instance. If we restructure to inherit property definitions from a prototype object (since assignment preserved enumerability we can still assign to our child object) we could potentially see a substantial speedup.

Not sure that optimizing the "object key" case is really the thing that needs doing. Few people are using non-string/non-numeric keys as far as I can tell.

But in any case, we should take care so as not to explode the set of possible configurations we need to test.

@ljharb
Copy link
Collaborator Author

ljharb commented Apr 7, 2015

The shim's primary goal is correctness, but performance is next, so I'd love to get both whenever possible.

I'd love to see PRs that made any "clobber the broken native implementation" code more performant.

@zloirock
Copy link

zloirock commented Apr 7, 2015

If you interesting, how works collection polyfilling in core-js:

In most case we have fully spec-complying native collection.

If you wanna fake subclassing - you can also add subclassing-friendly wrapper to this logic ;) I don't think adding not spec-complying feature is a good idea, considering your position about size in environment w/o descriptors.

@ljharb
Copy link
Collaborator Author

ljharb commented Apr 8, 2015

@zloirock what do you do for browsers like Safari 8 which has entries/keys/values methods that work in for..of but not as independent iterators?

What do you do in engines that don't have a native Map at all? (Note that mutating objects to add an ID is a far worse violation of the spec than O(n))

@zloirock
Copy link

zloirock commented Apr 8, 2015

@ljharb

what do you do for browsers like Safari 8 which has entries/keys/values methods that work in for..of but not as independent iterators?

Additional buggy iterators check - fully replace collection. Possible emulate iterators by using forEach (used it before), but it creates some problems and slow.

mutating objects to add an ID is a far worse violation of the spec than O(n))

Strongly disagree.

What do you do in engines that don't have a native Map at all

Entries chain and O(1) index for all keys except frozen objects.

@ljharb
Copy link
Collaborator Author

ljharb commented Apr 8, 2015

Mutation is clearly a violation of the spec. You can certainly make your own decision about which violation is worse or better, but I'd be surprised if many developers expected mutation more than they expected slowness.

@zloirock
Copy link

zloirock commented Apr 8, 2015

That's your opinion, I will not persuade you. I just suggest ways to fix native collections without complete replacement.

ljharb added a commit to ljharb/es6-shim that referenced this issue Apr 8, 2015
…to preserve as much of the original implementation as possible.

Relates to paulmillr#326.
ljharb added a commit to ljharb/es6-shim that referenced this issue Apr 8, 2015
…to preserve as much of the original implementation as possible.

Relates to paulmillr#326.
ljharb added a commit to ljharb/es6-shim that referenced this issue Apr 8, 2015
…to preserve as much of the original implementation as possible.

Relates to paulmillr#326.
ljharb added a commit to ljharb/es6-shim that referenced this issue Apr 9, 2015
…to preserve as much of the original implementation as possible.

Relates to paulmillr#326.
@ljharb
Copy link
Collaborator Author

ljharb commented Apr 9, 2015

With #328, there's still some browsers on the Map/Set` spectrum between IE 9 and latest Chrome that do have native collections, but have been totally clobbered - but that PR should preserve much or all of the native implementation.

I've also corrected the subclassing tests that were incorrectly using Map.call(this) - basically, to subclass Map and Set, Object.setPrototypeOf must be supported or shimmable.

ljharb added a commit to ljharb/es6-shim that referenced this issue Apr 9, 2015
…to preserve as much of the original implementation as possible.

Relates to paulmillr#326.
ljharb added a commit to ljharb/es6-shim that referenced this issue Apr 9, 2015
…to preserve as much of the original implementation as possible.

Relates to paulmillr#326.
@ljharb
Copy link
Collaborator Author

ljharb commented Apr 9, 2015

There's still a few browers (Safari 8 in particular) where we're clobbering the entire implementation, so we still need to look into that here.

@zloirock Thanks for your persistence! Despite that we disagree on priorities of performance versus non-performance-correctness, your suggestions have helped me to improve the former without sacrificing the latter, so it's much appreciated.

@medikoo
Copy link

medikoo commented Apr 12, 2015

If I read the spec correctly, it describes key search with O(n) algorithm.
Of course I don't mean that it should be followed, but my question is: On which basis it is assumed that native implementations internally do O(1)?
Is it observed from benchmarks, read from their source code, or is it just no-brainer assumption, taking that they may have some kind of id references to objects in C layer?

@medikoo
Copy link

medikoo commented Apr 12, 2015

@zloirock great thanks, that answers perfectly. I misread the spec then.

@ljharb
Copy link
Collaborator Author

ljharb commented Apr 12, 2015

The reality is that prior to native ES6, there is no "other mechanism" available. The spec doesn't mention mutating in http://www.ecma-international.org/ecma-262/6.0/#sec-map.prototype.set, which means that that must never be any mutation of key or value.

Any way to implement O(1) without mutation or native Maps is welcome, but I've not seen it yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants