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

#219 - Improve Map performance #293

Merged
merged 14 commits into from
Feb 7, 2020
Merged

Conversation

neenhouse
Copy link
Contributor

@neenhouse neenhouse commented Aug 14, 2019

Addresses #219

Only has test plan. Implementation still pending.

@CLAassistant
Copy link

CLAassistant commented Aug 14, 2019

CLA assistant check
All committers have signed the CLA.

@neenhouse
Copy link
Contributor Author

First commit only adds a performance unit test that consistently fails IE11 locally (per contributing.md)

@JakeChampion
Copy link
Owner

@neenhouse Thanks for working on this, I've triggered a run on CircleCI for this.

In order for us to be able to accept your contribution though, we require you to have read and accepted our contributor license agreement.

@neenhouse neenhouse changed the title #219 - Test plan per contributing.md #219 - Improve Map performance Aug 15, 2019
@neenhouse
Copy link
Contributor Author

Test fails in CI in expected way 👍

@neenhouse
Copy link
Contributor Author

Took a first pass at the algorithm. Two notes:

  • I avoided climbing the ladder of abstraction for first pass, so some code (normalize key + retrieve from hash table) is not DRY yet.
  • When looking up functions and objects by key, algo currently assigns an internal id property to the object __meta__.id so it can identify the object in the internal hash table. There are alternatives to this approach that don't involve modifying the key function or object, but they won't be as performant.

I'd like to run a few more tests to compare performance between master + branch. Also want to try at least one algo that does not do the key modification approach and instead uses smaller hash tables that are iterated on to handle those lookups as edge cases.

@neenhouse
Copy link
Contributor Author

neenhouse commented Aug 15, 2019

Running local Karma unit test in Chrome 76.0.3809.100 (Official Build) (64-bit) comparing native implementation of Map vs polyfill-library:

  • Native Chrome: 86ms, 107ms, 110ms, 94ms, 96ms, 81ms, 107ms, 100ms
  • Polyfill Library (branch): 127ms, 116ms, 124ms, 116ms, 128ms, 116ms, 99ms, 121ms
  • Polyfill Library (master): fails consistently (after 1000ms when Map size reaches around 6000 / 10000 records)

Screen Shot 2019-08-15 at 8 37 47 AM

So reasonably close performance wise to the native implementation in Chrome.

@JakeChampion
Copy link
Owner

Looking great @neenhouse!

@neenhouse
Copy link
Contributor Author

@JakeChampion Think this is ready for 1st review. I improved the marking of objects/functions by introducing randomness to avoid collisions if lib is loaded multiple times and abuse. Using reference comparison based algos even in smaller hash tables eventually leads to the same O(n) perf problem if a particular key type is over-used, so I thought this would be an acceptable trade off (thought also since primitives are usually used as keys in most UIs I've worked on, maybe a moot issue?).

@zloirock
Copy link

zloirock commented Aug 15, 2019

  • Metadata should be unobservable (Object.assign, Object.getOwnPropertySymbol, Reflect.ownKeys, etc. on keys).
  • It should work with frozen keys.
  • .size and .delete also could be O(1).

@neenhouse
Copy link
Contributor Author

neenhouse commented Aug 15, 2019

@zloirock Thanks for the feedback. Frozen object keys forced me to climb the ladder of abstraction by 1 rung and were a bit tricky. I don't see a way to avoid O(n) computation time when dealing with those since I'm depending on being able to mark an object key with some kind of meta data to map it back to the internal state of the Map. Do you have any suggestions for dealing with perf in those cases? Appreciate any additional feedback.

@JakeChampion - I saw for size you already were precomputing a _size property and thought it would be a good idea (as @zloirock suggested) to convert the size getter to implement that instead of performing a O(n) iteration. I couldn't spot any logical fallacies for doing that. Is there anything I'm missing there? (reference: https://github.com/Financial-Times/polyfill-library/pull/293/files#diff-aaebc3d5cda7f687fb935dad9a4a98eaR502)

@zloirock
Copy link

@neenhouse see core-js.

@neenhouse
Copy link
Contributor Author

Thanks for the feedback @zloirock

@neenhouse
Copy link
Contributor Author

☝️ Last CI failure looks like browser stack flake:

Screen Shot 2019-08-16 at 2 24 08 PM

@craigkovatch
Copy link

Will this also fix the perf issues in Set? I'm guessing they have the same root cause.

@neenhouse
Copy link
Contributor Author

Hi @craigkovatch, this only improves Map, but Set has the same underlying issue with O(n) perf. I would imagine the right path would to be either to:

  • Abstract a private class to use between Map and Set that implement similar, optimized operations.
  • Re-implement the same optimizations used in Map in Set.

@craigkovatch
Copy link

Any update on this? We would really like to be able to use Set/Map

@chee chee added this to incoming in Origami ✨ Feb 1, 2020
@JakeChampion JakeChampion merged commit 170c779 into JakeChampion:master Feb 7, 2020
Origami ✨ automation moved this from incoming to complete Feb 7, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 7, 2021
@robertboulton robertboulton removed this from Done in Origami ✨ Jul 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants